C bindings

Allow C bindings with the expressiveness of C macros.

Problem

Re-arranging parameters in C function calls is currently not possible.

Output parameters in C are not always at the end of the parameter list. For example, a function like strcpy can not be used from Blech via a simple extern function.

char *strcpy(char *restrict s1, const char *restrict s2);

The C function strcpy copies s2 to s1.

The extern function declaration in Blech would put the target toString in the output parameter list.

@[CFunction (binding ="strcpy", header = "string.h")]
extern function stringCopy (fromString: string)(toString: string)

Given that we currently do not support strings, which is another future topic, the order of input and output parameters cannot be rearranged for the bound C function.

@[EntryPoint]
activity Main () ()
    let s2 = "hello"
    var s1: string
    stringCopy(s2)(s1)

    await true
end

When we call stringCopy from Blech we actually we like to call strcpy in C directly. This is currently not possible.

Proposal

Specifiy the full C function by indexing Blech identifiers

This could be done by using the parameter names in a macro-like fashion.

@[CFunction (binding = "strcpy($2, $1)" , header = "string.h")]
extern function stringCopy (formString: string)(toString: string)

The Blech identifiers in the extern declaration can be addressed via $i.

$0 is stringCopy, $1 is fromString, $2 is toString.

For code generation we would create the C macro by subsituting the Blech parameter names.

#include <string.h>

#define blc_stringCopy(blc_fromString, blc_toString) strcpy(blc_toString, blc_fromString)

As usual it is the reponsibility of the programmer to guarantee correct types, additionially the indexing of identifiers must be correct.

If strings will become structs with len field and a buf reference. The extern declaration could look like the following:

@[CFunction (binding = "do { strcpy($2.buf, $1.buf); $2.len = $1.len; } while (0)" , header = "string.h")]
extern function stringCopy (formString: string)(toString: string)

which would create the following macro

#include <string.h>

#define blc_stringCopy(blc_fromString, blc_toString) \ 
    do { strcpy(blc_toString.buf, blc_fromString.buf); blc_toString.len = blc_fromString.len; } while (0)

As always with C code, this code is unsafe, because Blech cannot check the buffer sizes - actually the compiler does not know anything about copying or the semantics of the function call.

Perhaps we should also allow multi-line strings in attributes and later on in string literals, for better formatting of long strings.

Last modified May 4, 2021: drafting the module chapter (fa3db01)