C Language Interface¶
Zerynth allows mixing Python and C code in the same project. The Python language is compiled to bytecode and executed by the VM independently of the target board; C language is compiled to object code dependent on the target microcontroller instruction set. The two can coexist because during the uplinking phase, Zerynth resolves any unresolved symbol from the C code and saves C function addresses in the bytecode.
This kind of “hybrid” programming is extremely useful in those scenarios where the programmer needs to write or has already written performant low level code for time critical tasks, but wants to retain Python flexibility and readability for non time critical sections.
In Zerynth it is therefore possible to call C functions from Python, but not (yet) Python functions from C. To call a C function from Python follow this procedure:
- Define an “empty” Python function decorated with
@c_native
(let’s call itpyfn
) - As arguments of
@c_native
pass the name of the C function to be called (let’s call itcfn
), the source files wherecfn
implementation resides and a list of C macros to be defined during compilation - Create the C source file containing
cfn
and declarecfn
using the C macroC_NATIVE()
At compile time, @c_native
parameters will be used to locate the C source code files and compile them with the appropriate macros defined. When pyfn
is called, the VM translates the Python call to a C call.
C function call example¶
A minimal example of C function calling from Python follows.
In Python:
# main.py
@c_native("my_c_function",["my_c_source.c"],[])
def my_py_function(a,b):
"""
a simple function that returns the sum of a and b, with a and b integers
""""
# just pass, the body of my_py_fun is ignored by the compiler
pass
In C:
// my_c_source.c
//include zerynth header
#include "zerynth.h"
C_NATIVE(my_c_function) {
C_NATIVE_UNWARN();
int32_t a,b;
if (parse_py_args("ii",nargs,args,&a,&b)!=2)
return ERR_TYPE_EXC;
*res = PSMALLINT_NEW(a+b);
return ERR_OK;
}
In main.py
the empty function my_py_function
is defined, decorated with @c_native
. The @c_native
decorator informs the compiler that the body of my_py_fun
will be a C function called my_c_function
implemented in the file my_c_source.c
in the same directory where main.py
is residing.
In my_c_source.c
the function my_c_function
is implemented. The macro C_NATIVE(my_c_function)
expands to:
err_t my_c_function(int32_t nargs, PObject *self, PObject **args, PObject **res)
where:
- nargs is the number of arguments passed to
my_py_function
- self
is the self parameter in case
my_py_function` is a method - args is an array of PObject*, the generic structure used by the VM to represent Python objects
-
res is a pointer to a PObject containing the result of the function call
-
the returned value is of type
err_t
The VM passes Python arguments to the C function without touching them; it is responsibility of the C function to convert them as needed. To help the conversion, the function parse_py_args
is provided by the VM.
The return value of function my_py_function
must be set into res and must be of type PObject*. The actual return value of my_c_function
is an error code indicating success (ERR_OK) or an exception that is subsequently raised by the VM.
For more details on the low level VM functions and macros available for hybrid programming, refer to VM Guide.
Macros¶
Some utility macros are added on top of the VM defined macro by the header zerynth.h
.
C_NATIVE(fn)
Used to define the implementation of a C function callable from Python. It equals to:
err_t fn(int nargs, PObject *self, PObject **args, PObject **res)
C_NATIVE_UNWARN
Silences C warnings about unused C_NATIVE
arguments in the body of a C function callable from Python
debug(...)
If the user defines ZERYNTH_DEBUG
before including zerynth.h
, this macro behaves like a printf, writing to the default serial port of the virtual machine. Otherwise it does nothing.
The printf funcionalities are limited to the following placeholders: %s
%i
%I
%d
%D
%u
%U
%x
%X
%p
%c
%%
.
printf(...)
If the user defines ZERYNTH_PRINTF
before including zerynth.h
, this macro behaves like a printf, writing to the default serial port of the virtual machine. Otherwise it does nothing.
The printf funcionalities are limited to the following placeholders: %s
%i
%I
%d
%D
%u
%U
%x
%X
%p
%c
%%
.
Zerynth “C” Limitations¶
C functions callable from Python have some limitations:
- floating point math can not be used (yet)
- C standard library function are not available. Only the following subset can be used:
memcpy
,memset
,memmove
,memcmp
,memchr
malloc
,free
(implemented as macros calling the garbage collector allocator)strlen
Refer to VM Guide for the available api.