Embedding

OvermindDL1

25-01-2006 01:08:19

Greetings, I've just barely started integrating Python into my application to serve as an object control point (think Unreal script) which I will probobly eventually port certian functions into C++ if I see a speed need. Currently I am using boost and it is working wonderfully (sans the quadrupled build times on those files). Would PyOgre allow this to go along any easier, I will not want it to control the game per-say, but rather the application will make calls into python classes that subclass from my C++ classes to perform functions, nothing special overall except for the python subclassing from C++ and vice-versa. Never used swig, always used Boost for binding thus far. So how would this be useful to use instead?

griminventions

25-01-2006 01:59:18

PyOgre is an extension, not an embedding. So, as far as I know, you wouldn't be able to use it inside a C++ application the way UnrealScript operates.

OvermindDL1

25-01-2006 04:47:02

I just recall a forum post from a while back that someone used PyOgre to embed python instead of extend, wondering how feasible that was. I should continue along the boost route for what I intend to do though?

Istari

25-01-2006 07:51:47

If you embed the .cxx and .h files in your project you only need to call init_ogre() to start the _ogre module (after you call Py_Initialize()). Nothing difficult about it. Then you need the ogre.py file in your path.
PyOgre uses SWIG Directors so you are able to subclass C++ classes with virtual functions, and the correct Python functions are called.

Kanma

25-01-2006 08:35:11

I embedded Python in my framework and can use pyOgre and any other Python module (including my framework bindings) without any modification.

As long as your modules are somewhere on your sys.path, you can import them as usual.

In fact, I can now start my game from a Python script or a C++ program.

griminventions

25-01-2006 13:44:38

Cool. I didn't know it was that easy to switch between embedding and extending. :)

rman77

26-01-2006 02:57:40

If you look at the "embedded" files under "demos" its shows that calling python scripts from c++ and vise versa is possable. However, this doesn't apear to be well documented and I'm wondering if you can use pyorge to wrap custom classes as well (not just the ogre lib). Can anyone clarify this please? :?

~Rman

Istari

26-01-2006 15:25:08

@rman77
You don't use PyOgre to wrap your classes, for that you would use SWIG or Boost::Python.
I have experimented with SWIG and it's not hard to call python code. You just use the standard Python API.
// theSource.cpp
void callPyFuncWithString(PyObject* theFunc)
{
if (PyCallable_Check(theFunc))
{
Py_INCREF(theFunc);
PyObject* argTuple = Py_BuildValue("(s)", "Hey Joe!");
PyObject* result = PyObject_CallObject(theFunc, argTuple);
Py_XDECREF(theFunc);
Py_XDECREF(argTuple);
Py_XDECREF(result);
}
}

// theInterface.i
%module tester

void callPyFuncWithString(PyObject* theFunc)

# in Python
def someFunc(myStr):
print myStr

tester.callPyFuncWithString(someFunc)

rman77

26-01-2006 19:21:37

Ok, so I understand that you can run python scripts through the API and that you can wrap your c++ lib's with python to access them from python... But can the c++ and python code interact? Let me explain myself:

My program is an emualtor for the lego rcx to test various programs for a robotics competiton. The idea is to create a virtual world that the robot can function in to test various versions of the program against itself. The idea is to embed python into the c++ application to run the robots "scripts". As the python script runs its reacts to changes in variables from c++ and in turn calls various c++ functions....
example:
RCX.exe
-Loads Ogre/Phisics ect.
-Loads and runs Some_Script.py


//Python code (the robots program)
while light>50 (where "light" is a variable provided by c++)
do this_function (where "this_function" is a C++ function)
more python code....


If that made any sence... I know angelscript can do this but I would rather use python (its that whole loyalty thing :wink: )

~Rman

OvermindDL1

29-01-2006 23:27:44

That definitally opens more options, thank you. I will then compile PyOgre out of my ogreaddons directory and take a closer look.

What the post above me is doing is similer to what I will be doing, except to handle events from C++ code.

As for SWIG, never touched it. I have used Boost's python wrapper since it was first introduced into Boost and I have heard that swig tends to be far lower level due to many things needing to be supported. Just from reading that code up above, it still uses PyObject's directly. At the very least you should get Boost and use the Boost::Python::handle class to wrap them. Not a fancy class, but it does handle referencing counting and there is a helper class that works with handles to auto-convert Python values to C++ natives and vice-versa. For note, that barely scratches the top of Boost::Python. Although there is one things swig supports that I was wanting to use long ago that Boost did not (does it now?), can't remember what it was now...

As for lack of documention (if you think your embedding documentation is slim), Boost::Python has plenty, except in the area of embedding, had to figure that out myself, not terribly fun. :)

persoontje

19-02-2007 14:45:55

