SCRIPTING II & III

(LAB 21 AND [OPTIONAL] LAB 22)

References: • https://docs.python.org/3.5/c-api/ • https://docs.python.org/3.5/extending/index.html#extending-index

COMPARISON OF PART I & II

• Part I (Lab19): Python extending • Make a new dll (pyd) file from code which is callable by the python interpreter (IDLE, PyCharm, etc.) EXTENDING

C/C++ extension (a .pyd file). Can contain: IDLE (or other python • Python interfaces to C interpreter) "classes" • Python interfaces to C functions • Python interfaces to C variables

Python code (.py) COMPARISON OF PART I & II, CONT.

• Part II (Lab21): Python embedding • We'll create a light-weight python interpreter inside our application • No IDLE, PyScripter, PyCharm this time. • We'll have two forms of communication between C/Python: • python functions can call C++ utility functions [Lab21] • LogMessage • createGameObject • … • C++ code will call python functions upon "events" [Lab22] • Two objects colliding • A sound ending • An update function • …

EMBEDDING THE INTERPRETER

Python Interpreter

Python (.py) files C/C++ Program (.exe)

Other C/C++ objects

Python code (strings, read from files) GENERAL APPROACH

• Create a “ssuge” module (just as before) • Includes any built-in classes and functions • logMessage function • getAxis function • A few other "top-level" functions • Append it to the built-in modules • Create the interpreter (one line!) • Load .py file(s) and execute them • … • Shutdown the interpreter on C++ application shutdown. C FUNCTION POINTERS

// I should've mentioned this in the last lab…hopefully you've // seen it in Operating Systems I float foo(int x, char * y) { } float foo2(int p, char * q) { } int main() { float (*func_ptr)(int x, char* y); // func.ptr. Var. float f;

func_ptr = foo; f = func_ptr(7, "test");

func_ptr = foo2; F = func_ptr(99, “blah”); }

// This is how the PyMethodDef (and related) structures work. CREATING THE EMBEDDED INTERPRETER • "Import" the C module into the embedded interpreter: PyImport_AppendInittab(char * modName, PyObject * (*funcPtr)(void)); Change the return type of your PyInit_??? Method to PyObject* • Second, start up the interpreter Py_Initialize() • Note: this interpreter will (only) have modules defined with PyImport_AppendInittab. • To add other modules…[design decision] • [lightweight]: only light-weight built-in python functionality • [heavy-weight]: put all python .pyd files in your $bin/$(Config)/ directory. • Third, run the rest of the program • At appropriate times, load and run .py files into the embedded interpreter (more on this shortly) • Finally, shut down the interpreter Py_Finalize()

LOADING / EXECUTING A .PY FILE

• Load the file into a char * buffer • FILE* or fstream and new / delete • Make sure it's null-terminated (ask if not sure about this) • Compile the buffer into a code module PyObject * Py_CompileString(char * buff, char * fname, Py_file_input); • Import and Execute the code module PyImport_ExecCodeModule(char * name, PyObject * codeMod); • This will run the python code. • If that code references the C functions / classes with Python interfaces, that code will now be run. • This will usually interact with the rest of the C structures in the program C EXTERN’S

• Normally we put all “stuff” we want to share in a .h • However… • …in the C/Python API a lot of “stuff” is structures. • If we want to share it in multiple places, we need to tell the linker to expect it. • A little like a forward class declaration. • File A ... Static PyTypeObject foo = { ... }; • File B extern PyTypeObject foo; … // Use foo as normal LOOK AT LAB21

• [Do it] PART III : "GAME-OBJECT" WRAPPER

• This is the (slightly harder) [optional] bonus lab. • In this section, we'll add a wrapper around GameObject • Similar structure to what we did with "testClass" in Lab19. • Will have an associated (C++) GameObject pointer • Will need to work with the GameObjectManager. CREATING PYTHON OBJECTS IN C

• “Call” the class (e.g. we have “PyTypeObject” struct called FooType and a C struct called Foo) Foo * f = (Foo*)PyObject_CallObject((PyObject*)&FooType, args_tuple)

C CAPSULES

• When making our python “wrapper” around C++ GameObjects, we need to connect them • Them = the python game object and the C++ counter-part. • A C-capsule is a PyObject that just contains a void* • It’s not usable / visible in python. • Useful to store upon creation and use later. PyObject* c = PyCapsule_New(void*, char*name, NULL);

Void* PyCapsule_GetPointer(PyObject*, char*name); bool PyCapsue_CheckExact(PyObject *);

SUGGESTED CODE STRUCTURE

• script_top_level.h • Has prototypes (only) for top-level functions • script_top_level.cpp • Has bodies for the functions (minus the static keyword – why?) • script_game_object.h • Contains the C __script_GameObject structure (C “class”) • Script_game_object.cpp • Has the (python) __script_GameObject Type • ScriptManager.h / .cpp • Has extern references to the __script_GameObjectType. • Contains the PyMethodDef array for the top-level functions • I got errors when I did this in ScriptTopLevelFunctions.cpp • Has a PyInit_ssuge function (static is OK) • A C++ singleton • A method to load and execute a .py file. • A method to generate an error string (I’ll give you this code -- soon)