Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Nox

Supermoderator

  • »Nox« ist der Autor dieses Themas

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

1

17.02.2011, 18:23

boost python superkurz Tutorial/Beispiele (c++/python)

Hi,

Mich hat es mittlerweile einiges an Zeit gekostet boost.python soweit zu bringen, dass es alles macht, was ich brauche. Ggf. wird ja jemand anderes auf das gleiche Problem stoßen und sich über diese kurzen (hoffentlich alle funktionierende) Beispiel freuen. Erklärungen etc. spare ich mich, weil da lohnt es sich eher ein Blick in die entsprechenden boost bzw. pythonquellen. Hier geht es echt nur darum funktionierende Beispiel zu liefern, anhand derer man dann seinen Code testen kann (Es wurde boost 1.40 und python 2.6 genutzt):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include <map>
#include <list>
#include <vector>

#include "boost/python.hpp"
#include "boost/python/suite/indexing/map_indexing_suite.hpp"

using boost::ref;
using boost::shared_ptr;
using boost::noncopyable;
using namespace boost::python;

//---------------------------------------------------------------------
//einige Helfer
//---------------------------------------------------------------------
struct GIL // Nur für Threading notwendig. Benötigt ein PyEval_InitThreads() Aufruf direkt nach Py_Initialize() falls man es aus einer main raus nutzen will
{
    GIL()   { gstate = PyGILState_Ensure(); }
    ~GIL()  { PyGILState_Release(gstate); }

private:
    PyGILState_STATE gstate;
};

struct Except : public std::exception
{
    std::string str;
    Except(const char* what) : str(what)        {}
    Except(const std::string &what) : str(what) {}
    ~Except() throw()                           {}
    const char* what() const throw()            { return str.c_str(); }
};

class Self
{
protected:
    PyObject* self;

    Self(void) : self(0)        { }
    Self(PyObject* s) : self(s) { Py_INCREF(self); }
    ~Self()                     { Py_DECREF(self); }
    
    void assoziate(PyObject* s)
    {
        if(self)
            throw Except("c++ instance has already a 'self'");
        self = s;
        Py_INCREF(self);
    }
};

//---------------------------------------------------------------------
//ab hier beginnt der Spass
//---------------------------------------------------------------------

int simple_func(int x,int y)
{
    return x + y;
}

struct dont_create_me_in_python
{
    dont_create_me_in_python(int x) {}
};

struct simple_POD_example
{
    int readonly_val;
    int readwrite_val;
};

struct complex_POD_example
{
    int readonly_val;
    int readwrite_val;

    complex_POD_example(void) : readonly_val(0), readwrite_val(0) {}
    complex_POD_example(int val1) : readonly_val(val1), readwrite_val(0) {}
    complex_POD_example(int val1,int val2) : readonly_val(val1), readwrite_val(val2) {}

    void set1(int val1)                     { readonly_val = val1; }
    void set2(int val1=0)                   { readonly_val = val1; }
    void set3(int val1,int val2=0)          { readonly_val = val1; readwrite_val = val2; }
};

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(Overload_set2, set2, 0, 1)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(Overload_set3, set3, 1, 2)

struct stl_container
{
    std::vector<int>                    vector;
    std::list<simple_POD_example>       list;
    std::map<int,complex_POD_example>   map;

    stl_container(int count)
    {
        vector.insert(vector.begin(),count,10);
        list.insert(list.begin(),count,simple_POD_example());
        for(int counter = 0; counter < count; counter++)
            map.insert(std::make_pair(counter,complex_POD_example(counter)));
    }
};

template <class T> PyObject* get_data(typename std::vector<T>* ptr)
{
    return PyBuffer_FromMemory (&ptr->front(), ptr->size() * sizeof(T));
}

template <class T> T get_item(typename std::vector<T>* ptr, size_t pos)
{
    return (*ptr)[pos];
}

template <class T> void set_item(typename std::vector<T>* ptr, size_t pos, const T& val)
{
    (*ptr)[pos] = val;
}

struct Interface
{
    float val;

    Interface(float x) : val(x) {}

    virtual float pure(const simple_POD_example &e) = 0;
    virtual float overwriteable(float x, float y)           { return x + y; }
    float call_pure(const simple_POD_example &e)            { return pure(e) + val; }
    float call_overwriteable(float x,float y)               { return overwriteable(x,y) + val; }
};

class Interface_callback : public Interface, public Self
{
public:
    Interface_callback(PyObject* s,float x) : Self(s), Interface(x)     {}

