Stream: beginners

Topic: C binding


view this post on Zulip Slazaa (Dec 09 2023 at 12:09):

Hey, I'd like to write a binding for a C library, what would be the steps for that ?

view this post on Zulip Anton (Dec 10 2023 at 13:22):

Hi @Slazaa,
We don't yet support automatic generation of glue code for c platforms. If you want, you can call c functions from a rust platform. We do support automatic generation of rust glue code.

view this post on Zulip Slazaa (Dec 10 2023 at 13:23):

How does glue work ? How hard would it be not to use it ?

view this post on Zulip Anton (Dec 10 2023 at 13:37):

Glue allows the platform and roc to talk to each other.
example of glue for basic-cli

How hard would it be not to use it ?

I have not done this manually myself but from what I've heard, it's very tedious and error prone to do it manually.

Another possibility is to do all communication between the Roc code and the C platform using a String, like here. Also tedious, but simpler overall.

view this post on Zulip Anton (Dec 10 2023 at 13:40):

@Luke Boswell did manage to make the String based communication work quite well with a zig platform but I can't find the repo.

view this post on Zulip Shaiden Spreitzer (Dec 10 2023 at 14:52):

This is my attempt at implementing a minimal host.c. I have no idea if any of it is correct:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

void *roc_alloc(size_t size, unsigned int alignment)
{
    return malloc(size);
}

void *roc_realloc(void *ptr, size_t new_size, size_t old_size, unsigned int alignment)
{
    return realloc(ptr, new_size);
}

void roc_dealloc(void *ptr, unsigned int alignment)
{
    free(ptr);
}

__attribute__((noreturn)) void roc_panic(void *ptr, unsigned int tag_id)
{
}

void *roc_memset(void *str, int c, size_t n)
{
    return memset(str, c, n);
}

struct RocStr { char *bytes; size_t len; size_t capacity; }; // BROKEN!! Missing small-str-optim.!

void   roc__mainForHost_1_exposed(char *output); // roc_main
size_t roc__mainForHost_1_exposed_size(); // roc_main_size
void   roc__mainForHost_0_caller(const char *flags, const char *closure_data, char *output); // call_Fx
size_t roc__mainForHost_0_size(); // size_Fx
size_t roc__mainForHost_0_result_size(); // size_Fx_result

int main()
{
    size_t size = roc__mainForHost_1_exposed_size();
    char *returnedFromMain_data_ptr = roc_alloc(size, 1);
    // calling "main" function in main.roc:
    roc__mainForHost_1_exposed(returnedFromMain_data_ptr);

    const unsigned char flags = 0;
    size = roc__mainForHost_0_result_size();
    char *buffer = roc_alloc(size, 1);
    // "returnedFromMain_data_ptr" contains a closure, we call it here:
    roc__mainForHost_0_caller(&flags, returnedFromMain_data_ptr, buffer);

    // Done everything:
    roc_dealloc(buffer, 1);
    roc_dealloc(returnedFromMain_data_ptr, 1);
}

I assume that c glue code would look simiilar..

view this post on Zulip Brian Carroll (Dec 10 2023 at 14:59):

At first glance that looks reasonable except roc_panic should print a message and call exit.

view this post on Zulip Brian Carroll (Dec 10 2023 at 14:59):

Its argument is a RocStr* I think

view this post on Zulip Shaiden Spreitzer (Dec 10 2023 at 15:07):

Brian Carroll said:

At first glance that looks reasonable except roc_panic should print a message and call exit.

I left out the details but my confidence in posting that betrays my confusion:
This example:
const program = roc__mainForHost_1_exposed();
https://github.com/roc-lang/roc/blob/13703eea7da82691a7bc2030f9c069685f6433c8/examples/cli/tui-platform/host.zig#L176
calls roc__mainForHost_1_exposed() and then simply THROWS AWAY (!) the result:
_ = program;
https://github.com/roc-lang/roc/blob/13703eea7da82691a7bc2030f9c069685f6433c8/examples/cli/tui-platform/host.zig#L194

I have no idea why! Is it to init the main in main.roc? Why is the result (program) not being used? Wish there was better (or any!) documentation on how to interface with roc!

view this post on Zulip Brian Carroll (Dec 10 2023 at 15:10):

Roc is built by volunteers working for free in their spare time. Documentation takes time to write and we are in the process of totally changing how this works.

view this post on Zulip Brian Carroll (Dec 10 2023 at 15:10):

I'm not sure why that result is thrown away.

view this post on Zulip Brian Carroll (Dec 10 2023 at 15:12):

@Folkert de Vries might know

view this post on Zulip Shaiden Spreitzer (Dec 10 2023 at 15:15):

Brian Carroll said:

Roc is built by volunteers working for free in their spare time. Documentation takes time to write and we are in the process of totally changing how this works.

Yeah I mean just a comment or two would be helpful here!
Of course I understand this is a volunteer effort. Hope I can learn enough and find the time to contribute in whatever little way I can!!

view this post on Zulip Folkert de Vries (Dec 10 2023 at 15:19):

ah, we're doing something slightly cheaty here. The zig code assumes that the functions returned by roc's main function are top-level functions, in other words they don't capture anything from their surrounding environment

view this post on Zulip Folkert de Vries (Dec 10 2023 at 15:19):

that is a valid assumption for the applications we write, but in general not entirely correct. (we want to actively enforce this restriction in the future)

view this post on Zulip Folkert de Vries (Dec 10 2023 at 15:20):

so, normally, the result of main would be a struct containing 3 functions, which in practice would be a struct containing for each function the captured values. Because there are none, this is in effect a zero-sized struct: there is no information in it

view this post on Zulip Folkert de Vries (Dec 10 2023 at 15:20):

so we can just ignore it

view this post on Zulip Shaiden Spreitzer (Dec 10 2023 at 15:28):

Folkert de Vries said:

ah, we're doing something slightly cheaty here. The zig code assumes that the functions returned by roc's main function are top-level functions, in other words they don't capture anything from their surrounding environment

Haha! Does this not contradict the very idea behind bot Roc & Zig? Such as Freedom from Side-effects, Immutability, and No Hidden Control-flow??

Aside from that, is it possible to split the mainFromHost into three? => initFromHost, updateFromHost, viewFromHost?? Or do they HAVE to be in a record? Or is this an open issue?

view this post on Zulip Oskar Hahn (Dec 10 2023 at 15:32):

It's an open issue https://github.com/roc-lang/roc/issues/6115

view this post on Zulip Luke Boswell (Dec 10 2023 at 19:48):

https://github.com/lukewilliamboswell/roc-graphics-mach/blob/1733ab62562c4bd7b868d9dc23e20a450ce36a66/platform/main.roc#L64

view this post on Zulip Luke Boswell (Dec 10 2023 at 19:49):

This is how I implemented Model View Update just passing a List U8 between Roc and Zig. It worked ok for that experiment


Last updated: Jul 06 2025 at 12:14 UTC