Posts tagged ‘basic_string’

Przypuśćmy, że mamy gotowy program w c++, który sobie wczytuje pewne dane, operuje na nich i wypluwa wynik, nic wielkiego, przykładów można znaleźć tysiące. Jednak nasz program, żeby być w miarę elastycznym obsługuje pluginy w formie plików bibliotecznych (dll/so), ładowanych na żądanie, ale chcielibyśmy skorzystać z elastyczności jaką daje język skryptowy, np. python a jednocześnie cały czas mieć możliwość optymalizacji kodu po stronie c++.

Rozwiązaniem jest biblioteka boost::python, ale podobnie jak w przypadku boost::shared_ptr dokumentacje mają niezłą, więc wprowadzenia robić nie będę.

Chciałbym się skupić na konwesji typów pomiędzy c++ a python’em i odwrotnie a konkretnie przedstawić konwerter pomiędzy std::basic_string<unsgned char>, który jest wykorzystywany do przechowywania danych wewnątrz elementu (np. Field) z i do pythonowego stringa, mamy już wyeksportowane api, które może mieć postać:

class SampleObject {
public:
void setValue( std::basic_string v& ) {
// do some important stuff
}
}

Jednak po wyeksportowaniu takiej metody okazuje się, że wołając z pythona:sampleObject.setValue( ‚sample value’ )Dostajemy błąd o niezgodności typów i pozostaje nam tylko przyglądać sie komunikatowi błędu.

Rozwiązaniem jest napisanie własnego konwertera, który weźmie stringa pythonowego, zamieni go na unsigned char* i przekaże dalej gdzie trzeba. Oczywiście nie jest to jedyne rozwiązanie, bo równie dobrze można sobie przeciążyć dana metode we wrapperze do api pythonowym, i w niej zrobić całe czary-mary, ale to takie szablonowe…

No więc nasz konwerter, wie że dostanie PyString albo std::basic_string<unsigned char> i ma wyprodukować to co trzeba, oto i on:

typedef std::basic_string ustring;
namespace bp = boost::python;
namespace tulips{
namespace converters {
namespace ustr {
// definiujemy zawijacza na ustring
class custom_string {
public:
custom_string() {}
custom_string(const ustring& value) :
 	m_string(value) {
}
const ustring& value() const {
return m_string;
}
private:
ustring m_string;
};
struct custom_string_to_python_str {
static PyObject* convert(const custom_string& s) {
return bp::incref(bp::object(s.value()).ptr());
}
};
struct custom_string_from_python_str {
custom_string_from_python_str() {
bp::converter::registry::push_back(&convertible, &construct, bp::type_id());
}// sprawdz czy konwersja jest mozliwa
static void* convertible(PyObject* obj_ptr) {
if (!PyString_Check(obj_ptr))
return 0; //zero 🙂
return obj_ptr;
}// no i przekonwertuj
static void construct(PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data) {

// pobierze stringa z obiektu PyObject
const char* str = PyString_AsString(obj_ptr);
const unsigned char* value = (const unsigned char*) str; // cast na unsigned char*
if (value == 0) // sprawdz czy napewno sie udał
bp::throw_error_already_set();
void* storage = ((bp::converter::rvalue_from_python_storage*) data)->storage.bytes; // pobierz pamiec zalakowana w storage'u
new (storage) custom_string(value); // przypisz custom_string'a do pamieci
data->convertible = storage; // zapisz przekonwertowany typ
}
};
// jeszcze tylko pobierzemy sobie rozmiar to tak zeby len() tez zadzialalo
std::size_t size(const custom_string& s) {
return s.value().size();
}
// no i trzeba powiadomic pythona jak ma przeprowadzic konwersje
void init_converter() {
bp::to_python_converter();

custom_string_from_python_str();

bp::def("size", size);
}}
}
} // namespace tulips::converters::ustring

Voila, to nie było aż takie trudne!