Version 4 (modified by jorisborgdorff, 12 years ago) (diff) |
---|
The C API of MUSCLE is a subset of the C++ API, the C API is again a subset of the Java API. Because the core of MUSCLE is written in Java, any C++ executable will communicate with Java code, but this is hidden in the API. In both cases, the include path is $MUSCLE_HOME/include
C API
All muscle2 headers are installed in $MUSCLE_HOME/include/muscle2. The header for the C API is muscle2/cmuscle.h.
The functions there are very limited, but they should be sufficient for most needs:
muscle_error_t MUSCLE_Init(int* argc, char*** argv); void MUSCLE_Finalize(void); const char* MUSCLE_Get_Kernel_Name(void); const char* MUSCLE_Get_Property(const char* name); int MUSCLE_Will_Stop(void); muscle_error_t MUSCLE_Send(const char *exit_name, void *array, size_t size, muscle_datatype_t type); void* MUSCLE_Receive(const char *entrance_name, void *array, size_t *size, muscle_datatype_t type);
The MUSCLE_Init function must be called before any of the other MUSCLE functions is used, and also before MPI_Init is called, if MPI is used. The usage is the same of MPI_Init: to initialize MUSCLE given the current arguments of main. After all MUSCLE calls have been made, MUSCLE_Finalize should be called. Usually:
int main(int argc, char *argv[]) { MUSCLE_Init(&argc, &argv); ... MUSCLE_Finalize(); return 0; }
After this, any code can be written. The MUSCLE_Get_Kernel_Name function gives the current kernel name. This must be freed after use. Similarly, MUSCLE_Get_Property gives a property as a string, which must be freed afterwards.
The send and receive functions can transport several datatypes, which are enumerated in muscle2/muscle_types.h. For reference, it takes the following datatypes
muscle_datatype_t | C/C++ data type | Java data type | maximum size |
---|---|---|---|
MUSCLE_DOUBLE | double * | double[] | 134e6 values |
MUSCLE_FLOAT | float * | float[] | 268e6 values |
MUSCLE_INT32 | int * | int[] | 268e6 values |
MUSCLE_INT64 | long * | long[] | 134e6 values |
MUSCLE_STRING | char * | String | 64e3 characters |
MUSCLE_RAW | unsigned char * | byte[] | 268e6 values |
MUSCLE_COMPLEX | muscle::ComplexData * | any other object | 1 GiB |
The type MUSCLE_COMPLEX is only available for C++ code.
Receiving a message can be done in two ways: either the memory is initialized beforehand and the buffer size is given as the third argument, or a 0 pointer is passed, in which case MUSCLE will allocate the memory. In both cases, the memory must be freed by the user. For example:
size_t sz = 100; double *data = (double *)malloc(sz*sizeof(double)); if (data) { for (int i = 0; !MUSCLE_Will_Stop(); i++) { MUSCLE_Receive("exitName", data, &sz, MUSCLE_DOUBLE); // sz will be set to the actual size of the received message // do something with the data } free(data); }
or
size_t sz = 0; for (int i = 0; !MUSCLE_Will_Stop(); i++) { double *data = (double *)MUSCLE_Receive("exitName", (void *)0, &sz, MUSCLE_DOUBLE); // do something with the data // data will have length sz; free(data); }
The first example will only work if you know the upper bound of received message size in advance. In this way, memory is only allocated once per submodel run. The second example is safer since MUSCLE will allocate the necessary data for you based on the message size. It does mean that new memory is allocated each message.
The MUSCLE_Send command can only be used one way.
double data[100]; size_t sz = 100; for (int i = 0; !MUSCLE_Will_Stop(); i++) { // do something with the data... // send the data MUSCLE_Send("entranceName", data, sz, MUSCLE_DOUBLE); } // data is on the stack; it will be freed automatically