    float pure(const simple_POD_example &e)                                 { return call_method<float>(self, "pure", ref(e)); }
    float overwriteable(float x, float y)                                   { return call_method<float>(self, "overwriteable", x, y); }
    static float s_overwriteable(Interface_callback &e,float x, float y)    { return e.Interface::overwriteable(x,y); }

    //Anmerkung: call_method stürzt ab, wenn ein GIL Lock a la
    //http://wiki.python.org/moin/boost.python/HowTo#MultithreadingSupportformyfunction davor geöffnet wird
};

//---------------------------------------------------------------------
//Eigentliches "exposing". Wenn man ein Modul erstellen will, welches von Python aus geladen werden kann,
//muss das Modul als DLL erstellt werden und in diesem Fall den Namen "test.pyd" haben (siehe Linkeroptionen).
//---------------------------------------------------------------------
BOOST_PYTHON_MODULE(test)
{
    //kann wegen des "no_init" nicht von python aus erstellt werden
    class_<dont_create_me_in_python>("dont_create_me_in_python",no_init)
    ;
    
    class_<simple_POD_example>("simple_POD_example")
        .def_readonly("readonly_val", &simple_POD_example::readonly_val)
        .def_readwrite("readwrite_val", &simple_POD_example::readwrite_val)
    ;
    
    class_<complex_POD_example>("complex_POD_example",init<int>())
        .def(init<int,int>())
        .def_readonly("readonly_val", &complex_POD_example::readonly_val)
        .def_readwrite("readwrite_val", &complex_POD_example::readwrite_val)
        .def("set1",&complex_POD_example::set1)
        .def("set2",&complex_POD_example::set2,Overload_set2())
        .def("set3",&complex_POD_example::set3,Overload_set3())
    ;

    class_<stl_container>("stl_container",init<int>())
        .def_readwrite("map", &stl_container::map)
        .def_readwrite("list", &stl_container::list)
        .def_readwrite("vector", &stl_container::vector)
    ;

    class_<std::vector<int> >("testvector")
        .add_property("data", &get_data<int>)
        .def("__iter__", iterator<std::vector<int> >())
        .def("__getitem__", &get_item<int>)
        .def("__setitem__", &set_item<int>)
    ;

    class_<std::list<simple_POD_example> >("testlist")
        .def("__iter__", iterator<std::list<simple_POD_example> >())
    ;

    class_<std::map<int, complex_POD_example> >("testmap")
        .def(map_indexing_suite<std::map<int, complex_POD_example> >())
    ;
    
    
    //Es ist auch "class_<Interface,Interface_callback>" möglich (wenn keine rein virtuellen
    //Methoden existieren), aber dann erstellt Python interne Kopien soweit ich weiß.
    class_<Interface,noncopyable,shared_ptr<Interface_callback> >("Interface",init<float>())
        .def("call_pure",&Interface::call_pure)
        .def("call_overwriteable",&Interface::call_overwriteable)
        .def("overwriteable",&Interface_callback::s_overwriteable)
    ;
}


//---------------------------------------------------------------------
//nur notwendig zum Test bzw fürs "embedding".
//Wenn man nur ein Modul erstellen will, kann man diesen Block auslassen.
//---------------------------------------------------------------------
int main( int argc, char ** argv )
{
    PyImport_AppendInittab( "test", &inittest );

    Py_Initialize();
    object main_module((handle<>(borrowed(PyImport_AddModule("__main__")))));

    
    PyObject* PyFileObject = PyFile_FromString("bindings/test2.py", "r");
    PyRun_SimpleFile(PyFile_AsFile(PyFileObject), "test2.py");

    std::cin.get();
    return 0;
}

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import test
print "hi"

#Test von no_init
try:
    test.dont_create_me_in_python()
except RuntimeError as e:
    print e

#Test von readonly und readwrite
obj = test.simple_POD_example()
obj.readwrite_val = 10

try:
    obj.readonly_val = 10
except AttributeError as e:
    print e

print "Werte von simple_POD_example: ",obj.readonly_val, obj.readwrite_val


#Test von den (ueberladenen) Methoden und den verschiedenen inits
try:
    test.complex_POD_example()
except:
    print "Hierfuer gibt es keinen (python bekannter) ctor"
    
obj1 = test.complex_POD_example(1)
obj2 = test.complex_POD_example(1,1)

print "Werte von complex_POD_example(1): ",obj1.readonly_val, obj1.readwrite_val
print "Werte von complex_POD_example(2): ",obj2.readonly_val, obj2.readwrite_val

obj1.set1(10)
obj2.set2()
obj2.set2(20)
obj1.set3(30)
obj1.set3(50)

