Thank you Chad for this detailed answer. This is exactly what I needed.
I found the exact same workaround as yours, but I don't like it because it
requires to write some JavaScript code to call the callback. I've also
thought about the second approach (generated JavaScript functions calling
back C++ functions mapped with ids) but it sounds quite hard to get right
and definitely requires to learn the intrinsics of embind. I think I see
the global picture but I'm still lost in the details. What would be the
process to implement it?
- create a global store like std::map<int, Function> where Function is a
generic std::function with type information erased
- create a dispatcher method which takes an id as input and variadic
parameters and calls the C++ Function (how to deal with type information
here?)
- register a new wire type for each std::function<T> signature used
- put the function in the map associated with its unique id
- define a new function named _embind_callback_<id> using
craftInvokerFunction
or similar which calls the C++ dispatcher
- use _embind_callback_<id> wherever a function is passed as argument
Am I missing something? By the way, this approach doesn't solve the memory
leak and I can't think of a way to fix it since there is no way to specify
a destructor for the allocated C++ function object.
On Tuesday, July 1, 2014 2:36:56 AM UTC+2, Chad Austin wrote:
>
> Hi Warren,
>
> embind and emval don't currently support this functionality. That doesn't
> mean it's impossible, just that we haven't thought through how it work.
> There are two main challenges. Let's discuss them and see what we can do
> here.
>
> Your example of passing a lambda as an argument to to
> emscripten::val::call() is great because it looks nice and simple but it
> hides its unclear lifetime semantics. That is, a C++ lambda is just sugar
> for a class with a call operator. In your example, that class is allocated
> on the stack and passed by either value or rvalue reference to the call<>
> function. Since the lambda's type has never been registered with embind,
> it doesn't know how to convert it into a JavaScript object.
>
> Even if embind did know how to convert the lambda into a JavaScript
> object, that JavaScript would have a pointer to a C++ heap object, which it
> would then need to deallocate at some point. How and when would it
> deallocate it? JavaScript just assumes everything's garbage collected, so
> it would never notify Emscripten that it needs to free some heap memory.
> Thus, every use of this API would leak memory.
>
> What we've done here at IMVU is have the C++ bind an interface with
> callback methods on it, and then have a JavaScript implementation of that
> interface that sets up the callbacks. Something like:
>
> struct Callbacks {
> void invoke() {
> callback();
> }
>
> std::function<void()> callback;
> };
>
> class_<Callbacks>("Callbacks")
> .function("invoke", &Callbacks::invoke);
> ;
>
> Then you can have a bit of JavaScript that, when given a Callbacks object,
> will do something like:
>
> function registerCallback(cb) {
> object.on('connect', function() {
> cb.invoke();
> // when do you deallocate the callback object? that's up to your
> application
> });
> }
>
> Is there a nicer approach? Perhaps!
>
> Maybe we could do something where we can generate JS function objects
> associated with integer handles and then C++ could map each integer handle
> back to the appropriate std::function...
>
> Do you have any ideas?
>
> On Sat, Jun 28, 2014 at 4:03 PM, Warren Seine <[email protected]
> <javascript:>> wrote:
>
>> I'm using embind to wrap a JS library in C++. The library uses callbacks
>> such as:
>>>
>>> object.on('connect', function() {
>>> console.log('connected');
>>> });
>>
>> How can I use a C++ function as callback?
>>
>> Ideally, I'd like to do something like:
>>
>>> val object = val::global("object");
>>> object.call<void>("on", std::string("connect"), []() {
>>> std::cout << "connected" << std::endl;
>>> });
>>
>> which, unfortunately, throws "Uncaught BindingError: parameter 2 has
>> unknown type" with a mangled lambda type name.
>>
>> Chad recently posted something related to C++ callbacks
>> <http://chadaustin.me/2014/06/emscripten-callbacks-and-c11-lambdas/> but
>> doesn't talk about this specific issue. If that's unsupported, what would
>> embind need to support C++ callbacks?
>>
>> Also, what's the workaround?
>>
>>
> --
> Chad Austin
> Technical Director, IMVU
> http://engineering.imvu.com <http://www.imvu.com/members/Chad/>
> http://chadaustin.me
>
--
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 [email protected].
For more options, visit https://groups.google.com/d/optout.