Click here to Skip to main content
15,861,125 members
Articles / Programming Languages / C
Article

Embedding Python in C/C++: Part I

Rate me:
Please Sign up or sign in to vote.
4.83/5 (45 votes)
5 Oct 2005CPOL9 min read 584.3K   12K   159   60
This article describes how to embed Python modules in C/C++ applications by using Python/C API.

Introduction

Inspired by the article "Embedding Python in Multi-Threaded C/C++ Applications" (Linux Journal), I felt the need for a more comprehensive coverage on the topic of embedding Python. While writing this article, I had two objectives:

  1. This is written for programmers who are more experienced in C/C++ than in Python, the tutorial takes a practical approach and omits all theoretical discussions.
  2. Try to maintain Python's cross-platform compatibility when writing the embedding code.

Now, you have got some modules written in Python by others and you want to use them. You are experienced in C/C++, but fairly new to Python. You might wonder if you can find a tool to convert them to C code, like the conversion from FORTRAN. The answer is no. Some tools can help you generate the executable from a Python module. Is the problem solved? No. Converting code into executables usually makes things even more complicated, as you must figure out how your C/C++ application communicates with the executable "black-box".

I am going to introduce C/C++ programmers to Python/C API, a C library that helps to embed python modules into C/C++ applications. The API library provides a bunch of C routines to initialize the Python Interpreter, call into your Python modules and finish up the embedding. The library is built with Python and distributed with all the recent Python releases.

Part I of this article series discusses the basics of Python embedding. Part II will move on to more advanced topics. This tutorial does not teach the Python language systematically, but I will briefly describe how the Python code works when it comes up. The emphasis will be on how to integrate Python modules with your C/C++ applications. See article: "Embedding Python in C/C++: Part II".

In order to use the source code, you should install a recent Python release, Visual C++ (or GCC compiler on Linux). The environment that I have used to test is: Python 2.4 (Windows and Linux), Visual C++ 6.0 (Windows) or GCC 3.2 (RedHat 8.0 Linux). With Visual C++, select the Release configuration to build. Debug configuration requires Python debug library "python24_d.lib", which is not delivered with normal distributions.

Background

Python is a powerful interpreted language, like Java, Perl and PHP. It supports a long list of great features that any programmer would expect, two of my favorite features are "simple" and "portable". Along with the available tools and libraries, Python makes a good language for modeling and simulation developers. Best of all, it's free and the tools and libraries written for Python programmers are also free. For more details on the language, visit the official website.

Embedding basics: functions, classes and methods

First, let us start from a sample C program that calls a function within a Python module. Here is the source file "call_function.c":

// call_function.c - A sample of calling 
// python functions from C code
// 
#include <Python.h>

int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, *pFunc, *pValue;

    if (argc < 3) 
    {
        printf("Usage: exe_name python_source function_name\n");
        return 1;
    }

    // Initialize the Python Interpreter
    Py_Initialize();

    // Build the name object
    pName = PyString_FromString(argv[1]);

    // Load the module object
    pModule = PyImport_Import(pName);

    // pDict is a borrowed reference 
    pDict = PyModule_GetDict(pModule);

    // pFunc is also a borrowed reference 
    pFunc = PyDict_GetItemString(pDict, argv[2]);

    if (PyCallable_Check(pFunc)) 
    {
        PyObject_CallObject(pFunc, NULL);
    } else 
    {
        PyErr_Print();
    }

    // Clean up
    Py_DECREF(pModule);
    Py_DECREF(pName);

    // Finish the Python Interpreter
    Py_Finalize();

    return 0;
}

The Python source file "py_function.py" is as follows:

Python
'''py_function.py - Python source designed to '''
'''demonstrate the use of python embedding'''

def multiply():
    c = 12345*6789
    print 'The result of 12345 x 6789 :', c
    return c

Note that checks for the validity of objects are omitted for brevity. On Windows, simply compile the C source and get the executable, which we call "call_function.exe". To run it, enter the command line "call_function py_function multiply". The second argument is the name of the Python file (without extension), which when loaded becomes the module name. The third argument is the name of the Python function you are going to call within the module. The Python source does not take part in compiling or linking; it's only loaded and interpreted at run-time. The output from the execution is:

The result of 12345 x 6789 : 83810205

The C code itself is self-explanatory, except that:

  • Everything in Python is an object. pDict and pFunc are borrowed references so we don't need to Py_DECREF() them.
  • All the Py_XXX and PyXXX_XXX calls are Python/C API calls.
  • The code will compile and run on all the platforms that Python supports.