print "Werte von complex_POD_example(1): ",obj1.readonly_val, obj1.readwrite_val
print "Werte von complex_POD_example(2): ",obj2.readonly_val, obj2.readwrite_val

#Test der stl container
obj = test.stl_container(10)
print "Vectorbuffer: ", obj.vector.data
print "Vectorinhalt: ", [x for x in obj.vector]
print "Listinhalt: ", [x for x in obj.list]
print "Mapinhalt: ", [x for x in obj.map]
obj.vector[2] = 0
print obj.vector[2]

#Test von Interface/virtual/pure-virtual
class myClass1(test.Interface):
    def __init__(self,val):
        test.Interface.__init__(self,val)
        
class myClass2(test.Interface):
    def __init__(self,val):
        test.Interface.__init__(self,val)
        
    def pure(self,POD):
        return float(POD.readonly_val+POD.readwrite_val)
    
    def overwriteable(self,x,y):
        return (x+y)*2
obj1 = myClass1(100)
obj2 = myClass2(100)
data = test.simple_POD_example()
data.readwrite_val = 10
print "hi2"

print obj1.call_overwriteable(1,1)
print obj2.call_overwriteable(1,1)
print obj2.call_pure(data)

try:
    obj1.call_pure(data)
except RuntimeError as e:
    print "obj1 has no call_pure"


Nachtrag: Für Objekte mit einem eigenen Refcount bieten sich statt den shared_ptr intrusive_ptr an. Dazu muss man zwei freie Funktionen implementieren.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
template<class T> void intrusive_ptr_add_ref(T* ptr)
{
    ptr->grab();
}

template<class T> void intrusive_ptr_release(T* ptr)
{
    ptr->drop();
}
//Nutzung
class_<MemExServerClient,noncopyable,intrusive_ptr<ServerClient_callback> >(.....

Für template Funktionen/Methoden geht man folgendermaßen vor (schick ist, dass man auch freie Funktionen als Methode registieren kann):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<class T> T Read(NetworkBuffer &e)
{
    T ret;
    e.Read(ret);
    return ret;
}
//Nutzung
    class_<NetworkBuffer,noncopyable>("NetworkBuffer")
        .def<void(NetworkBuffer::*)(const int&)>("WriteInt", &NetworkBuffer::Write)
        .def<void(NetworkBuffer::*)(const bool&)>("WriteBool", &NetworkBuffer::Write)
        .def<void(NetworkBuffer::*)(const float&)>("WriteFloat", &NetworkBuffer::Write)
        .def<void(NetworkBuffer::*)(char*)>("WriteString", &NetworkBuffer::Write)
        .def<int(*)(NetworkBuffer&)>("ReadInt", &Read)
        .def<float(*)(NetworkBuffer&)>("ReadFloat", &Read)
        .def<std::string(*)(NetworkBuffer&)>("ReadString", &Read)
        ;
»Nox« hat folgende Datei angehängt:
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »Nox« (16.05.2011, 22:19)


2

19.05.2011, 09:18

associate schreibt man mit c, nicht mit z. Ansonsten... Lesezeichen, vielleicht brauch ichs noch mal. Fürs erste bleib ich bei Lua.

Nox

Supermoderator

  • »Nox« ist der Autor dieses Themas

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

3

19.05.2011, 10:40

Für Lua gibt es ja LUA-Bind was ja stark durch poost.python inspiriert wurde. Im Prinzip ist das sogar fast eine 1:1 Geschichte und sicherlich nicht schlecht, wenn man komplexere Sachen mal anbinden will.

P.S: aktuell gibt es ein problem mit den GIL lock und C Threads. Ich bin noch dabei rauszufinden was genau da schief läuft. Aktuell kann ich nur davon abraten call_method von einem Thread aus aufzurufen, oder falls das bei euch kein Problem ist, bitte ich drum mir die Lösung zu verraten 8) .
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Nox

Supermoderator

  • »Nox« ist der Autor dieses Themas

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

4

01.07.2011, 21:58

Also ich fand das Problem. PyEval_InitThreads(); muss in den BOOST_PYTHON_MODULE gepackt werden, wenn man nicht-Python Threads nutzt. Weil ansonsten ist der GIL anscheinend nicht korrekt initialisiert.
Außerdem ist folgender try - catch um alle call_method etc. die von einem anderen Thread aufgerufen werden von Nöten um sicher zu stellen, dass die Fehlermeldungen auch ausgegeben werden:

try { return call_method<foo*>(self, "foobar", ref(bar)); } catch(boost::python::error_already_set) { PyErr_Print(); throw; return 0; }
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Werbeanzeige