2011-01-30 12 views
9

Necesito construir enlaces python para una base de código C++. Utilizo boost :: python y encontré problemas al tratar de exponer las clases que contienen funciones que usan y devuelven plantillas. Aquí está un ejemplo típicoBoost :: python Exponer funciones C++ usando y devolviendo plantillas

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

Típico T son de cadena, doble, vector.

Después de leer el documentation, traté de usar envoltorios finos para cada tipo usado. Aquí están los envoltorios para cadena y doble y la declaración de clase correspondiente.

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>()); 

Compila bien, pero cuando intento usar los enlaces de python, obtengo una excepción de C++.

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

Curiosamente, cuando sólo expongo Foo por el valor doble o de cadena, es decir

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

funciona bien. ¿Me estoy perdiendo algo?

+1

es posible que desee ver en el hilo en http://mail.python.org/pipermail/cplusplus-sig/2006-February/009990.html (que aborda un problema similar: envolver cualquier impulso ::) – lijie

Respuesta

3

Esto puede no estar relacionado con su problema directamente, pero no me fiaría de la fusión de firma de funciones con plantillas como esa. Me hubiera envuelto de esta manera:

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>()); 

Si eso no funciona, puede que tenga que crear algunas funciones de la calza:

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

y exportar aquellos que pensaban que eran las funciones miembro.

Exportar múltiples sobrecargas de funciones con el mismo nombre es una cosa perfectamente válida para hacer en Boost :: Python, así que no creo que ese sea el problema.

+0

No veo ningún molde en el código en la pregunta. Así es como declaras una variable de tipo "pointer-to-member-function". –

+0

Funcionó gracias. – LouisChiffre

0

Sospecho que el problema es que boost :: python no sabe a qué sobrecarga llamar para "getValue" - ¿debería llamar a getValueDouble o getValueString? Si los vincula explícitamente como getValueString y getValueDouble (como el nombre del método), apuesto a que funcionará.

+0

Gracias por su ayuda. Intenté con lo que sugieres y funcionó. Pero hace que el código python resultante sea más detallado ya que tengo que usar diferentes getter dependiendo del tipo de valor. Tal vez hay una manera más elegante? – LouisChiffre

+0

Esto no debería ser necesario. Exportar múltiples firmas al mismo nombre es una cosa perfectamente válida para hacer en Boost :: Python. –

+0

@Louis: no entiendo cómo quieres que python sepa qué sobrecarga quieres ... Incluso en C++, tienes que ser prolijo (como dices) especificando el tipo: 'foo.getValue (...) ' – rafak

0

¿Qué tal crear una envoltura de C++ para getters/setters que devuelven/toman un impulso :: python :: object? A continuación, puede simplemente determinar el tipo que obtuvo en su contenedor de C++ y ajustarlo/desenvolverlo en/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>()) 
Cuestiones relacionadas