Now, we want to pass arguments to the Python function. We add a block to handle arguments to the call:

if (PyCallable_Check(pFunc)) 
{
    // Prepare the argument list for the call
    if( argc > 3 )
    {
            pArgs = PyTuple_New(argc - 3);
            for (i = 0; i < argc - 3; i++)
            {
            pValue = PyInt_FromLong(atoi(argv[i + 3]));
                    if (!pValue)
                    {
                PyErr_Print();
                         return 1;
                    }
                    PyTuple_SetItem(pArgs, i, pValue);    
            }
            
        pValue = PyObject_CallObject(pFunc, pArgs);

        if (pArgs != NULL)
        {
            Py_DECREF(pArgs);
        }
    } else
    {
        pValue = PyObject_CallObject(pFunc, NULL);
    }

    if (pValue != NULL) 
    {
        printf("Return of call : %d\n", PyInt_AsLong(pValue));
        Py_DECREF(pValue);
    }
    else 
    {
        PyErr_Print();
    }
    
    // some code omitted...
}

The new C source adds a block of "Prepare the argument list for the call" and a check of the returned value. It creates a tuple (list-like type) to store all the parameters for the call. You can run the command "call_function py_source multiply1 6 7" and get the output:

The result of 6 x 7 : 42
Return of call : 42

Writing classes in Python is easy. It is also easy to use a Python class in your C code. All you need to do is create an instance of the object and call its methods, just as you call normal functions. Here is an example:

// call_class.c - A sample of python embedding 
// (calling python classes from C code)
//
#include <Python.h>

int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, 
                  *pClass, *pInstance, *pValue;
    int i, arg[2];

    if (argc < 4) 
    {
        printf(
          "Usage: exe_name python_fileclass_name function_name\n");
        return 1;
    }

    // some code omitted...
   
    // Build the name of a callable class 
    pClass = PyDict_GetItemString(pDict, argv[2]);

    // Create an instance of the class
    if (PyCallable_Check(pClass))
    {
        pInstance = PyObject_CallObject(pClass, NULL); 
    }

    // Build the parameter list
    if( argc > 4 )
    {
        for (i = 0; i < argc - 4; i++)
            {
                    arg[i] = atoi(argv[i + 4]);
            }
        // Call a method of the class with two parameters
        pValue = PyObject_CallMethod(pInstance, 
                    argv[3], "(ii)", arg[0], arg[1]);
    } else
    {
        // Call a method of the class with no parameters
        pValue = PyObject_CallMethod(pInstance, argv[3], NULL);
    }
    if (pValue != NULL) 
    {
        printf("Return of call : %d\n", PyInt_AsLong(pValue));
        Py_DECREF(pValue);
    }
    else 
    {
        PyErr_Print();
    }
   
    // some code omitted...
}

The third parameter to PyObject_CallMethod(), "(ii)" is a format string, which indicates that the next arguments are two integers. Note that PyObject_CallMethod() takes C variables types as its arguments, not Python objects. This is different from the other calls we have seen so far. The Python source "py_class.py" is as follows:

Python
'''py_class.py - Python source designed to demonstrate''' 
'''the use of python embedding'''

class Multiply: 
    def __init__(self): 
            self.a = 6 
            self.b = 5 
    
    def multiply(self):
            c = self.a*self.b
    print 'The result of', self.a, 'x', self.b, ':', c
            return c
    
    def multiply2(self, a, b):
            c = a*b
    print 'The result of', a, 'x', b, ':', c
    return c

To run the application, you add a class name between the module and function names, which is "Multiply" in this case. The command line becomes "call_class py_class Multiply multiply" or "call_class py_class Multiply multiply2 9 9".

Multi-threaded Python embedding

With the above preparations, we are ready for some serious business. The Python module and your C/C++ application have to run simultaneously from time to time. This is not uncommon in simulation communities. For instance, the Python module to be embedded is part of a real-time simulation and you run it in parallel with the rest of your simulation. Meanwhile, it interacts with the rest at run-time. One conventional technique is multi-threading. There are a number of options for multi-threaded embedding. We are going to discuss two of them here.

In one approach you create a separate thread in C and call the Python module from the thread function. This is natural and correct, except that you need to protect the Python Interpreter state. Basically, we lock the Python Interpreter before you use it and release after the use so that Python can track its states for the different calling threads. Python provides the global lock for this purpose. Let us look at some source code first. Here is the complete content of "call_thread.c":

