RosettaCommons / binder

Binder, tool for automatic generation of Python bindings
MIT License
321 stars 67 forks source link

Using Binder for templated methods #101

Open adamnovak opened 4 years ago

adamnovak commented 4 years ago

In #80 there's an example for how to confince Binder to bind an instantiation of a template class:

struct MyStructDouble : public test_my_struct<double> {};

I have a non-template class with some template methods (which can be instantiated for std::function or for various unique lambda function types). I want Binder to bind one or more instantiations of this method (in particular, one I can pass a Python callable to). Do I need to use a custom +binder that wraps the class and hides the template method behind a non-template method? Or is there a way to tell Binder to instantiate the method more directly?

I already tried:

+function handlegraph::PathHandleGraph::for_each_path_handle<std::function<bool(handlegraph::path_handle_t)>>

This doesn't seem to instantiate the template method.

lyskov commented 4 years ago

Unfortunately Binder can not instantiate template methods so +bind directives will not help (it only control bindings of things-that-could-be-bound). @adamnovak have you tried to explicitly instantiate these template methods? (see https://stackoverflow.com/questions/4933056/how-do-i-explicitly-instantiate-a-template-function)

If above does not help then try this: in one of the header of your project add dummy inline function with body like: void _trigger_bindings(MyClass &o) { o.my_function<SomeType>(<function-args>); }; (you might beed to use std::declval to create "dummy" input arguments for this function call to compile)

adamnovak commented 4 years ago

Explicitly instantiating the necessary methods in a header that's just for Binder is what I ended up doing. But that required some build system tomfoolery to get Binder to use the header even though no real source files include it, and the header still ends up getting installed alongside the actual headers for the project, when it's only there to control Binder at build time.

It would be nice if Binder were to gain the ability to instantiate template methods.

lyskov commented 4 years ago

Have you consider wrapping these instantiation in ifdef's (say MY_PYTHON_PROJECT_NAME) in the same header when class was defined and then defining them only when running Binder? That way this extra code will be invisible to main project build system.

It would be nice if Binder were to gain the ability to instantiate template methods.

-- absolutely! Unfortunately so far i have not found a way to do it.

EricCousineau-TRI commented 4 years ago

FWIW genpybind and autopybind11 both take the similar approach of explicitly specifying your instantiations (but autopybind11 through config -> codegen -> clang, whereas the other just suggests explicit instantiations): https://gitlab.kitware.com/autopybind11/autopybind11/-/blob/eb4e7270710cf34c2f82b15f90196f70ad0ae4ac/README.rst#preparing-c-code https://github.com/kljohann/genpybind/blob/f264e5a20a55dc5069c8eb7f618e80139b3740b0/tests/explicit_template_function.h

I've briefly (but unconstructively) complained some about autopybind11's setup in this issue :P https://gitlab.kitware.com/autopybind11/autopybind11/-/issues/92 Brad in that issue was right that I needed a better motivating / concrete case. Also, my takeaway, though, is that explicit instantiations could be helpful for speeding up compilation anywho, regardless of defining concrete symbols for binding.