pmed / v8pp

Bind C++ functions and classes into V8 JavaScript engine
http://pmed.github.io/v8pp/
Other
887 stars 119 forks source link

Function & constructor overloading #125

Open ayles opened 4 years ago

ayles commented 4 years ago

I'm also intrested in possibility of setting functions and constructors like:

// constructors
.ctor<double>().ctor<double, double>()
// functions
.function<void, double>(&X::add).function<void, double, double>(&X::add)
// or solve disambiguation with static_cast hint
.function(static_cast<void (X::*)(double)>(&X::add))

Then best match will be selected. As far as I understand adding two or more functions is not as difficult as selecting wich one to use, cause here should be some rules like:

First, try find handler with same argument count. Second, try find exact argument type match. If there is no such handler, ...? In any other case select handler with (const FunctionCallbackInfo &) signature if available.

So, is it possible and do you have any thoughts about it?

ayles commented 4 years ago

Tried to implement some kind of function selecting behaviour. Gist. So we're checking if arguments match for each passed function sequentially. On first match we're calling and just exiting with true returned, or if there was no match false is returned. I'm quite unfamiliar with templates but I think this idea is fine, so check it please...

pmed commented 4 years ago

Hi,

yes, there was such a request earlier in #41. The issue as I see, is the overloaded functions dispatching can be resolved only at run-time, since an overloaded C++ wrapped function can be invoked in JavaScript with any argument list.

So a generic f(v8::FunctionCallbackInfo<v8::Value> const& args) would lookup a suitable C++ function in a registry of overloads.

Current class_ implementation sets a wrapped function value in the class prototype template. Unfortunately, v8::Template has no API to get previously set value, so for overloaded functions v8pp::class_ instance would have to track the function names, in order to replace overloaded ones with a generic dispatching version. And this tracking is required only on the class binding step.

ayles commented 4 years ago

Hmm, I tried to implement simple function wrapping this morning. And it works. Use looks like this:

void f(int a) {
    std::cerr << "One int arg: " << a << std::endl;
}

void f(int a, int b) {
    std::cerr << "Two int args: " << a << " " << b << std::endl;
}

void f(std::string s) {
    std::cerr << "String arg: " << s << std::endl;
}

...

// binding (static_cast here used as hint to compiler, functions with names `f1` `f2` `f3` can be bound just by passing name)
context->Global()->Set(context, v8_str("test"), 
    v8b::wrap_function(isolate, 
        static_cast<void(*)(int, int)>(f), 
        static_cast<void(*)(int)>(f), 
        static_cast<void(*)(std::string)>(f)
    )->GetFunction(context).ToLocalChecked()
);

...

// usage (in js)
test(1); 
test(1, 3); 
test("str");

Output will be

One int arg: 1
Two int args: 1 3
String arg: str

How it works: On function template creation I'm passing v8::External with array of void * pointers to all passed functions. Then I'm binding handler that is result of calling template function get_handler that accepts all passed functions along with their signatures in templates. get_handler builds handler that sequentially will compare args passed from js and functions signatures:

arguments_traits<typename function_traits<F>::arguments>::is_match(info)

And if there is match, it gets function pointer with current index from info.Data(), casts it and calls with converted arguments.

So this approach is half-runtime half-compiletime (and it relies on template instantiation recursion =), and for any functions sequence passed will be generated new handler, but it is quite fast and requires no runtime type checks...

I hope you get the idea, because my English is very bad.

Now I see that it will be hard to just add this functionality to v8pp, it is easier just to rewrite it from scratch...

pmed commented 4 years ago

Thanks for sharing the idea!

I haven't thought before to use an array of functions with the same name for single wrap_function() call.

ayles commented 4 years ago

Hi again) Started implementing ideas inspired by your lib here: https://github.com/ayles/v8bind I'll later add link to v8pp in README. Class member variables binding and multiple constructors binding works fine, will add function overloads binding later.

pmed commented 4 years ago

Hi,

thanks for sharing, and good luck with all your ideas!

Sincerely, Pavel

пт, 6 сент. 2019 г. в 00:36, Alexander Seleznev notifications@github.com:

Hi again) Started implementing ideas inspired by your lib here: https://github.com/ayles/v8bind I'll later add link to v8pp in README. Class member variables binding and multiple constructors binding works fine, will add function overloads binding later.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pmed/v8pp/issues/125?email_source=notifications&email_token=AAISAUVECSREGHR3TVXWCPDQIGCX5A5CNFSM4ISOJYUKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD6BBF2I#issuecomment-528618217, or mute the thread https://github.com/notifications/unsubscribe-auth/AAISAUXWQBZ7BUTNA3ESPJDQIGCX5ANCNFSM4ISOJYUA .