在C++中嵌入Python - 从C++程序运行Python

2 投票
3 回答
1854 浏览
提问于 2025-04-16 07:43

谢谢你们俩快速的回复 :)

我觉得我明白你们的建议了,但这并不是我真正需要的。老实说,我不知道该用什么正确的方法来实现我想要的功能。

我有一个程序,在运行过程中,有时需要调用Python来执行一些任务。我需要一个函数,可以调用Python并捕获Python的输出。

          pythonCallBackFunc(const char* pythonInput)

这个函数就是用来实现这个功能的。

举个例子:

     pythonCallBackFunc("5**2") needs to print on stdin(or some other file): 
     PythonResult: 25
     pythonCallBackFunc("print 5**2") needs to print on stdin(or some other file): 
     PythonResult: 25
     pythonCallBackFunc("'a'+'b'") needs to print on stdin(or some other file):
     PythonResult: 'ab' 
     pythonCallBackFunc("print 'a'+'b'") needs to print on stdin(or some other file):
     PythonResult: ab 
     pythonCallBackFunc("execfile('temp.py')")
     it should print nothing but is needs to run the temp.py script

     the next 2 calls need to print the value of result, meaning 4.
     pythonCallBackFunc("result = 4")
     pythonCallBackFunc("print result")

我遇到的问题是要捕获给定命令的所有Python输出(pythonInput)。我尝试的第一件事是使用这个脚本将Python的标准输出和错误输出重定向到一个文件:

    #stdout.py
    import sys
    saveout = sys.stdout                                     
    fsock = open('out.log', 'w')                           
    sys.stdout = fsock                                      

    #stdout_close.py
    sys.stdout = saveout                                    
    fsock.close()        

    #stdout_close.py
    fsock = open('error.log', 'w')         
    sys.stderr = fsock  

重定向之后,我使用了函数Py_run_SimpleString。

这还不错,但这个函数忽略了这些类型的命令:

    Py_run_SimpleString("'a'+'b'")

结果是空的……

Alex

3 个回答

0

你可以直接解析字符串,然后使用 PyRun_SimpleString。

5

你现在用的示例代码只会在你的Python代码返回一个可以用C++的类型表示的对象时才有效。这种情况是因为你代码中的这一行:

int boostOutputStr = extract<int>(result);

你需要面对的一个主要问题是,C++是一种严格类型的语言,而Python则不是。Python中的一条语句(或者一个函数调用)可以返回任何类型的对象,而且同一个函数的不同调用可以返回不同类型的对象。在C++中,函数的返回类型(以及参数的类型)必须是明确的。不过,C++提供了模板功能,可以让你在某些情况下更加灵活。通过定义一个模板函数(比如boost::python中的extract函数就是一个很好的例子),你可以用这个函数处理不同类型的配置。但是,编译器会为你使用的每一种类型实例化这个模板函数。也就是说,模板方法只是帮你省去了复制粘贴和修改代码的繁琐工作。编译器会为你完成这些工作。但所有类型必须在编译时就明确好。你不能让一个函数根据用户输入有时返回,有时返回。这就是像Python这样的脚本语言,或者其他高级语言的优势所在。在C++中,所有类型必须在代码编译时就确定。

那么在你的情况下该怎么办呢?

你可以让pythonCallBackFunc直接返回result,而不进行任何类型转换,这样返回值就是一个类型为object(来自boost::python)的对象,表示一个Python对象。你可以在后面的代码中用extract<>将其转换为你需要的任何类型。(我假设你确实想把Python表达式的返回值传回你的代码,而不是一直通过cout打印出来。)

或者,你也可以把你的函数pythonCallBackFunc做成一个模板函数。

    template<class ReturnType>
    void pythonCallBackFunc(string inputStr){   
      Py_Initialize();
      try   
         {
        str boostInputStr(inputStr.c_str());
        object result = eval(boostInputStr);

        return extract<ReturnType>(result);

       //Py_Finalize() #not calling because of the boost.python
          } 
          catch(error_already_set const &)
          {
       //do somthing
          }
     }

...在这种情况下,你需要用合适的类型来调用它,比如:

       pythonCallBackFunc<int>(inputStr);

希望这些对你有帮助。

0

首先,如果你想构建比简单的解释器更复杂的东西,确保你先阅读并理解svenor的回答。不过在这个特定的情况下,你可以把这一行

    int boostOutputStr = extract<int>(result);

替换成

    std::string boostOutputStr = extract<std::string>(str(result));

这样就能得到你想要的结果。这里的str(result)部分是把你在Python中计算出来的结果转换成字符串形式,这样如果你只是想把它输出到std::cout,这样做就足够了。

如果你决定使用带模板的pythonCallBackFunc(),有两点需要注意:

(1) 声明时返回类型应该是'ReturnType'而不是'void'

(2) 你应该在其他地方调用Py_Initialize()(比如程序或解释器启动时),或者用一个静态变量来保护它(也就是'静态 bool initialized = false; 如果 (!initialized) { Py_Initialize(); initialized = true; }')。

撰写回答