fubark / cyber

Fast and concurrent scripting.
https://cyberscript.dev
MIT License
1.16k stars 38 forks source link

CStruct with bitfields #43

Open brentp opened 1 year ago

brentp commented 1 year ago

Hi, I know it's early days, but I wanted to report this in case there's already a solution. Below is a diff to your examples/{foo.c,ffi.cy} that shows the issue. In short, how to handle C structs that use bit fields?


diff --git a/examples/ffi.cy b/examples/ffi.cy
index 0d8aedf..4f03827 100644
--- a/examples/ffi.cy
+++ b/examples/ffi.cy
@@ -7,7 +7,18 @@

 import os 'os'

+object bitStruct:
+  flag uint32
+  other uint32
+
+
 lib = os.bindLib('./libfoo.so', [
     os.CFunc{ sym: 'add', args: [#int, #int], ret: #int }
+    os.CFunc{ sym: 'getBitStruct', args: [#uint], ret: bitStruct }
+    os.CStruct{ fields: [#uint, #uint], type: bitStruct}
 ])
-lib.add(123, 321)
+print lib.add(123, 321)
+
+
+print lib.getBitStruct(22).flag
+
diff --git a/examples/foo.c b/examples/foo.c
index 082fc1e..22909ff 100644
--- a/examples/foo.c
+++ b/examples/foo.c
@@ -1,3 +1,16 @@
+#include <stdint.h>
+
+typedef struct bitStruct {
+    uint32_t flag: 1, other: 31;
+} bitStruct;
+
+bitStruct getBitStruct(uint32_t a) {
+   bitStruct b;
+   b.flag = 1;
+   b.other = a;
+   return b;
+}
+
 int add(int a, int b) {
     return a + b;
-}
\ No newline at end of file
+}
fubark commented 1 year ago

This hasn't been implemented yet but I did check that it's possible with libtcc. It would look something like this in Cyber:

import os 'os'

lib = os.bindLib('./libfoo.so', [
    os.CFunc{ sym: 'getBitStruct', args: [#uint], ret: bitStruct }
    os.CStruct{ fields: [os.CuintBits(1), os.CuintBits(31)], type: bitStruct }
])

object bitStruct:
  flag number
  other number

s = lib.getBitStruct(22)
print s.flag
print s.other
brentp commented 1 year ago

I see. Thanks for the response. For that example, I also don't mind bitshifting myself. I do have a related question. I was just trying out wrapping htslib. The first function is hts_open with this signature:

htsFile *hts_open(const char *fn, const char *mode);

where htsFile is:

typedef struct htsFile {
    uint32_t is_bin:1, is_write:1, is_be:1, is_cram:1, is_bgzf:1, dummy:27;
    int64_t lineno;
    kstring_t line;
    char *fn, *fn_aux;
    union {
        BGZF *bgzf;
        struct cram_fd *cram;
        struct hFILE *hfile;
    } fp;
    void *state;  // format specific state information
    htsFormat format;
    hts_idx_t *idx;
    const char *fnidx;
    struct sam_hdr_t *bam_header;
    struct hts_filter_t *filter;
} htsFile;

so I'd prefer not to specify that in cyber. In fact it can be completely opaque. I tried this:

import os 'os'

hts = os.bindLib('libhts.so', [
    os.CFunc{ sym: 'hts_open', args: [#charPtrZ, #charPtrZ], ret: #ptr}
])
print hts

h = hts.hts_open("../htslib/test/range.bam")
print h

but I see:

BindLib53175d61490b23df
panic: Missing method symbol `hts_open` from receiver of type `BindLib53175d61490b23df`.

/home/brentp/src/cyber/bam.cy:8:5 main:
h = hts.hts_open("../htslib/test/range.bam")
    ^

Is there a way to do this without writing a separate shared lib containing a wrapper function that returns a void *?

fubark commented 1 year ago

You've set up bindLib correctly, but you called hts_open with 1 arg instead of 2. Since Cyber currently has function overloading by number of params, it say's it's missing a symbol. I'll make the error message more descriptive.

Is there a way to do this without writing a separate shared lib containing a wrapper function that returns a void *

That's the intended purpose of #ptr, I think it will be renamed to #voidPtr in the near future.

brentp commented 1 year ago

You've set up bindLib correctly, but you called hts_open with 1 arg instead of 2. Since Cyber currently has function overloading by number of params, it say's it's missing a symbol. I'll make the error message more descriptive.

Oh, wow. Thanks for noticing this. Indeed it's working now.