Our C code focuses on the training loops (where speed is crucial) but memory allocation/deallocation, dataset loading, saving of results, etc. are all more easily scripted. In fact let’s just have C immediately hand off control to the Cicada interpreter, and tell Cicada to callback runNetwork() as needed. To do this, include the Cicada header in NN.c and NN.h:
#include <cicada.h>
and add following to NN.c (say, just before runNetwork):
const Cfunction fs[] = { { "runNetwork", &runNetwork } };
int main(int argc, char **argv)
{
return runCicada(fs, NULL, true);
}
We also need to adjust the definition of runNetwork() so that Cicada can call it properly. The new function prototype (in both NN.c and NN.h) is:
ccInt runNetwork(argsType args)
An argsType variable holds a list of Cicada variables passed into a C function call. The ccInt return type is user-definable but defaults to just int.
Finally, we need to set up our variables at the beginning of runNetwork(), using Cicada’s functions for a) checking and b) loading data/pointers from args. Since memory is shared between the two environments, any pointers we load can be used to send data back to Cicada. Replace the ‘set up data types’ block comment with the following code:
myNN.numNeurons = args.indices[1];
numInputs = args.indices[2];
if (args.num < 4) return 1;
if (getArgs(args,
arrayRef(double_type, &myNN.weights),
arrayRef(double_type, &myNN.activity),
arrayRef(double_type, &inputs),
scalarValue(double_type, &step_size),
endArgs) != 0) return 2;
if (args.num == 6) {
numOutputs = args.indices[4];
if (getArgs(args, fromArg(4),
arrayRef(double_type, &target_outputs),
scalarValue(double_type, &learning_rate)
) != 0) return 3;
}
Our code loads pointers for myNN.weights, myNN.activity and inputs. There are two reasons to load these parameters by reference: 1) they are arrays not scalars, and 2) our C code will modify the values of activity and weights and we want those changes to persist outside of the function call. However, step_size and learning_rate are constant scalar parameters, so we load their data by value using the scalarValue() macro.
The C code doesn’t need to save any results, since its data is shared with Cicada, and there are scripting functions for writing to files (e.g. save() or saveTable()). Just delete the “save results” comment line in NN.c.
It’s time to build our program. To link against the Cicada library use the linker flag -lcicada. (For example, using gcc on a UNIX machine the command would be: gcc -lcicada -o NN NN.c). With luck, we’ll end up with an executable which we can run from the command prompt (by typing ‘NN’ or ‘./NN’, depending on the system). We should see:
>
Last update: November 12, 2025