Ockonal Ockonal - 1 month ago 6
C++ Question

Export template function

what's the right way to export template function from c++ into python using boost.python? Here is the code:

template<typename T>
T getValue(const std::string &key, const T &defaultValue = T()) {}

// Export into some python class:
.def("GetValue", getValue<int>)
.def("GetValue", getValue<float>)
.def("GetValue", getValue<std::string>);

And usage:

print GetValue("width")
Boost.Python.ArgumentError: Python argument types in
GetValue(ConfigManager, str)
did not match C++ signature:
GetValue(ConfigManager {lvalue}, std::string, int)

What's wrong?


You should read the relevant Boost documentation regarding default arguments. I'll summarize below.

The problem here is that default arguments are used when calling functions in C++. Get rid of them and you'll see the problem from Python's perspective:

// this function *must* be called with two parameters
template<typename T>
T getValue(const std::string &key, const T &defaultValue) {}

.def("GetValue", getValue<int>) // two arguments!
.def("GetValue", getValue<float>) // Python has no idea about the defaults,
.def("GetValue", getValue<std::string>); // they are a C++ feature for calling

The fundamental issue is that function types don't carry default argument information. So how can we simulate it? Essentially, by overloading:

template<typename T>
T getValue(const std::string &key, const T &defaultValue) {}

template<typename T>
T getValueDefault(const std::string &key)
    // default available in C++,
    // transitively available in Python
    return getValue(key);

.def("GetValue", getValue<int>) // two arguments
.def("GetValue", getValueDefault<int>) // one argument
// and so on

A maintenance hassle. Luckily, Boost makes this easy:

template<typename T>
T getValue(const std::string &key, const T &defaultValue) {}

// creates utility class x, which creates overloads of function y,
// with argument count as low as a and as high as b:

BOOST_PYTHON_FUNCTION_OVERLOADS(getValueIntOverloads, getValue<int>, 1, 2);

.def("GetValue", getValue<int>, getValueIntOverloads()) // one or two arguments

// and so on

The macro also exists for class members. This is in the documentation, if any of it is unclear.