// call_thread.c - A sample of python embedding 
// (C thread calling python functions)
// 
#include <Python.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#ifdef WIN32    // Windows includes
#include <Windows.h>
#include <process.h>
#define sleep(x) Sleep(1000*x)
HANDLE handle;
#else    // POSIX includes
#include <pthread.h>
pthread_t mythread;
#endif

void ThreadProc(void*);

#define NUM_ARGUMENTS 5
typedef struct 
{
   int argc;
   char *argv[NUM_ARGUMENTS]; 
} CMD_LINE_STRUCT;

int main(int argc, char *argv[])
{
    int i;
    CMD_LINE_STRUCT cmd;
    pthread_t mythread;

    cmd.argc = argc;
    for( i = 0; i < NUM_ARGUMENTS; i++ )
    {
        cmd.argv[i] = argv[i];
    }

    if (argc < 3) 
    {
        fprintf(stderr,
          "Usage: call python_filename function_name [args]\n");
        return 1;
    }

    // Create a thread
#ifdef WIN32
    // Windows code
    handle = (HANDLE) _beginthread( ThreadProc,0,&cmd);
#else
    // POSIX code
    pthread_create( &mythread, NULL, 
                 ThreadProc, (void*)&cmd );
#endif

    // Random testing code
    for(i = 0; i < 10; i++)
    {
        printf("Printed from the main thread.\n");
    sleep(1);
    }

    printf("Main Thread waiting for My Thread to complete...\n");

    // Join and wait for the created thread to complete...
#ifdef WIN32
    // Windows code
    WaitForSingleObject(handle,INFINITE);
#else
    // POSIX code
    pthread_join(mythread, NULL);
#endif

    printf("Main thread finished gracefully.\n");

    return 0;
}

void ThreadProc( void *data )
{
    int i;
    PyObject *pName, *pModule, *pDict, 
               *pFunc, *pInstance, *pArgs, *pValue;
    PyThreadState *mainThreadState, *myThreadState, *tempState;
    PyInterpreterState *mainInterpreterState;
    
    CMD_LINE_STRUCT* arg = (CMD_LINE_STRUCT*)data;

    // Random testing code
    for(i = 0; i < 15; i++)
    {
        printf("...Printed from my thread.\n");
    sleep(1);
    }

    // Initialize python inerpreter
    Py_Initialize();
        
    // Initialize thread support
    PyEval_InitThreads();

    // Save a pointer to the main PyThreadState object
    mainThreadState = PyThreadState_Get();

    // Get a reference to the PyInterpreterState
    mainInterpreterState = mainThreadState->interp;

    // Create a thread state object for this thread
    myThreadState = PyThreadState_New(mainInterpreterState);
    
    // Release global lock
    PyEval_ReleaseLock();
    
    // Acquire global lock
    PyEval_AcquireLock();

    // Swap in my thread state
    tempState = PyThreadState_Swap(myThreadState);

    // Now execute some python code (call python functions)
    pName = PyString_FromString(arg->argv[1]);
    pModule = PyImport_Import(pName);

    // pDict and pFunc are borrowed references 
    pDict = PyModule_GetDict(pModule);
    pFunc = PyDict_GetItemString(pDict, arg->argv[2]);

    if (PyCallable_Check(pFunc)) 
    {
        pValue = PyObject_CallObject(pFunc, NULL);
    }
    else {
        PyErr_Print();
    }

    // Clean up
    Py_DECREF(pModule);
    Py_DECREF(pName);

    // Swap out the current thread
    PyThreadState_Swap(tempState);

    // Release global lock
    PyEval_ReleaseLock();
    
    // Clean up thread state
    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);

    Py_Finalize();
    printf("My thread is finishing...\n");

    // Exiting the thread
#ifdef WIN32
    // Windows code
    _endthread();
#else
    // POSIX code
    pthread_exit(NULL);
#endif
}

The thread function needs a bit of explanation. PyEval_InitThreads() initializes Python's thread support. PyThreadState_Swap(myThreadState) swaps in the state for the current thread, and PyThreadState_Swap(tempState) swaps it out. The Python Interpreter will save what happens between the two calls as the state data related to this thread. As a matter of fact, Python saves the data for each thread that is using the Interpreter so that the thread states are mutually exclusive. But it is your responsibility to create and maintain a state for each C thread. You may wonder why we didn't call the first PyEvel_AcquireLock(). Because PyEval_InitThreads() does so by default. In other cases, we do need to use PyEvel_AcquireLock() and PyEvel_ReleaseLock() in pairs.

