Guidance on data types for passing 'user' parameters in SEND, RECEIVE, and TRIGGER_THREAD:

History:

Pointers are addresses of variables or structures. It is usually best to declare a pointer relative to the kind of data it points at. However, there are cases where a routine or variable can pass pointers to many kinds of data. A catch-all type is needed.

Originally (in the '80s prior to ANSI standardization), K&R provided that addresses could be held in integer variables, and K&R compilers accepted passing addresses in integer typed variables as the catch-all type.

By the time of ANSI standardization, the type (void *) became the preferred way to declare a pointer to any arbitrary data, - an address to anything.

The original K&R C specification said that "int" would always be the size of the native machine word, which is the same size as an address. Since CSIM was originally written that way, and it seemed assured to remain true, we deferred changing it. Unfortunately, some newer 64-bit compilers have not kept with the rule, and type (int) stayed 32-bits - unable to hold 64-bit addresses. Type (long) will hold addresses on all machines, but will cause compiler warnings due to the apparent type mismatch. Long is not considered a catch-all type.

At this time it makes sense to bite-the-bullet and make the switch to the more modern style. This becomes an issue with CSIM's SEND, RECEIVE, and TRIGGER_THREAD routines that enable you to pass data or pointers to arbitrary user-data. Previously, the second argument of SEND and RECEIVE, and the third argument of TRIGGER_THREAD were formally defined as type (int). You could pass integer values without any type-conversion or type-casting. However, passing any other types, such as chars, floats, or pointers to structures required type-casting to avoid compiler warnings. In the early days, it was more common to pass simple integer values, but now it is much more common to pass pointers to complex structures. So now there are many reasons to make this change. It will simplify writing most models.


Guidelines:

The following sections show how to change old models to the new convention with explanations about the reasoning.

Passing an integer:

 Old Models:
    Calling thread:
	   int m;				
	   TRIGGER_THREAD( x, 0.0, m );
    Called thread:
	   int M;
	   M = THREAD_VAR;

 New Models:
    Calling thread:
	   int m;
	   TRIGGER_THREAD( x, 0.0, (void *)((long)m) );
    Called thread:
	   int M;
	   M = (int)((long)THREAD_VAR);
The calling thread converts the int m to a "long int" and tells the compiler you are purposely doing this, to consider it as a (void *) to avoid warnings. The called thread interprets THREAD_VAR as a long int and converts it to an int. (On 32-bit machines, this merely pacifies the compiler. It compiles out because both long and int are the same. Costs no run-time.) This is the only case where the change adds text.

It is actually not necessary to type-cast the int's to long's on 32-bit machines. However, as 64-bit machines become prominent, the type-cast to long will be needed.

A slightly better way would be to just use long ints to begin with, as in:

 New Models:
    Calling thread:
	   long m;
	   TRIGGER_THREAD( x, 0.0, (void *)m );
    Called thread:
	   long M;
	   M = (long)THREAD_VAR;

Passing a pointer to a structure:
 Old Models:
    Calling thread:
	   struct xyz *ptr;				
	   TRIGGER_THREAD( x, 0.0, (int)ptr );
    Called thread:
	   struct xyz *PTR;
	   PTR = (struct xyz *)THREAD_VAR;

 New Models:
    Calling thread:
	   struct xyz *ptr;
	   TRIGGER_THREAD( x, 0.0, ptr );
    Called thread:
	   struct xyz *PTR;
	   PTR = (struct xyz *)THREAD_VAR;
This is a very common case. Notice the simplification in the new TRIGGER_THREAD.

Passing a pointer to an integer or float:
 Old Models:
    Calling thread:
	   int m;
	   TRIGGER_THREAD( x, 0.0, (int)(&m) );
    Called thread:
	   int *M;
	   M = (int *)THREAD_VAR;

 New Models:
    Calling thread:
	   int m;
	   TRIGGER_THREAD( x, 0.0, &m );
    Called thread:
	   int *M;
	   M = (int *)THREAD_VAR;
Again, notice the slight simplification in the new TRIGGER_THREAD.


General rules for updating old models:

  1. Search for TRIGGER_THREAD, SEND and RECEIVE.
  2. Either:
        - Remove type casts to (int) where pointers to structures or data are being passed.
    Or:
        - Add type-cast to (void *)((long)xxx) where xxx was an integer.


Other options:

The following convenience method has been suggested:

Define:
      #define Int2Point(x) ((void *)((long)(x)))
      #define Point2Int(x) ((int)((long)(x)))

Then use as, for example:
      int m;
      TRIGGER_THREAD( x, 0.0, Int2Point(m) );