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:
class_<ConfigManager>(...)
.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 matc开发者_C百科h 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) {}
class_<ConfigManager>(...)
.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);
}
class_<ConfigManager>(...)
.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(x, y, a, b);
BOOST_PYTHON_FUNCTION_OVERLOADS(getValueIntOverloads, getValue<int>, 1, 2);
class_<ConfigManager>(...)
.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.
You can also add another template for your class so that you don't have to write/instantiate for each int/float type.
template<typename LinksT>
class Base {
public:
virtual ~Base() {}
virtual Base* x() = 0;
};
#include <boost/python.hpp>
using namespace boost::python;
template<typename LinksT>
class BaseWrap : public Base<LinksT>, public wrapper<Base<LinksT> > {
public:
virtual Base<LinksT>* x() { return this->get_override("x")(); }
};
template<typename LinksT>
void export_virtualfunction()
{
class_<BaseWrap<LinksT>, boost::noncopyable>("Base", no_init)
.def("x", pure_virtual(&Base<LinksT>::x), return_internal_reference<>())
;
}
BOOST_PYTHON_MODULE(test_template_python)
{
export_virtualfunction<int>();
}
精彩评论