Run "call_thread py_thread pythonFunc" and you can get the output as shown below. The file "py_thread.py" defines a function called pythonFunc() in which a similar random testing block prints "print from pythonFunc..." to screen fifteen times.

Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
...Printed from my thread.
Printed from the main thread.
Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
Printed from the main thread.
...Printed from my thread.
Main Thread waiting for My Thread to complete...
...Printed from my thread.
...Printed from my thread.
...Printed from my thread.
...Printed from my thread.
...Printed from my thread.
My thread is finishing...
Main thread finished gracefully.

Obviously, the implementation is getting complicated because writing multi-threading code in C/C++ is not trivial. Although the code is portable, it contains numerous patches, which require detailed knowledge of the system call interfaces for specific platforms. Fortunately, Python has done most of this for us, which brings up the second solution to our question under discussion, namely, letting Python handle the multi-threading. This time the Python code is enhanced to add a threading model:

Python
''' Demonstrate the use of python threading'''

import time
import threading

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        for i in range(15):
            print 'printed from MyThread...'
            time.sleep(1)

def createThread():
    print 'Create and run MyThread'
    background = MyThread()
    background.start()
    print  'Main thread continues to run in foreground.'
    for i in range(10):
        print 'printed from Main thread.'
        time.sleep(1)
    print  'Main thread joins MyThread and waits until it is done...'
    background.join() # Wait for the background task to finish
    print  'The program completed gracefully.'

The C code does not handle threading any more. All it needs to do is just call createThread(). Try to use the previous "call_function.c". You can run "call_function py_thread createThread" to see how the output looks like. In this case, the second solution is cleaner and simpler. More importantly, Python threading model is portable. While the C threading code for Unix and Windows is different, it remains the same in Python.

The Python function createThread() is not required if our C code calls the thread class' start() and joint() methods. The relevant changes are listed in the following (from the C source file "call_thread_2.c"):

// Create instance
pInstance = PyObject_CallObject(pClass, NULL); 

PyObject_CallMethod(pInstance, "start", NULL);

i = 0;
while(i<10)
{
printf("Printed from C thread...\n");

// !!!Important!!! C thread will not release CPU to 
// Python thread without the following call.
PyObject_CallMethod(pInstance, "join", "(f)", 0.001);        
Sleep(1000);
i++;
}

printf(
  "C thread join and wait for Python thread to complete...\n");
PyObject_CallMethod(pInstance, "join", NULL);        

printf("Program completed gracefully.\n");

Basically, after you create the class instance, call its start() method to create a new thread and execute its run() method. Note that without frequent short joins to the created thread, the created thread can only get executed at the beginning and the main thread will not release any CPU to it until it's done. You may try this out by commenting the joint call within the while loop. The behavior is somehow different from that of the previous case where we had called start() from within the Python module. This seems to be a feature of multi-threading which is not documented in the Python Library Reference.

Points of interest

I have deliberately paid attention to writing generic, portable C code for Python embedding. By encapsulating low-level system calls, Python supports platform portability and makes writing portable code easier. Most Python modules can be ported between Unix-like environment and Windows with little effort. We should keep this portability in mind when writing C/C++ wrapping code for Python modules. It may not be always straightforward to write portable C code yourself. Python has done a lot of hard work, as in the above case. Try to explore easier, simpler and cleaner solutions. Anyway, I stop here. How to write portable Python code goes beyond the scope of this tutorial. It could well make a good title for a new article.

While embedding is a good option for utilizing Python modules in C/C++ applications, there are other alternative approaches. On Windows, some tools (e.g. "py2exe") can convert Python modules into Windows executables directly. Then, you can spawn a process to run the executable from within the C/C++ application. One drawback is that you cannot call the module directly. Instead, your application has to interact with the module through certain types of IPC. This requires that the Python module under discussion be "IPC ready", meaning that it should have implemented an IPC interface to process incoming and outgoing data. Part II of this article will discuss embedding-related IPC techniques.

All the source code provided in this article is simple C code for demonstration purpose. In practice, I recommend to put the Python embedding code in C++ wrapper classes. This way, high-level application developers don't have to deal with the embedding details.

Conclusion

In this part, I have covered Python embedding from the basics such as calling functions, classes and methods, to not so basic topics like multi-threaded embedding. Python/C API provides a consistent calling interface to ease the task of integration between C/C++ and Python modules.

The discussion of multi-threaded embedding has raised a question for us: how does your C/C++ application communicate with the embedded Python module? The second part of this article shall address this issue from the IPC point of view.

History

  • This is the first revision of the article and the source code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect GuestLogix Inc.