I'm looking for a similiar solution too. I've searched through the documentation of boost.python, but I can't find how to do it. If you wrap your classes boost creates a dynamic libary for you that python can loads, but from the c++ code I want to call python code, which interacts with the c++ code. (they are in the same executable) So the libary for python and the exectuable that runs python scripts are in the same execuatble.
It's actually the same what you do with lua/squirrel/...., but I like the more advanced features of python.

The documentation explains how to make your own libary for python, and how to call python code from c++, but I didn't found how to do this.

Do you have any ideas how to do is?

Game_Ender

19-02-2007 15:37:02

Its pretty simple. You have 4 parts. You main C++ excutable, your C++ support library, you Boost.Python wrapper library, and your python scripts. You C++ excutable and the Boost.Python wrapper library both link to the support library. Your C++ excutables uses the python API to call the python scripts.

persoontje

19-02-2007 16:06:29

Its pretty simple. You have 4 parts. You main C++ excutable, your C++ support library, you Boost.Python wrapper library, and your python scripts. You C++ excutable and the Boost.Python wrapper library both link to the support library. Your C++ excutables uses the python API to call the python scripts.

So I put all code that it used in python and c++ in a support library? And where do I store and create central stuff like a GameManager(which stores the scenemanager, Ogre::Root and other stuff)

Isn't it possible to do it without putting it in a support libary, like you can do with lua/squirrel/..... ? I don't like using libaries :oops:

Game_Ender

19-02-2007 22:24:58

You need a seperate library to be able to have a script that goes (ie with the import statement):

import MyGameModule

def action(event):
MyGameModule.kill_character(event.subject)


The other way to do things (as is done in some Boost.Python examples) is just compile everything into your executable. Then when you run the python script you just manually place the objects into python scripts local/global dict.

persoontje

20-02-2007 09:14:31

Thanks for you answer. Is there any difference in speed if you just build in everything in your exe, or if you make a libary of it?

OvermindDL1

20-02-2007 16:24:59

No speed difference, creates a function pointer either way. I always have my main game classes built into the exe and any python script can, for example, "import BZ2" even though BZ2 does not exist externally in any form. Very simple really; if you want to go that route then I have a great deal of experience that I could assist with.

Game_Ender

20-02-2007 16:37:03

How do you make the import statement aware of the module embedded in your executable? Is this done automatically by Boost.Python or Python?

Istari

20-02-2007 18:38:08

If you create a module named spam it will need to export something similar to this:
PyMODINIT_FUNC
initspam(void)
{
(void) Py_InitModule("spam", SpamMethods);
}


This is the function that Python will call when importing the dll/so file. It will always have the same name as the dll/so, plus an init at the start.
spam.dll => initspam
_eggs.dll => init_eggs

Boost::Python will create it for you. If you then compile the source for this module into your executable
(and link to all required libs) , all you need to do is call initspam() sometime after calling Py_Initialize()

http://docs.python.org/ext/methodTable.html

OvermindDL1

20-02-2007 19:21:58

There are two ways, and both work perfectly and identically if your engine is an exe. My engine is a dll and this thus restricts me to one method since I have my engine able to be imported as a python module, or able to be linked and loaded (thus init'ing python internally) by an exe.

First of all, if you intend to have it be an exe, or a dll that only acts as a linkable dll and not as a python module itself, then first define "BOOST_PYTHON_STATIC_MODULE" before you include boost::python, this makes it so that BOOST_PYTHON_MODULE just defines a local function and not an exported function. So as such:
BOOST_PYTHON_STATIC_MODULE(MyEmbeddedModule)
{ /* do import stuff */ }

// Is equiv to:
void initMyEmbeddedModule(void)
{ /* do import stuff */ }


Or if you are making a dll and want to export the dll as both a python module, or have it be linkable to an exe, then leave the above define undefined.

Either way, this next way will work for both cases (and you have to do this for the second). Just define an export function directly:

void initMyEmbeddedModule(void)
{ /* do import stuff */ }


Or if you are wanting to export as well, without defining the above define:

void _initMyEmbeddedModule(void) // Added an underscore for ease of use since you can't define the same function twice, and the BOOST_PYTHON_STATIC_MODULE uses the name as both the function name and the module name.
{ /* do import stuff */ }

BOOST_PYTHON_STATIC_MODULE(MyEmbeddedModule)
{ initMyEmbeddedModule();


And the main part, how to import a local module into the global interpreter:
PyImport_AppendInittab("ModuleNameInPython", initMyEmbeddedModule);
If it returns a -1 then an error occurred, check the python exception for details. But that would allow any script that is loaded by the local interpreter to call "import ModuleNameInPython". Do note, call PyImport_AppendInittab *BEFORE* you init the interpreter, it 'usually' works if you call it after, but it is designed and is supposed to be called before. Also, don't forget to start the interpreter using Py_InitializeEx and not Py_Initialize, and make sure you call Py_InitializeEx with the first param as zero, reason being is that if you don't call the Ex version, or you call the Ex version with !0, then the normal keyboard hooks would be installed, as such, pressing ctrl+c and such would be bad in most projects. :)