Cicada ---> Online Help Docs ---> Example: neural networks in Cicada ---> Rules for embedding C/C++ code

C function declaration

An C/C++ function needs to be of type (int)(int, char **) in order to be called from within Cicada. The two arguments to the C function are: 1) the number of variables/arrays passed to the function, and 2) an array of pointers to these variables; and its return value is usually interpreted as an error code. (This is the same function type as C’s main() function, making it easy to adapt a stand-alone C program.) Each variable or array passed to the function is a list of one or more Booleans, characters, integers, floating-point numbers, or strings stored in linked lists.

Here is an example of a Cicada-compatible function that has one argument of each possible data type:


    #include "userfn.h"
   
    int myFunction(int argc, char **argv)
    {
       ccBool firstArg = *argv[0];
       char secondArg = *argv[1];
       ccInt *thirdArg = argv[2];
       ccFloat *fourthArg = argv[3];
       linkedlist *fifthArg = argv[4];
       
       ...
       
       return 0;        // no error
    }
   

We want to point out several features of this function call. First, we included Cicada’s userfn.h header file. Second, notice that we passed the first two arguments by value but the other three arguments by reference. We must pass an argument by reference if it might be changed, particularly if it is a string (the linkedlist type). The reason it is essential to pass strings by reference is that any resize of a string using a copy of the original linked list will probably crash the program. Arrays are usually passed by reference because in C an array variable is the pointer to the first element.

Finally, we see that our C function used several Cicada-defined types: for example ccInt rather than int. Ordinarily these two types are interchangeable. However, it is possible to change Cicada’s default integer type to, say, a long integer by changing three lines in Cicada’s lnklst.h file to


    typedef long int ccInt;
    #define ccIntMax LONG_MAX
    #define ccIntMin LONG_MIN
   

In lnklst.h we can also change Cicada’s default floating-point type ccFloat, as well as ccBool which is the integer type used to store Booleans. (If we change Cicada’s floating point type to simple float then we might also want to change the Cicada name of this type -- see Chapter 4.) On the other hand, character variables are always simply of type char, and string variables always use the linkedlist data type. Cicada’s lnklst source files provide routines for accessing and resizing linked lists, as described in the reference section.


type # (macro) Cicada type name C type name default data type
0 (bool_type) bool ccBool char
1 (char_type) char char char
2 (int_type) int ccInt int
3 (double_type) double ccFloat double
4 (string_type) string linkedlist { int; void *; int; int; }
5 (composite_type) { } N/A N/A
6 (array_type) [ ] N/A N/A

Table 1: Cicada data types. Types 0 - 4 are primitive

Cicada’s userfn source files provide a handy getArgs() function, which helps us simplify our C routine.


    #include "userfn.h"
   
    int myFunction(int argc, char **argv)
    {
       ccBool firstArg;
       char secondArg;
       ccInt *thirdArg;
       ccFloat *fourthArg;
       linkedlist *fifthArg;
       
       getArgs(argc, argv, byValue(&firstArg), byValue(&secondArg),
               &thirdArg, &fourthArg, &fifthArg);
       
       ...
       
       return 0;        // no error
    }
   

The first two arguments of getArgs() are the two arguments to our C/C++ function: in this case argc and argv. After that we list the address of each variable to load, using a getArgs() macro for each C variable that is not a pointer variable. If we don’t want to load all arguments, we can use the endArgs macro. For example our function could have loaded just the first three arguments with the command


       getArgs(argc, argv, byValue(&firstArg), byValue(&secondArg), &thirdArg, endArgs);
   

Likewise getArgs() can skip to a particular argument using the fromArg() macro, as in


       getArgs(argc, argv, fromArg(2), &thirdArg, &fourthArg, &fifthArg);
   

Notice that fromArg() uses C-style indexing, so the next argument read after fromArg(2) is argv[2] which is the third argument.

To give our C function some context, let’s look an example script that calls it. A typical script first sets up variables and arrays to pass, and then runs the C code using Cicada’s call() function. Notice that the name that call() uses is defined separately from the actual function name, in the file userfn.c.


    nums :: [2][5] int
    call("myFunctionInC", true, 'q', nums, pi, "a sample string")
   

This is pretty straightforward: three of the arguments are constants, pi was pre-defined in user.cicada, and nums is a two-dimensional array. It should be noted that nums is effectively a one-dimensional list as far as the C routine is concerned, and that the order of elements in the list is [1][1], [1][2], ..., [1][5], [2][1], ..., [2][5] (Cicada indices begin at 1). So, for example, the array variable written in Cicada as nums[2][3] is in memory slot thirdArg[7] within our C code.

Finally, the call() function actually passes one extra parameter, located at argv[argc], which is a list containing argc elements of type arg_info (defined in userfn.h) giving the types and numbers of elements of each parameter passed from the script. This is useful for argument-checking, and it also tells the C code the total number of elements in each array that was passed (though not the sizes of the individual dimensions of 2+ dimensional Cicada arrays). The type of a parameter is given by the argType field of its respective arg_info variable, as defined in Table 1. The total number of array elements is given by a argIndices field.

Continuing our last example, we might want to take advantage of the arg_info array by adding these few extra lines to myFunction().


    #include "lnklst.h"
   
    int myFunction(int argc, char **argv)
    {
       arg_info myArgsInfo = argv[argc];
       
       ccInt numThirdArgElements = myArgsInfo[2].argIndices;
       
       ...
       
       if (myArgsInfo[2].argType != int_type) return 1;     // do some type-checking
       
       ...
    }
   


Prev: Rules for embedding C/C++ code   Next: Cicada’s userfn source files


Last update: May 11, 2017

Get Cicada scripting language at SourceForge.net. Fast, secure and Free Open Source software downloads