Canada Canada
Jun is an experienced software architect. He wrote his first computer code on the tape machine for a "super computer". The tape machine reads holes on the black pape tape as source code. When manually fixing code, you need a punch and tranparent tape. To delete code, you block holes or cut off a segment and glue two ends together. To change code, you block old holes and punch new holes. You already know how to add new code, don't you? Anyway, that was his programming story in early 1980's.

Jun completed university with the specialty in oceanography, and graduate study in meteorology. He obtained his Ph.D. in physics. Jun has worked in a number of different areas. Since mid-90's, he has been working as a software professional in both military & commercial industries, including Visual Defence, Atlantis Systems International and Array Systems Computing.

Currently, Jun is an architect at GuestLogix, the global leader in providing onboard retail solutions for airlines and other travel industries. He is also the founder of Intribute Dynamics, a consulting firm specialized in software development. He has a personal blog site, although he is hardly able to keep it up to date.

In his spare time, Jun loves classic music, table tennis, and NBA games. During the summer, he enjoyes camping out to the north and fishing on wild lakes.

Comments and Discussions

 
QuestionCrash at the end Pin
Member 1257686814-Jun-16 5:40
Member 1257686814-Jun-16 5:40 
PraiseRe: Crash at the end Pin
kooookies19-Jul-17 3:27
kooookies19-Jul-17 3:27 
QuestionNo a very good example Pin
_James_17-Dec-15 13:00
_James_17-Dec-15 13:00 
Question[got Error] Help for Below this errors (Embedding Python in C/C++) Pin
ripjan21-Apr-15 10:09
ripjan21-Apr-15 10:09 
QuestionHow to deploy the vc++ project with python Pin
Gordon Zhang23-Nov-14 14:37
Gordon Zhang23-Nov-14 14:37 
QuestionHow do you know which references are borrowed? Pin
Member 1122476711-Nov-14 4:36
Member 1122476711-Nov-14 4:36 
Question" PyInt_FromLong " was not declared in this scope Pin
Aditya Ghantasala10-Oct-14 5:15
Aditya Ghantasala10-Oct-14 5:15 
GeneralMy vote of 5 Pin
leaboy20-Mar-13 0:28
leaboy20-Mar-13 0:28 
Questionfatal error LNK1104: cannot open file 'python27.lib' Pin
Qianfei Li8-Nov-12 10:20
Qianfei Li8-Nov-12 10:20 
QuestionHow to pass an array? Pin
jayden11827-Mar-12 21:52
jayden11827-Mar-12 21:52 
QuestionProblem passing tuple to the method of a class Pin
pasparis9-Jan-12 0:20
pasparis9-Jan-12 0:20 
GeneralVery useful Pin
Mast3rMind9-Nov-11 10:21
Mast3rMind9-Nov-11 10:21 
Questionusing Python.h in Windows Forms Pin
shaiu2211-Jul-11 2:47
shaiu2211-Jul-11 2:47 
Generalgreat job :) Pin
alejandro29A5-Oct-10 10:02
alejandro29A5-Oct-10 10:02 
GeneralMy vote of 1 Pin
owillebo19-May-10 4:25
owillebo19-May-10 4:25 
GeneralCan't be correcly compiled with VS2005 Pin
freeforvtk6-May-10 17:45
freeforvtk6-May-10 17:45 
GeneralRe: Can't be correcly compiled with VS2005 Pin
freeforvtk6-May-10 22:47
freeforvtk6-May-10 22:47 
GeneralRe: Can't be correcly compiled with VS2005 Pin
Member 1257686814-Jun-16 5:22
Member 1257686814-Jun-16 5:22 
GeneralFacing problem when using "import" in python file Pin
Manav seth2-Mar-10 12:37
Manav seth2-Mar-10 12:37 
Generalpython24_d.lib Pin
BetulSahin18-Feb-10 13:00
BetulSahin18-Feb-10 13:00 
GeneralRe: python24_d.lib Pin
alejandro29A5-Oct-10 9:59
alejandro29A5-Oct-10 9:59 
GeneralMy vote of 1 Pin
mrehsan6-Sep-09 23:43
mrehsan6-Sep-09 23:43 
QuestionProblem in importing Pin
scrat_amir31-Oct-08 0:48
scrat_amir31-Oct-08 0:48 
GeneralGood Article Pin
xinwuping10-Jul-08 9:52
xinwuping10-Jul-08 9:52 
QuestionHow do i check for version? Pin
gReaen21-May-08 7:04
gReaen21-May-08 7:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.