2015-08-20 51 views
1

我正在將Python嵌入到C++應用程序中。我想在主模塊中定義一個函數V,它接收一個字符串並返回一個類A的實例。當使用Boost.Python嵌入時,Python的__main__消失的原因

的問題是,A需要一些在課堂上嵌入的Python實例可用的數據,在本例中通過爲_env - 所以我想我可以使用高清(...)的方法來定義V,使用lambda捕獲所需的數據。

但是,當我這樣做時,我最終得到AttributeError: 'NoneType' object has no attribute '__dict__'當我嘗試獲取主模塊的字典。

沒有調用boost的def(...),我可以獲取並添加到主模塊。

有沒有什麼我做錯了,導致__main__去失蹤,併產生一個無我試圖訪問它?有關如何實現這一目標的任何建議?

void B::processRequest(Ptr<protocol::Message> msg, const std::function<void(const std::string &)> &send) {  
     try { 
     //make my_module module available to python 
     PyImport_AppendInittab("my_module", &initmy_module); 
     // 
     Py_Initialize(); 
     //get the main module 
     py::object main_module((py::handle<>(py::borrowed(PyImport_AddModule("__main__"))))); 
     py::object main_namespace = main_module.attr("__dict__"); 
     py::object my_moduleNamespace((py::handle<>(PyImport_ImportModule("my_module")))); 
     //add the module to the main namespace 
     main_namespace["my_module"] = my_moduleNamespace; 
//add attribute to namespace 
//  main_namespace["V"] = py::ptr(this); 
     auto AFn([this](std::string label) { return ptr<A>(_env, label); }); 
     typedef boost::mpl::vector<Ptr<A>, std::string> AFnSig; 
     const auto policies = py::default_call_policies(); 
     py::def("V", boost::python::make_function(AFn, policies, AFnSig())); 

     py::handle<> ignored((PyRun_String((*msg->action).c_str(), Py_file_input, main_namespace.ptr(), main_namespace.ptr()))); 
     } catch (py::error_already_set) { 
     PyErr_Print(); 
     } 
     Py_Finalize(); 
    } 

我能想到的解決這個問題的唯一的事情就是讓B調用定義operator(std::stirng)但是,這並不工作,因爲我有需要_env其他兩個功能,其中一個爲相同的簽名V所以根據我所見,無法區分這些電話。

編輯: 改變標題,試圖使它更清楚我所指的。

回答

2

問題不是__main__正在消失;問題是V方法從未在__main__的範圍內定義。的python::def()功能僅運行在當前python::scope

def()是其可用於以暴露C++函數和調用的對象作爲Python函數在當前scope的功能。

爲了解決這個問題,人們可以直接分配一個Python調用對象到Python名稱空間:

namespace python = boost::python; 
python::object main = python::import("__main__"); 
python::object main_namespace = main.attr("__dict__"); 
main_namespace["V"] = python::make_function(...); 

或設置當前範圍__main__,然後經由def()露出的Python可調用對象:

namespace python = boost::python; 
python::object main = python::import("__main__"); 
python::object main_namespace = main.attr("__dict__"); 
python::scope main_scope(main); 
python::def("V", python::make_function(...)); // defined in __main__ 

下面是一個完整的例子demonstrating此:

#include <boost/python.hpp> 

int main() 
{ 
    namespace python = boost::python; 
    try 
    { 
    Py_Initialize(); // Start interpreter. 

    // Create the __main__ module. 
    python::object main = python::import("__main__"); 
    python::object main_namespace = main.attr("__dict__"); 

    // >>> def func1(): return 100 
    main_namespace["func1"] = python::make_function(
     []() { return 100; }, 
     python::default_call_policies(), 
     boost::mpl::vector<int>()); 

    // >>> def func2(): return 100 
    { 
     // Set __main__.__dict__ as current scope. 
     python::scope main_scope(main); 

     // def will define in current scope. 
     python::def("func2", python::make_function(
     []() { return 200; }, 
     python::default_call_policies(), 
     boost::mpl::vector<int>()) 
    ); 
    } 

    // Execute func1 and func2 via a python string. 
    python::exec(
     "print dir()\n"  
     "assert(100 == func1())\n" 
     "assert(200 == func2())\n" 
     , main_namespace 
    ); 
    } 
    catch (const python::error_already_set&) 
    { 
    PyErr_Print(); 
    } 
} 

輸出:

['__builtins__', '__doc__', '__name__', '__package__', 'func1', 'func2'] 

此外,您可能需要更改解釋的壽命或不使用Boost.Python的。該documentation警告Py_Finalize應該不會被調用:

注意,在這個時候,你不能調用Py_Finalize()停止解釋。

0

似乎無法找到一種方法來處理bp。所以我想出了在命名空間中使用普通的python定義我想要的函數,並註冊了python函數使用的環境對象。因此,我可以創建一個圍繞C++ API的python包裝器。

BOOST_PYTHON_MODULE (my_module) { 
    //env is now exposed to the user but can't be constructed and none of its methods are exposed so it's not so bad. Just the symbol exists. 
    py::class_<Ptr<Environment>>("Environment", py::no_init); 
    py::class_<A>("A", py::init<const Ptr<Environment>, std::string>()); 
} 

void B::processRequest(Ptr<protocol::Message> msg, const std::function<void(const std::string &)> &send) {  
     try { 
     //make my_module module available to python 
     PyImport_AppendInittab("my_module", &initmy_module); 
     // 
     Py_Initialize(); 
     //get the main module 
     py::object main_module((py::handle<>(py::borrowed(PyImport_AddModule("__main__"))))); 
     py::object main_namespace = main_module.attr("__dict__"); 
     py::object my_moduleNamespace((py::handle<>(PyImport_ImportModule("my_module")))); 
     //add the module to the main namespace 
     main_namespace["my_module"] = my_moduleNamespace; 


     my_moduleNamespace.attr("environment") = _env; 
     //HERE - provide the API I want using a native Python function 
     py::handle<> envRun((PyRun_String("def V(label):\n return my_module.A(my_module.environment,label)", Py_file_input, main_namespace.ptr(), main_namespace.ptr()))); 

     py::handle<> ignored((PyRun_String((*msg->action).c_str(), Py_file_input, main_namespace.ptr(), main_namespace.ptr()))); 
     } catch (py::error_already_set) { 
     PyErr_Print(); 
     } 
     Py_Finalize(); 
    }