Python in C++ Project(e) einbinden (wie in Battlefield)

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

Hi leute,
ich arbeite momentan an einem C++ Project (GHost++; http://forums.codelain.com) und möchte hier wie in der berühmten Battlefield Reihe python einbinden. Das schaut in battlefield (falls ihr bf nicht kennt) etwa so aus:
Man hat einige .py-dateien in einem bestimmten Ordner im BF-Installationsverzeichnis. Die führt dann BF aus. Man kann hier auf einige (leider nicht alle) Python Befehle zugreifen.
Der BF-Python interpreter zieht sich nur die "__init__.py" datei "rein". Hier ist es natürlich möglich, alle möglichen anderen module zu importieren. Etwa so:
__init__.py

Code: Alles auswählen

import meineDatei
meineDatei.py

Code: Alles auswählen

import ghost #direkt auf mein C++ projekt bezogen

def init():
     ghost.registerHandler( 'PlayerJoin', onPlayerJoin ) #so wird es in BF gemacht ( import host; host.registerHandler( 'PlayerJoin', onPlayerJoin )

def onPlayerJoin(player):
    if player.getName().lower() == "Krauzi":
        ghost.sayAll("Krauzi joined the game")
Ich habe mal gelesen, dass man das irgendwie mit einem #include "Python.h" machen kann, jedoch war das immer nur für python code INNERHALB des c++ codes gedacht (bzw so war es auf den siten, wo ich das gefunden habe). Mein Gedanke ist halt, dass man nicht jedes mal das ganze C++ Project neu kompilieren muss, wenn man kleine änderungen im Programm macht.

Ich hoffe, das ist soweit verständlich

MfG Krauzi

P.s.: GHost++ ist ein WarCraft 3 - Bot zum hosten von spielen/kicken/muten/etc.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Du suchst vermutlich Boost.Python.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

das hab ich auch schon gefunden, aber ich weis leider net so genau, wie ich das einbinden kann.....
und da habe ich nur support für python 2.2 gefunden. BF2 zum Beistift setzt aber "schon" 2.3 ein. Verwenden die dann auch boost?
BlackJack

@Krauzi: Du hast die gesamte Doku davon gelesen und verstanden und weisst nicht, wie man's einbindet?

Laut Changelog wird seit 2005 Python 2.4 als Voreinstellung erwartet: `The build now assumes Python 2.4 by default, rather than 2.2`. Keine Ahnung wie gut das mit aktuellen Python-Varianten zusammenarbeitet. Die Bibliotheken auf meinem Linux-Rechner haben jedenfalls Python 2.5 als Abhängigkeit.
lunar

Darii hat geschrieben:Du suchst vermutlich Boost.Python.
Boost.Python ist für größere Projekte ungeeignet, weil es zum Kompilieren extrem viel Speicher und Zeit braucht, und auch zur Laufzeit sehr viel Speicher belegt.

Zudem funktioniert Boost 1.39 nicht mit 2.6.3, ob Boost 1.40 funktioniert, habe ich nicht probiert.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

das ist sehr schlecht.
Ich könnte hier MINIMAL auf 1.38 zurückgreifen (also MINDESTENS 1.38 ).
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

hm ich weis jetzt zwar, wie man c++ in python verwendet, aber ich möchte es ja noch erweitern:
meine c++.exe soll python dateien importieren...
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

lunar hat geschrieben:
Darii hat geschrieben:Du suchst vermutlich Boost.Python.
Boost.Python ist für größere Projekte ungeeignet, weil es zum Kompilieren extrem viel Speicher und Zeit braucht, und auch zur Laufzeit sehr viel Speicher belegt.
Ich würde eher sagen, C++ ist ungeeignet um Scriptsprachen einzubinden(nicht standardisiertes name mangling, keinerlei introspection).

@Krauzi: Siehe Tutorial: http://www.boost.org/doc/libs/1_40_0/li ... dding.html
lunar

@Darii: Boost.Python funktioniert nicht, weil es Änderungen an der Python-API gegeben hat, mit denen Boost.Python nicht klarkommt. Mit Name Mangeling und Introspection hat das nichts zu tun. C++ ist deswegen auch nicht besser oder schlechter zur Einbettung geeignet als andere Sprachen.

Um C++-Typen dem Interpreter bekannt zu machen, muss man zwangsläufig Wrapper-Klassen erstellen. Folglich muss man auch ebenso zwangsläufig kompilieren. Damit ist Name Mangeling Sache des Compilers, und man muss letztlich wie immer hoffen, dass die Binärschnittstellen der verwendeten Compiler zusammenpassen.

Introspection ist auch nicht nötig. Da C++-Typen sich zur Laufzeit nicht ändern, kann man die nötigen Metainformationen auch statisch erzeugen.

C++ ist aus anderen Gründen problematisch.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

@lunar: Was würdest Du als Alternative für Boost.Python empfehlen?
Py++ als Ergänzung zu B.P funktioniert dann ja wohl auch nicht. Wie steht es mit Swig oder Pybindgen? Hast Du von diesem schon etwas gehört?
MfG
HWK
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

lunar hat geschrieben:Um C++-Typen dem Interpreter bekannt zu machen, muss man zwangsläufig Wrapper-Klassen erstellen. Folglich muss man auch ebenso zwangsläufig kompilieren. Damit ist Name Mangeling Sache des Compilers, und man muss letztlich wie immer hoffen, dass die Binärschnittstellen der verwendeten Compiler zusammenpassen.

Introspection ist auch nicht nötig. Da C++-Typen sich zur Laufzeit nicht ändern, kann man die nötigen Metainformationen auch statisch erzeugen.
Richtig, und ohne (nicht standardisiertem) name mangeling und mit introspection wäre beides nicht nötig. Es geht natürlich auch so und ist auch in dem Fall nicht so schlimm, weil man für Scripting in Spielen meist sowieso nur klar definierte ggf. abgespeckte Schnittstellen haben möchte.

Aber ansonsten ist es halt krampfig, dass man für jede Bibiolthek(z.B.) Qt extra, ggf zueinander inkompatible Bindings braucht.
lunar

@HWK: Ich kann keine Alternative bieten. Ich stand noch nie vor dem Problem, eine Anbindung an eine größere C++-Bibliothek schreiben oder warten zu müssen. Müsste ich das, würde ich wahrscheinlich SIP nutzen. Nicht, weil ich damit Erfahrung habe, sondern weil man Qt4 sehen kann, dass es offenbar gut funktioniert.

Das Boost.Python nicht funktioniert und ansonsten nicht sonderlich effizient ist, weiß ich nur, weil ich gegen eine damit angebundene Bibliothek programmiert habe.

@Darii: Es ist schon richtig, dass der C++-Standard über Symbolnamen und Binärschnittstelle keine Aussagen trifft. Mit dem Anspruch der Architekturunabhängigkeit wäre das kaum vereinbar. Allerdings gilt das auch für C. Die Umsetzung von Bezeichnern auf Symbolnamen ist bei C++ genauso unspezifiziert wie bei C. Der binäre Aufbau von Klassen ist genauso unspezifiziert wie der binäre Aufbau von Strukturen. Genau deswegen muss man die Strukturen bei ctypes-Anbindungen ja im Python-Quelltext manuell deklarieren.

ctypes funktioniert nur deswegen, weil es für bestimmte Kombinationen von System und Architektur zusätzliche, mehr oder weniger offizielle Standards gibt, welche die Binärschnittstelle regeln (z.B. ELF). Solche Standards aber gibt es auch für C++. Nur deswegen gibt es ja binärkompatible C++-Compiler wie beispielsweise g++ und icc.

Das Problem an C++ ist nicht das Fehlen einer dokumentierten Binärschnittstelle. Wenn man nur wollte, dann könnte man ctypes-c++ unter Linux durchaus implementieren. Nur wird man dann eben sehr schnell mit der unglaublichen Komplexität des Laufzeitmodells von C++ konfrontiert. ctypes-c++ gibt es also vor allem deswegen nicht, weil es sehr schwer zu implementieren wäre. In Anbetracht der Tatsache, dass kompilierte Anbindungen eigentlich gut funktionieren, stellt sich dann irgendwo die Frage nach der Relation zwischen Aufwand und Nutzen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Wie wäre es mit Kross? Allerdings kann ich zur Performance / Useability nichts sagen, da ich außer einigen Spielerein damit noch nichts großes gemacht habe. Größtes Manko könnten natürlich die Abhängigkeit von Qt und einigen KDE-libs sein.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

Hyperion hat geschrieben:Wie wäre es mit Kross? Größtes Manko könnten natürlich die Abhängigkeit von Qt und einigen KDE-libs sein.
und genau deshalb kann ichs vergessen^^.

Ich denke, ich habe jetzt Boost-Python recht gut zum laufen bekommen. Zum Speicherverbrauch muss ich sagen: Der ist recht...... ähm .... *beachtlich* :D. Egal, hauptsache es funzt jetzt.

Allerdings möchte ich jetzt so eine registerHandler funktion machen, nur fehlen mir die ansätze dazu. Ich müsste irgendwie rausbekommen, von welchem modul genau diese funktion aufgerufen wird. Ein Bsp zur verdeutlichung:
__init__.py

Code: Alles auswählen

import myModule
import myModule2
myModule.init()
myModule2.init()
myModule.py

Code: Alles auswählen

import gHost

def init():
    gHost.registerHandler( "myEvent", myFuncForMyEvent )

def myFuncForMyEvent(arg1, arg2, arg3, arg4):
    # do stuff
    return
myModule2.py

Code: Alles auswählen

import gHost

def init():
    gHost.registerHandler( "myEvent", blah )

def blah(arg1, arg2, arg3, arg4):
    # do stuff
    return
So jetzt ist die frage, wie ich rausbekomme, ob registerHandler von myModule oder von myModule2 aus aufgerufen wurde. Irgendwie sollte es ja möglich sein.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Wozu musst du jetzt das Modul rausbekommen? Mir schwant, dass sich dein Problem in Luft auflösen würde, wenn du dir eine saubere Schnittstelle überlegen würdest. Also was willst du machen?
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

Ja richtig so ne Schnittstelle will ich machen. Nur hab ich keine Ahnung / keinen Ansatz wie das gehen soll. Könnte mir da jemand mit ner guten Idee behilflich sein?
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Krauzi hat geschrieben:Ja richtig so ne Schnittstelle will ich machen. Nur hab ich keine Ahnung / keinen Ansatz wie das gehen soll. Könnte mir da jemand mit ner guten Idee behilflich sein?
Wenn du die Fragen nicht beantwortest, die man dir stellt, dann kann man dir nicht behilflich sein.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

Ich dachte daran, die registerHandler funktion so zu gestalten, dass in einem array der funktionsname in der python datei gespeichert ist.

Code: Alles auswählen

import gHost 

def init(): 
    gHost.registerHandler( "myEvent", myFuncForMyEvent ) 

def myFuncForMyEvent(arg1, arg2, arg3, arg4): 
    # do stuff 
    return
also sollte jetzt in nem c++ array "myFuncForMyEvent" gespeichert sein. Wenn ich jetzt noch wüsste, wie genau das Modul heißt, woraus das gHost.registerHandler(...) aufgerufen worden ist, dann könnte ich bei jedem aufrufen von myEvent in C++ einfach die zugehörige Python funktion aufrufen. Nur dazu brüchte ich halt den Modulnamen. Ich möchte das auch nicht über einen extra parameter für registerHandler machen.
Aber ich glaube, da müsste es einfachere Möglichkeiten geben, die Python Funktion ordentlich in C++ zu registrieren.

EDIT: also nochmal konkret: Wenn ich den Modulnamen habe, mit dem die Funktion aufgerufen worden ist, dann dürfte es ohne weiters möglich sein, ein PyRun_SimpleString(modulename+".myFuncForMyEvent"); zu starten (dadurch würde dann die Python funktion aufgerufen). Jedoch müsste ich dazu den modulnamen rausbekommen.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Du kannst doch auch einfach die Funktion selbst speichern. Ich kenne mich mit Boost::Python nicht so aus, aber du kannst da sicherlich beliebige Python-Objekte speichern. Du übergibst das Funktionsobjekt ja schon an deine C++-Funktion, jetzt musst du es nur einfach speichern.
Krauzi
User
Beiträge: 77
Registriert: Montag 22. Oktober 2007, 18:06
Kontaktdaten:

Darii hat geschrieben: Ich kenne mich mit Boost::Python nicht so aus,
tja , ich auch nicht, sonst würde ich hier nicht posten :D.
Darii hat geschrieben:Du übergibst das Funktionsobjekt ja schon an deine C++-Funktion, jetzt musst du es nur einfach speichern.
Hm ich glaube du missverstehst micht da etwas: Das System mit C++ & Python - Kombo dient dem Zweck, ein Plugin System zu schaffen. Es bringt mir von dem her nicht viel, die Funktionsobjekte an sich zu speichern, da schon eher die ganze Funktion. Die Frage ist nur, wie ich das in Boost::Python mache. Ich dachte hier an Boost Gurus, die mir hier helfen können.
Antworten