Working with C: First Steps

In this chapter, we'll talk about how Spawn can work with code in other languages, in particular the C programming language.

Spawn was originally designed to have seamless integration with C code, allowing you to use existing libraries or write more productive parts of your C program.

In fact, Spawn can interact with any language as long as it provides a C API interface (for example, C++ code that has a C wrapper).

First Steps

Let's try to create our first program that will use a C function.

Let's create a file main.sp and a module mylib with mylib.sp and mylib.c. Note that the mylib.c.sp file has a .c.sp extension, this extension indicates that the file will contain externally defined symbols.

Now, let's create our small C library, which we will place in the mylib module folder:

#include "mylib.h" int _add(int a, int b) { return a + b; }

In order for Spawn to know about the definitions in the library, it will need a header file, which we will also create in the mylib module folder:

int _add(int a, int b);

Now everything is ready to use this C code in our Spawn program.

First, let's take a look at the complete file structure:

.
├── main.sp
└── mylib
    ├── mylib.sp
    ├── mylib.c.sp
    ├── mylib.c
    └── mylib.h

In the file mylib.c.sp we define our function from C code:

module mylib extern "C" fn _add(a i32, b i32) -> i32

As you can see, this function definition has two differences from regular functions, first of all it starts with the extern keyword, which indicates that the function is defined outside the Spawn code. The second difference is the absence of a function body.

Extern functions cannot have a body!

Now we need to include the header file of our library. To do this, Spawn has the #[include("..."")] attribute, which allows you to tell the compiler to use the passed file as a header:

module mylib #[include("$SPAWN_DIR/mylib.h")] extern "C" fn _add(a i32, b i32) -> i32

To let the compiler know where to look for the mylib.h file, we use the $SPAWN_DIR variable, which points to the current module directory.

All that remains is to connect the implementation file, and we are ready to run the program:

module mylib #[include("$SPAWN_DIR/mylib.h")] #[cflags("$SPAWN_DIR/mylib.c")] extern "C" fn _add(a i32, b i32) -> i32

The #[cflags("...")] attribute allows you to pass additional flags to the compiler, including C files that need to be compiled.

This completes the definition of the extern C code for the library.

Let's create a wrapper for this function in the mylib.sp file:

module mylib fn add(a i32, b i32) -> i32 { return _add(a, b) }

Module is ready, now we can use it in our program:

module main import mylib fn main() { println(mylib.add(1, 2)) }

Compiling the program:

spawnc main.sp

Conclusion

We have created a simple wrapper around the C function and used it in our Spawn program. In the next article, we will talk about more complex aspects of working with C code.

On this page