mirror of
https://github.com/carp-lang/Carp.git
synced 2024-11-05 04:44:12 +03:00
6593a64a19
* Adds C Interop documentation for Strings * Replaces usage of triple backticks with single when used in a paragraph * Adds link to new C Interop docs in the Language Guide * Small tweaks to wording in C Interop doc * Adds Array section to C Interop doc * Adds deftemplate examples in C interop doc * Small copy change in C interop doc * Adds identifiers section in C interop doc
345 lines
9.1 KiB
Markdown
345 lines
9.1 KiB
Markdown
# C Interop
|
|
|
|
This is an extension of what is covered in the [Language Guide](./LanguageGuide.md#c-interop).
|
|
|
|
## Content
|
|
|
|
- [How Carp generates identifiers]("#how-carp-generates-identifiers")
|
|
- [Managed types](#managed-types)
|
|
- [String](#string)
|
|
- [Array](#array)
|
|
- [Embedding C code in Carp](#embedding-c-code-in-carp)
|
|
- [`deftemplate`](#deftemplate)
|
|
- [`Basic example`](#basic-example)
|
|
- [`Generics`](#generics)
|
|
|
|
|
|
## How Carp generates identifiers
|
|
|
|
When creating a function or def it might be useful to know what identifier gets
|
|
generated on the C side. Here are some examples:
|
|
|
|
```clojure
|
|
(def a-def 100)
|
|
; => a_MINUS_def
|
|
|
|
(defn hello [] (println* "Hello"))
|
|
; => hello
|
|
|
|
(sig true? (Fn [Bool] Bool))
|
|
(defn true? [b] b)
|
|
; true_QMARK_
|
|
|
|
(defmodule Reverse
|
|
(defn hello [] (println* "Goodbye"))
|
|
; => Reverse_hello
|
|
(defmodule ReReverse
|
|
(defn hello [] (println* "Hello"))))
|
|
; => Reverse_ReReverse_hello
|
|
|
|
; Generic signature
|
|
(sig print-first-and-add (Fn [(Ref (Array a)) b b] b))
|
|
(defn print-first-and-add [arr x y]
|
|
(do
|
|
(println* (Array.unsafe-first arr))
|
|
(+ x y)))
|
|
; Generates no code until it is called
|
|
|
|
(print-first-and-add &[1] 1 1)
|
|
; => print_MINUS_first_MINUS_and_MINUS_add__int_int
|
|
(print-first-and-add &[@"hello"] 2l 40l)
|
|
; => print_MINUS_first_MINUS_and_MINUS_add__String_Long
|
|
```
|
|
|
|
Looking at the examples should be clear enough but let's break it down:
|
|
Carp will replace illegal characters in C with a string representation of them
|
|
(`- => _MINUS_`, `? => _QMARK_`, etc...)
|
|
If in modules it will prefix the identifier with the modules name.
|
|
When the arguments to a function are generic it will suffix the types to the
|
|
identifiers, the identifiers are not able to be generated until it is used. If
|
|
a function is potentially generic but you don't want it to be you can add a
|
|
non-generic signature to it to make Carp generate your function like in our
|
|
`true?` example.
|
|
|
|
When creating bindings to a library it would be hard to coerce this logic into
|
|
creating the exact identifiers the library uses, this is why `register` and
|
|
`register-type` accepts an optional argument to specify what identifiers to use:
|
|
|
|
```clojure
|
|
(defmodule CURL
|
|
(register-type HttpPost "curl_httppost")
|
|
(register form-free (Fn [(Ref HttpPost)] ()) "curl_formfree"))
|
|
```
|
|
|
|
## Managed types
|
|
|
|
In Carp types like `String` and `Array` are _managed_ types in that they are
|
|
allocated on the Heap and the compiler will automatically free the allocated
|
|
memory when they go out of scope. We'll see how we can go from these managed
|
|
type to C and back.
|
|
|
|
### String
|
|
|
|
To use a managed `String` with a C function requiring a `char*` you can use the
|
|
`String.cstr` function that will turn your `(Ref String)` into `(Ptr CChar)`:
|
|
|
|
|
|
```clojure
|
|
(register puts (Fn [(Ptr CChar)] ()))
|
|
|
|
(let [a-str @"A string."]
|
|
(puts (String.cstr &a-str)))
|
|
(puts (String.cstr "Hello"))
|
|
```
|
|
|
|
You may want to hide the C type from the end-user:
|
|
|
|
```clojure
|
|
(defmodule MyMod
|
|
(hidden puts-c)
|
|
(private puts-c)
|
|
(register puts-c (Fn [(Ptr CChar)] ()) "puts")
|
|
(defn puts [str-ref] (puts-c (String.cstr str-ref))))
|
|
|
|
(let [a-str @"A string."]
|
|
(MyMod.puts &a-str))
|
|
(MyMod.puts "Hello")
|
|
```
|
|
|
|
---
|
|
|
|
If you are given a `char*` and want to turn it into a managed `String` you can
|
|
use `String.from-cstr`. It will allocate and copy the content of the C string.
|
|
|
|
```c
|
|
// static-str.h
|
|
char* returns_a_static_str() {
|
|
return "Hello";
|
|
}
|
|
```
|
|
|
|
```clojure
|
|
(relative-include "static-str.h")
|
|
|
|
(register returns-a-static-str (Fn [] (Ptr CChar)) "returns_a_static_str")
|
|
|
|
(let [a-str (String.from-cstr (returns-a-static-str))]
|
|
(println* (String.concat &[a-str @" " @"Carp"])))
|
|
```
|
|
|
|
---
|
|
|
|
The function you're consuming might be allocating the string on the Heap for
|
|
you. In that case you can declare the function as returning a managed `String`.
|
|
However this might be unsafe, you need to ensure that the string is actually
|
|
Heap-allocated and that the allocator is the same as the one that Carp is
|
|
using.
|
|
|
|
```c
|
|
char* returns_a_heap_string() {
|
|
char *hello = "Hello from the heap";
|
|
char *str = malloc((strlen(hello)+1));
|
|
strcpy(str, hello);
|
|
return str;
|
|
}
|
|
```
|
|
|
|
```clojure
|
|
(relative-include "heap-string.h")
|
|
|
|
(register returns-a-heap-str (Fn [] String) "returns_a_heap_string")
|
|
|
|
(let [a-str (returns-a-heap-str)]
|
|
(println* a-str))
|
|
```
|
|
|
|
If you are the one writing the C code, you can use the `CARP_MALLOC` macro to
|
|
ensure you are using the same allocator as the Carp compiler:
|
|
|
|
```c
|
|
char* returns_a_heap_string() {
|
|
char *hello = "Hello from the heap";
|
|
char *str = CARP_MALLOC((strlen(hello)+1));
|
|
strcpy(str, hello);
|
|
return str;
|
|
}
|
|
```
|
|
|
|
### Array
|
|
|
|
`Array.unsafe-raw` can be used in case you have a function taking an C array as
|
|
a parameter.
|
|
|
|
```c
|
|
int sum(int *arr, int len) {
|
|
int acc = 0;
|
|
for (int i = 0; i < len; i++) {
|
|
acc += arr[i];
|
|
}
|
|
return acc;
|
|
}
|
|
```
|
|
|
|
```clojure
|
|
(relative-include "sum.h")
|
|
|
|
(register sum-c (Fn [(Ptr Int) Int] Int) "sum")
|
|
|
|
(let [ints [1 2 3]]
|
|
(println* (sum-c (Array.unsafe-raw &ints) (Array.length &ints))))
|
|
```
|
|
|
|
Again, you might want to wrap the bare C function in more Carp-esque interface.
|
|
|
|
```clojure
|
|
(relative-include "sum.h")
|
|
|
|
(defmodule MyMod
|
|
(hidden sum-c)
|
|
(private sum-c)
|
|
(register sum-c (Fn [(Ptr Int) Int] Int) "sum")
|
|
|
|
(sig sum (Fn [(Ref (Array Int))] Int))
|
|
(defn sum [ints] (sum-c (Array.unsafe-raw ints) (Array.length ints))))
|
|
|
|
(MyMod.sum &[1 2 3])
|
|
```
|
|
|
|
---
|
|
|
|
In cases where the consuming function takes ownership over the data,
|
|
`Array.raw` can be used. It becomes the responsibility of the consuming
|
|
function to call `free` on the pointer and any managed types it contains.
|
|
|
|
```c
|
|
// printall.h
|
|
void println_all(char **arr, int len) {
|
|
for (int i = 0; i < len; i++) {
|
|
printf("%s\n", arr[i]);
|
|
CARP_FREE(arr[i]);
|
|
}
|
|
CARP_FREE(arr);
|
|
}
|
|
```
|
|
|
|
```clojure
|
|
(relative-include "printall.h")
|
|
|
|
(register println-all (Fn [(Ptr String) Int] ()) "println_all")
|
|
|
|
(let [lines [@"One" @"Two" @"Three"]
|
|
len (Array.length &lines)]
|
|
(println-all (Array.raw lines) len))
|
|
```
|
|
|
|
## Embedding C code in Carp
|
|
|
|
When interfacing C libraries it is sometimes beneficial to wrap the libraries
|
|
function with some custom C code. An entirely valid method is the write your
|
|
code in a header file, include it from the Carp side and `register` it:
|
|
|
|
```c
|
|
// print.h
|
|
// String is a carp core alias for char*
|
|
void print_that_takes_ownership(String str) {
|
|
printf("%s", str);
|
|
CARP_FREE(str);
|
|
}
|
|
```
|
|
|
|
```clojure
|
|
(relative-include "print.h")
|
|
|
|
(register print (Fn [String] ()) "print_that_takes_ownership")
|
|
|
|
(print @"Print this!")
|
|
```
|
|
|
|
However you might prefer to keep your C code close to your Carp code, enter `deftemplate`...
|
|
|
|
### `deftemplate`
|
|
|
|
#### Basic example
|
|
We can instead define the previous example like so:
|
|
|
|
```clojure
|
|
(deftemplate print (Fn [String] ())
|
|
"void $NAME(String str)"
|
|
"$DECL {
|
|
printf(\"%s\", str);
|
|
CARP_FREE(str);
|
|
}")
|
|
|
|
(print @"Print this!")
|
|
```
|
|
|
|
Let's break down what's going on here:
|
|
The **first** argument to `deftemplate` is the name we'll use to refer to the
|
|
function.
|
|
The **second** is a type signature and is identical to the one found
|
|
in our previous `register` call.
|
|
The **third** is our function declaration, it'll be injected at the top of the
|
|
generated C file.
|
|
The **last** argument represent the function definition.
|
|
|
|
Two more things to look at:
|
|
`$NAME` is a variable that will be derived from the name you've given the
|
|
function plus any module it's defined in, so no need to worry about name
|
|
clashes with other `print` functions in other modules.
|
|
`$DECL` will be replaced with the declaration passed as a third argument when
|
|
the function is defined
|
|
|
|
So we've seen how `deftemplate` can be used to keep Carp and C code close to
|
|
each other and help you write less code in general but it's real power lies
|
|
somewhere else...
|
|
|
|
#### Generics
|
|
|
|
Let's say one would like to write a function that adds two numbers, it would be
|
|
tedious to write a version for every type of number, let's see how
|
|
`deftemplate` can help us with that.
|
|
|
|
```clojure
|
|
(deftemplate add (Fn [a a] a)
|
|
"$a $NAME($a x, $a y)"
|
|
"$DECL {
|
|
return x + y;
|
|
}")
|
|
|
|
(add 1 2)
|
|
(add 20l 22l)
|
|
(add 2.0f 5.0f)
|
|
|
|
; Can't do that as they're different types
|
|
; (add 2.0f 22l)
|
|
```
|
|
|
|
Carp allows us to use generic type in type signatures, `a` in that example. You
|
|
can use `$` plus the generic name you used in your signature to refer to that
|
|
type in your C code. Carp will then generate a separate function everytime the
|
|
template is used with a different type.
|
|
|
|
Warning! You'll need to be careful when calling that function as you've lost
|
|
all type safety the Carp compiler guarantees. You will have to hope the C
|
|
compiler will catch it.
|
|
|
|
``` clojure
|
|
(deftemplate add (Fn [a a] a)
|
|
"$a $NAME($a x, $a y)"
|
|
"$DECL {
|
|
return x + y;
|
|
}")
|
|
|
|
(add @"A string" @" another string")
|
|
```
|
|
|
|
This thankfully result in this Clang error, but it's probably good not to rely on it.
|
|
|
|
```
|
|
out/main.c:9153:29: error: invalid operands to binary expression ('String' (aka 'char *') and 'String')
|
|
return x + y;
|
|
~ ^ ~
|
|
1 error generated.
|
|
```
|
|
|