c 只获取程序名_在C/C++中嵌入Python

d4b3233da3d146b806ffe2bf54657037.png

背景

Python是一种强大的解释语言,如Java、Perl和PHP。它支持一长串伟大的特性,这是任何程序员都会期望的,我最喜欢的两个特性是“简单”和“可移植”。与可用的工具和库一起,Python为建模和仿真开发人员提供了一种很好的语言。最重要的是,它是免费的,为Python程序员编写的工具和库也是免费的。有关该语言的更多细节,请访问官方网站。网站.

嵌入基础:函数、类和方法

首先,让我们从一个在Python模块中调用函数的样例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_namen");
        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;
}

Python源文件“Py函数.py“如下:

'''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

注意,为了简洁起见,省略了对对象有效性的检查。在Windows上,只需编译C源代码并获取可执行文件,我们称之为“调用函数.exe“.要运行它,请输入命令行”call_function py_function multiply“.第二个参数是Python文件的名称(没有扩展名),加载后该文件成为模块名。第三个参数是您要在模块内调用的Python函数的名称。Python源不参与编译或链接;它只在运行时加载和解释。执行的输出如下:

The result of 12345 x 6789 : 83810205

C代码本身是不言自明的,只是:

  • Python中的所有东西都是一个对象。pDictpFunc是借来的推荐信所以我们不需要Py_DECREF()他们。
  • 所有Py_XXX和PyXXX_XXX调用都是Python/CAPI调用。
  • 代码将在Python支持的所有平台上编译和运行。

现在,我们希望将参数传递给Python函数。我们添加一个块来处理调用中的参数:

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 : %dn", PyInt_AsLong(pValue));
        Py_DECREF(pValue);
    }
    else 
    {
        PyErr_Print();
    }
    
    // some code omitted...
}

新的C源添加了一个“为调用准备参数列表”的块,并对返回的值进行了检查。它创建一个元组(类似列表的类型)来存储调用的所有参数。您可以运行命令“call_function py_source multiply1 6 7“并得到输出:

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

用Python编写类很容易。在C代码中使用Python类也很容易。您所需要做的就是创建对象的实例并调用其方法,就像调用普通函数一样。以下是一个例子:

// 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_namen");
        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 : %dn", PyInt_AsLong(pValue));
        Py_DECREF(pValue);
    }
    else 
    {
        PyErr_Print();
    }
   
    // some code omitted...
}

的第三个参数PyObject_CallMethod(), "(ii)"格式字符串,它指示下一个参数是两个整数。请注意PyObject_CallMethod()将C变量类型作为其参数,而不是Python对象。这与我们到目前为止看到的其他电话不同。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

要运行应用程序,在模块名和函数名之间添加一个类名,即“Multiply在这种情况下,命令行变成call_class py_class Multiply multiply“或”call_class py_class Multiply multiply2 9 9".

多线程Python嵌入

有了上述准备,我们准备好了一些严肃的事情。Python模块和C/C++应用程序必须不时同时运行。这在模拟社区中并不少见。例如,要嵌入的Python模块是实时仿真的一部分,您可以与仿真的其余部分并行运行它。同时,它在运行时与其他部分进行交互。一种传统的技术是多线程.多线程嵌入有许多选项。我们将在这里讨论其中的两个。

在一种方法中,您可以在C中创建一个单独的线程,并从线程函数调用Python模块。这是自然的,也是正确的,只是您需要保护Python解释器状态。基本上,我们在使用Python解释器之前锁定它,并在使用之后发布它,以便Python能够跟踪不同调用线程的状态。Python为此提供了全局锁。让我们先看看一些源代码。以下是“调用线程.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
}

线程函数需要一些解释。PyEval_InitThreads()初始化Python的线程支持。PyThreadState_Swap(myThreadState)状态中的当前线程的交换,以及PyThreadState_Swap(tempState)换掉它。Python解释器将将两个调用之间发生的情况保存为与此线程相关的状态数据。实际上,Python为使用解释器的每个线程保存数据,这样线程状态是相互排斥的。但是为每个C线程创建和维护一个状态是您的责任。你可能想知道为什么我们不打电话给第一个PyEvel_AcquireLock()。因为PyEval_InitThreads()默认情况下这样做。在其他情况下,我们确实需要使用PyEvel_AcquireLock()PyEvel_ReleaseLock()成对。

跑“call_thread py_thread pythonFunc“并且您可以获得如下所示的输出。Py螺纹.py“定义一个名为pythonFunc()其中一个类似的随机测试块打印“pythonFunc的打印.”放映十五遍。

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.

显然,实现变得越来越复杂,因为用C/C++编写多线程代码并不简单。虽然代码是可移植的,但它包含了许多补丁,需要详细了解特定平台的系统调用接口。幸运的是,Python已经为我们做了大部分工作,这就引出了我们正在讨论的问题的第二个解决方案,即让Python处理多线程。这一次增强了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.'

C代码不再处理线程。它所需要做的就是打电话createThread()。尝试使用前面的“调用函数“.你可以跑”call_function py_thread createThread“要了解输出的样子。在这种情况下,第二种解决方案更简洁。更重要的是,Python线程模型是可移植的。虽然Unix和Windows的C线程代码不同,但Python中的C线程代码保持不变。

Python函数createThread()如果我们的C代码调用线程类‘start()joint()方法。相关更改列在以下内容中(来自C源文件)调用线程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");

基本上,在创建类实例之后,调用其start()方法创建新线程并执行其run()方法。注意,如果创建的线程没有频繁的短连接,则创建的线程只能在开始时执行,主线程在完成之前不会将任何CPU释放给它。您可以通过在while循环。这种行为在某种程度上不同于上一种情况,在这种情况下,我们调用了start()在Python模块中。这似乎是多线程的一个特性,在Python库参考.

兴趣点

我有意地注意编写用于Python嵌入的通用的、可移植的C代码。通过封装低级别的系统调用,Python支持平台可移植性,并使编写可移植代码更容易。大多数Python模块可以在类似Unix的环境和Windows之间移植,只需很少的努力。在为Python模块编写C/C++包装代码时,我们应该记住这种可移植性。自己编写可移植的C代码可能并不总是那么简单。Python做了很多艰苦的工作,就像上面的例子一样。尝试探索更简单、更简单、更干净的解决方案。不管怎样,我到此为止。如何编写可移植的Python代码超出了本教程的范围。它很可能成为一篇新文章的好标题。

虽然嵌入是在C/C++应用程序中使用Python模块的一个很好的选择,但还有其他替代方法。在Windows上,一些工具(例如“py2exe”)可以直接将Python模块转换为Windows可执行文件。然后,您可以生成一个进程,以便在C/C++应用程序中运行可执行文件。一个缺点是不能直接调用模块。相反,您的应用程序必须通过某些类型的IPC与模块进行交互。这要求正在讨论的Python模块是“IPC就绪”,这意味着它应该实现一个IPC接口来处理传入和传出数据。本文的第二部分将讨论与嵌入相关的IPC技术。

本文提供的所有源代码都是用于演示的简单C代码。实际上,我建议将Python嵌入代码放在C++包装类中。这样,高级应用程序开发人员就不必处理嵌入细节了。

结语

在这一部分中,我介绍了Python嵌入,从调用函数、类和方法等基本知识,到不像多线程嵌入这样的基本主题。Python/CAPI提供了一个一致的调用接口,以简化C/C++和Python模块之间的集成任务。

关于多线程嵌入的讨论为我们提出了一个问题:您的C/C++应用程序如何与嵌入式Python模块通信?本文的第二部分将从IPC的角度来解决这个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值