First lets create 2 files in a project folder:

nbody.cr (our Crystal program file)

functions.c (our C program)

Open the C file, functions.c:

It’s empty but lets copy the code from the website and paste it in. I’ve collapsed all the functions since the calculations themselves are not interesting here, what we are interested in is the “main()” function and I’ve highlited the lines we will change:

NB!!! We will change the “atoi(argv[1])” statement as well

OK so you see here that it takes the input from the command line, uses that as an input to do the calculations and it prints the results to the terminal/console.

The function signature.

We want to change it so it takes:

an Integer from our Crystal code

a callback function we give it and it executes when each calculation is done

We don’t want to name our function main since it is a library. Lets rename it to “nbody”. Second, we dont need to return an integer anymore, so lets return “void”. Then we want to receive two arguments:

int value — the value we pass it from Crystal to base the calculations on

void (*callback(double)) — the callback function (or a pointer to a callback function), that takes a double (C equvivalent of Float64), and in our case returns nothing

It should look like this:

Secondly, we need to change the next line since we’re getting the input passed directly as an “int”:

Then we change the next two parts, and instad of printing out the results we are going to pass the results to the callback so our Crystal code decides what to do with them.

Lastly we remove the the return 0 statement in the bottom since we don’t return anything from the function. The code should now look like this(I only show the last piece of code, you must of course leave the rest of the code for it to work).

Compile the C code.

The last step is to compile our C code, I use GCC and the I’ll explain quickly the flags we set to compile it:

gcc -Wall -O3 -march=native -c functions.c -o functions.o

-Wall (enables all the warnings from the compiler, and you want those)

-O3 (thats the letter “O”, sets the optimizations to maximum, you can set it to -O2 also but don’t leave it blank, your code will be very slow)

-march=native (enables special optimizations for the CPU you are compiling on)

-c (compile only, we don’t do any linking)

functions.c (tells the compiler what file we want to compile)

-o functions.o (gives it an output name)

There are a whole set of different kind of flags, so google this if you’re curious. There should now be a file called functions.o in the folder.

The Crystal code.

Next open our nbody.cr file and write the following code (you can leave the comments out, they are just for information)

I won’t repete the comments but I’ll explain what we’re doing:

@[Link(ldflags: “#{__DIR__}/functions.o”)]

Here give the linker information on where to find the library we’re going to use.

lib LibFunctions

The “lib” declaration groups C functions and types that belong to this library. Note that you can call it what you want, but it’s good practice to call the objects that binds to C library “Lib…”

fun nbody(value : Int32, f : Float64 -> Nil)

The “fun” declaration inside a “lib” binds to a C function. You need to make sure you have the same name as the function you had in the C library.

We also tell it that the function takes a value of Int32, and the part that looks most cryptic is a “Proc” declaration:

f : Float64 -> Nil

This just declares what type of function we will give it, a Proc that takes a Float64 as input and returns nothing. As you see in the comments there is an alternative way of declaring the Proc that is more readable if they are new to you.

A Proc is a pointer to a function

Thats why we can pass it to the C code, and the C code can invoke it.

callback = ->(x : Float64) { puts “Written in Crystal: #{x}” }

Here we create the Proc itself. As you see it adheres to the declaration we gave above. All it does is add some text and print out the result from the computations that it will get when called in the C function.

LibFunctions.nbody(50000000, callback)

Last we call the C function, pass it a number and the callback (Proc) we just made.

Compile and run.

The last thing to do is to compile our code in crystal and run the program:

To compile in Crystal you write the following

The “ — release” flag tells Crystal to turn on optimizations, and the “ — no-debug” flag just tells it to skip any symbolic debug info. We didn’t specify a output filname here so the name of the executable will be the same as our source file.

Next run the program and verify that it works:

Yeah!!! It works. And if you run the code yourself you will see that each line pops up ca 5 secs after eachother.