5

I am trying to create python bindings for some C++ functions using pybind11. The functions are templated but in the python bindings, I need one of the template parameter to be a function argument. Currently the way I have it is a lot of repetition for each template parameter.

enum MyEnum {E1, E2, E3};

template<typename T, MyEnum E>
returnType templFunction(int arg){
    <function body>
}

PYBIND11_MODULE(moduleName, m){
    m.def("pyFunction1", [](int arg, MyEnum e){
            switch(e){
                case MyEnum::E1: return templFunction<templParam1, E1>(arg);
                case MyEnum::E2: return templFunction<templParam1, E2>(arg);
                case MyEnum::E3: return templFunction<templParam1, E3>(arg);
                default: throw std::invalid_argument("Unknown enum type ...");
                }
        });

    m.def("pyFunction2", [](int arg, MyEnum e){
            switch(e){
                case MyEnum::E1: return templFunction<templParam2, E1>(arg);
                case MyEnum::E2: return templFunction<templParam2, E2>(arg);
                case MyEnum::E3: return templFunction<templParam2, E3>(arg);
                default: throw std::invalid_argument("Unknown enum type ...");
                }
        });

    // for template type temlParam3, only E1 and E2 are valid enums
    m.def("pyFunction3", [](int arg, MyEnum e){
            switch(e){
                case MyEnum::E1: return templFunction<templParam3, E1>(arg);
                case MyEnum::E2: return templFunction<templParam3, E2>(arg);
                default: throw std::invalid_argument("Unknown enum type ...");
                }
        });

    // more specializations like above

    };

Is there a good way to maybe make a higher order function so I don't need to have so much repeated code?

I also have other functions with similar signatures and template parameters. So ideally I would like to pass a function handle (or function name) and one of the template parameters to a "meta-function" that can generate specializations based on enum and the given template parameters.

Note: I have a static attribute on each of the template types (templParam1, templateParam2, ...) which determine the allowed enum types for that template parameter. For e.g. templParam1::allowedEnumTypes = std::vector<myEnum> { E1, E2, E3 };

Bilentor
  • 486
  • 1
  • 5
  • 13
  • Possible duplicate of [Execute integer templated function based on run-time parameter](https://stackoverflow.com/questions/39942813/execute-integer-templated-function-based-on-run-time-parameter) – ShadowRanger Apr 26 '19 at 12:35

1 Answers1

1

You can write

template<typename T, MyEnum... Cases>
return_type switcher(int arg, MyEnum e)
{
    return_type r;
    bool tests[] = {(e == Cases ? (r = templFunction<T, Cases>(arg), true) : false)...};
    for(auto t : tests)
        if(t)
            return r;
    throw std::invalid_argument("Unknown enum type ..."); 
}

and use as

m.def("pyFunction1", switcher<templParam1, E1, E2, E3>);
m.def("pyFunction2", switcher<templParam2, E1, E2, E3>);
m.def("pyFunction3", switcher<templParam3, E1, E2>);
Passer By
  • 19,325
  • 6
  • 49
  • 96
  • You might get rid of bool array the same way you use only one return_type: `bool done = false; ((e == Cases ? r = f(arg), done |= true : false), ...);` (or even create lambda instead of the ternary operator) – Jarod42 Apr 26 '19 at 17:09
  • @Jarod42 I originally wrote a fold expression, but later decided to write for c++11. I'd have an array either way, so might as well use it. – Passer By Apr 26 '19 at 18:27
  • @PasserBy This is a great solution and works well for my use! Is it possible to template the switcher on function `templFunction` as well? I have several functions with same signature and template type so it would be nice to reuse the `switcher` function. – Bilentor Apr 26 '19 at 21:40
  • @Bilentor You may put the function in a class, pass it as another template parameter and call it like `Fn{}.template operator()(arg)` – Passer By Apr 27 '19 at 05:34