How do I interface with a C function that returns a struct?

867 views
Skip to first unread message

Jeff D

unread,
Feb 4, 2016, 3:08:21 PM2/4/16
to emscripten-discuss

In version 1.12, you could set a emcc compiler flag RUNTIME_TYPE_INFO = 1 and it would emit a javascript object containing the size and layouts of the types.

Example of the struct info...

C:
typedef struct SFLocation {
   
double x;
   
double y;
   
SFLinearUnit unit;
   
bool empty;
} SFLocation;


JS (NOTE - this is the feature that is no longer available):
   "SFLocation" : {
     
"alignSize" : 8,
     
"fields" : [ "double", "double", "i32", "i8" ],
     
"flatIndexes" : [ 0, 8, 16, 20 ],
     
"flatSize" : 24,
     
"packed" : false
   
}

So when I need to call a C function that has this interface...

C:
SFLocation ScreenGetCursorLocation(Screen * this);

JS:
function _ScreenGetCursorLocation($agg$result, $this)


I have to allocate memory for $agg$result somewhere.  The two problems I have at runtime without the type info is...

  1. I don't know the size of the struct, so I don't know how much memory to allocate.
  2. I can't retrieve values from the struct because I don't know the offsets or field types anymore  (emscripten's "getValue" function requires an 'address' and 'type' as arguments).

Is there a better way of doing this now with the newer compiler that I am not aware of?

Here is how I used to be able to do it:

var GetCursorLocation = Module.cwrap("ScreenGetCursorLocation", "number", ["number", "number"]);

var
metadata = Runtime.typeInfo["SFLocation"];
var locationPtr = _malloc(metadata.flatSize);

GetCursorLocation(locationPtr, screen);

var x = getValue(locationPtr + metadata.flatIndexes[0], metadata.fields[0]);

var y = getValue(locationPtr + metadata.flatIndexes[1], metadata.fields[1]);
// ETC, ETC

_free(locationPtr);

Alon Zakai

unread,
Feb 4, 2016, 3:45:52 PM2/4/16
to emscripten-discuss
They let you get and set fields on structs/classes. Or, you can create C methods to do that (or return the size/offset etc.), compile and call those, which is essentially what the WebIDL binder and embind do under the hood.

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jeff D

unread,
Feb 4, 2016, 5:01:43 PM2/4/16
to emscripten-discuss
WebIDL looks promising... I was definitely not looking forward to writing a ton sizeof() and offsetof() functions!

After playing around with it a bit, I think I can make this work if I mark all my struct WebIDL interfaces as [NoDelete] and write C methods for allocating and freeing the structs.  

Thanks for the advice.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-discuss+unsub...@googlegroups.com.

Jeff D

unread,
Feb 5, 2016, 4:59:00 PM2/5/16
to emscripten-discuss
Throwing this out there in case anybody comes across the same issue.


C:
typedef struct SFColor {
    float r;
    float g;
    float b;
} SFColor;

typedef struct SFAreaInfo
{
    char * name;
    double multiplier;
    SFColor color;
    bool livingArea;
bool perimeter;
} SFAreaInfo;

WebIDL:
[NoDelete]
interface SFColor{
  attribute float r;
  attribute float g;
  attribute float b;
};

[NoDelete]
interface SFAreaInfo{
  attribute DOMString name;
  attribute double multiplier;
  [Value]attribute SFColor color;
  attribute boolean livingArea;
  attribute boolean perimeter;
};

I initially got tripped up on the nested struct because without the [Value] flag it tries to assign a 'SFColor *'(pointer) to the color field.


After that, for the generated .cpp file, I wrote a script to remove the 'extern "C" {' stuff, and stripped out the "VoidPtr" destructor that it automatically creates (it uses the delete keyword).
Reply all
Reply to author
Forward
0 new messages