2011-01-30 13 views
9

Potrzebuję zbudować wiązania Pythona dla kodu C++. Używam boost :: python i natknąłem się na problemy próbujące ujawnić klasy zawierające funkcje używające i zwracające szablony. Oto typowy przykładZwiększenie :: python Ujawnianie funkcji C++ przy użyciu i zwracaniu szablonów

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

Typowy T są łańcuch, podwójne, wektor.

Po przeczytaniu documentation próbowałem używać cienkich obwolut do każdego użytego typu. Oto owijki na łańcuch i podwójne oraz odpowiadające im deklaracje klas.

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

Kompiluje się dobrze, ale gdy próbuję użyć powiązań Pythona, otrzymuję wyjątek 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 

Co ciekawe, kiedy tylko narazić Foo dla wartości podwójnym lub strun, tj

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

To działa dobrze. Czy czegoś brakuje?

+1

można zajrzeć do wątku na http://mail.python.org/pipermail/cplusplus-sig/2006-February/009990.html (który porusza podobny problem: impuls owijania :: dowolny) – lijie

Odpowiedz

3

Nie może to być bezpośrednio związane z twoim problemem, ale nie ufam, że funkcja odlewania podpisów będzie taka jak w szablonach. Ja bym owinął je tak:

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

Jeśli to nie zadziała, być może trzeba utworzyć niektóre funkcje podkładek:

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

i eksportować je jako że były funkcje składowe.

Eksport wielokrotnych przeciążeń funkcji o tej samej nazwie jest całkowicie prawidłowy w Boost :: Pythonie, więc nie sądzę, że to jest problem.

+0

Nie widzę żadnego castingu w kodzie w pytaniu. Tak deklarujesz zmienną typu "wskaźnik-do-funkcji-członka". –

+0

To zadziałało dzięki. – LouisChiffre

0

Podejrzewam, że problem polega na tym, że boost :: python nie wie, które przeciążenie wywołuje "getValue" - czy powinien wywołać getValueDouble lub getValueString? Jeśli wiążisz je jawnie jako getValueString i getValueDouble (jako nazwa metody), założę się, że to zadziała.

+0

Dziękuję za pomoc. Próbowałem tego, co sugerujesz i zadziałało. Ale powoduje, że wynikowy kod Pythona jest bardziej szczegółowy, ponieważ muszę użyć innego gettera, w zależności od typu wartości. Może jest bardziej elegancki sposób? – LouisChiffre

+0

To nie powinno być konieczne. Eksportowanie wielu podpisów pod tą samą nazwą jest całkowicie poprawną czynnością w Boost :: Python. –

+0

@Louis: Nie rozumiem, jak chcesz python wiedzieć, które przeciążenie chcesz ... Nawet w C++, musisz być verbose (jak mówisz), określając typ: 'foo.getValue (...) ' – rafak

0

A co z tworzeniem wrapperów C++ dla getterów/setterów zwracających/pobierających boost :: python :: object? Następnie możesz po prostu określić typ, jaki masz w opakowaniu C++ i owinąć/rozwinąć go do/z 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>()) 
Powiązane problemy