9

I need to build python bindings for a C++ codebase. I use boost::python and I ran into problems trying to expose classes containing functions using and returning templates. Here is a typical example

class Foo 
{ 
    public: 
        Foo(); 
        template<typename T> Foo& setValue(
            const string& propertyName, const T& value); 
        template<typename T> const T& getValue(
            const string& propertyName); 
}; 

Typical T are string, double, vector.

After reading the documentation, I tried using thin wrappers for every type used. Here are the wrappers for string and double and the corresponding class declaration.

Foo & (Foo::*setValueDouble)(const std::string&,const double &) = 
    &Foo::setValue; 
const double & (Foo::*getValueDouble)(const std::string&) = 
    &Foo::getValue;

Foo & (Foo::*setValueString)(const std::string&,const std::string &) = 
    &Foo::setValue; 
const std::string & (Foo::*getValueString)(const std::string&) = 
    &Foo::getValue;

class_<Foo>("Foo") 
    .def("setValue",setValueDouble, 
        return_value_policy<reference_existing_object>()) 
    .def("getValue",getValueDouble,
        return_value_policy<copy_const_reference>()) 
    .def("getValue",getValueString, 
        return_value_policy<copy_const_reference>()) 
    .def("setValue",setValueString, 
        return_value_policy<reference_existing_object>());

It compiles ok but when I try to use the python bindings, I get a C++ exception.

>>> f = Foo()  
>>> f.setValue("key",1.0) 
>>> f.getValue("key") 
Traceback (most recent call last): 
  File "<stdin>", line 1, in ? 
  RuntimeError: unidentifiable C++ exception

Interestingly, when I only expose Foo for double or string value, i.e

class_<Foo>("Foo") 
    .def("getValue",getValueString, 
        return_value_policy<copy_const_reference>()) 
    .def("setValue",setValueString, 
        return_value_policy<reference_existing_object>());

It works fine. Am I missing something?

1

3 Answers 3

3

This may not be related to your problem directly but I would not trust function signature casting with templates like that. I would have wrapped it like this:

class_<Foo>("Foo") 
    .def("setValue", &Foo::setValue<double>, 
        return_value_policy<reference_existing_object>()) 
    .def("getValue", &Foo::getValue<double>,
        return_value_policy<copy_const_reference>()) 
    .def("getValue", &Foo::getValue<std::string>, 
        return_value_policy<copy_const_reference>()) 
    .def("setValue", &Foo::setValue<std::string>, 
        return_value_policy<reference_existing_object>());

If that does not work, you may need to create some shim functions:

Foo& setValueDouble(foo& self, const string& propertyName, const double value)
{ 
    return self.setValue(propertyName, value)
}
...

and export those as thought they were member functions.

Exporting multiple function overloads to the same name is a perfectly valid thing to do in Boost::Python, so I do not think that that is the problem.

Sign up to request clarification or add additional context in comments.

1 Comment

I don't see any casting in the code in the question. That's how you declare a variable of type "pointer-to-member-function".
0

I suspect the problem is that boost::python does not know which overload to call for "getValue" - should it call getValueDouble or getValueString? If you bind them explicitly as getValueString and getValueDouble (as the method name) I bet it will work.

4 Comments

Thank you for your help. I tried what you suggest and it worked. But it makes the resulting python code more verbose since I have to use different getter depending on the value type. Maybe there is a more elegant way?
This should not be necessary. Exporting multiple signatures to the same name is a perfectly valid thing to do in in Boost::Python.
@Louis: I don't understand how you want python to know which overload you want... Even in C++, you have to be verbose (as you say) by specifying the type: foo.getValue<double>(...)
@rafak. You are correct. Thank you. I guess my C++ knowledge is a bit rusty right now.
0

What about creating a C++ wrappers for getters/setters that return/take a boost::python::object? You can then simply determine the type you got in your C++ wrapper and wrap/unwrap it into/from boost::python::object.

struct FooWrap : public Foo
{
    using boost::python;
    Foo& setValueO(const string& propertyName, const object& obj)
    {
        object value;
        if(PyInt_Check(obj.ptr())) {
            return setValue<int>(propertyName, extract<int>(obj);
        } else if(PyFloat_Check(obj.ptr())) {
            return setValue<double>(propertyName, extract<double>(obj);
        } else if(PyString_Check(obj.ptr())) {
            return setValue<std::string>(propertyName, extract<std::string>(obj);
        }
        // etc...
    }

    object getValueO(const std::string& propertyName)
    {
        if(determineType() == TYPE_INT) { // however you determine the type
            return object(getValue<int>(propertyName));
        } else if(determineType() == TYPE_DOUBLE)   {
            return object(getValue<double>(propertyName));
        } else if(determineType() == TYPE_STRING)   {
            return object(getValue<std::string>(propertyName));
        }
        // etc...
    }
};

class_<Foo>("Foo") 
    .def("setValue", &FooWrap::setValueO, 
        return_value_policy<reference_existing_object>()) 
    .def("getValue", &FooWrap::getValueO,
        return_value_policy<copy_const_reference>()) 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.