我正在使用pybind11嵌入python的c ++应用程序,尝试从类方法中调用嵌入式函数时遇到了一个问题。
从这里开始是我的绑定:
#ifdef _DEBUG
#undef _DEBUG
#include <python.h>
#define _DEBUG
#else
#include <python.h>
#endif
#include <embed.h>
namespace py = pybind11;
using namespace py::literals;
void DebugInfo(std::string string_)
{
String LogMessage_(string_.c_str());
LOGINFO(LogMessage_);
}
PYBIND11_EMBEDDED_MODULE(Test, m) {
m.def("DebugInfo", &DebugInfo, "Posts message to DEBUGINFO");
}
然后我可以拥有一个.py文件,其中包含:
import Test
test.DebugInfo("I'm a lumberjack and that's OK")
它可以很好地进行调试
当我尝试从类方法中调用它时,麻烦就开始了。
import Test
class PyTest(object):
def __init__(self):
test.DebugInfo("I'm a lumberjack and that's OK")
test = PyTest()
运行此命令时,它会针对cast.h引发异常,尤其是针对此函数一部分的1985行:
template <return_value_policy policy>
class unpacking_collector {
public:
template <typename... Ts>
explicit unpacking_collector(Ts &&...values) {
// Tuples aren't (easily) resizable so a list is needed for collection,
// but the actual function call strictly requires a tuple.
auto args_list = list();
int _[] = { 0, (process(args_list, std::forward<Ts>(values)), 0)... };
ignore_unused(_);
m_args = std::move(args_list);
}
const tuple &args() const & { return m_args; }
const dict &kwargs() const & { return m_kwargs; }
tuple args() && { return std::move(m_args); }
dict kwargs() && { return std::move(m_kwargs); }
/// Call a Python function and pass the collected arguments
object call(PyObject *ptr) const {
PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr());
if (!result)
throw error_already_set(); //EXCEPTION THROWS HERE!
return reinterpret_steal<object>(result);
}
因为这可能很重要,所以这就是我从主应用程序中调用整个事情的方式
//Start the Python Interpreter
py::scoped_interpreter guard{};
//Python variables
py::object thing_;
std::string test_py = Import_File("test.py");
auto locals = py::dict();
py::exec(test_py, py::globals(), locals);
thing_ = locals["test"].cast<py::object>();
thing_.attr("start")();
以及test.py的内容
import Test
class PyTest(object):
def __init__(self, message = "Test Object initialized"):
self.message = message
iterstr = str(self.iter)
message = self.message + iterstr
self.iter = 0
Test.DebugInfo(message)
def start(self):
self.message = "Starting Python Object"
self.iter = self.iter + 1
iterstr = str(self.iter)
message = self.message + iterstr
Test.DebugInfo(message)
def update(self):
self.message = "Python Object Update Cycle:"
self.iter = self.iter + 1
iterstr = str(self.iter)
message = self.message + iterstr
Test.DebugInfo(message)
test = PyTest()
我不确定是否遇到了pybind11的局限性,即其中的错误,还是只是把整个事情搞砸了。
任何见识将不胜感激。
答案 0 :(得分:2)
这也是pybind11
的问题,在这里:https://github.com/pybind/pybind11/issues/1452
我同时遇到了这个问题和问题,但是我发现了。将其复制到这里,供以后偶然遇到此问题的任何人
基本上,您实际上不需要py::dict
的空白locals
;这会引起各种各样的问题。如果您希望嵌入来自文档或测试的示例代码,则locals
值始终会复制全局范围。
请参阅:
* https://pybind11.readthedocs.io/en/stable/advanced/embedding.html
* https://github.com/pybind/pybind11/blob/master/tests/test_embed/test_interpreter.cpp#L57
您的选择是复制全局范围,或者在这种情况下,只需不传递locals
py::scoped_interpreter guard{};
auto globals = py::globals();
py::exec(test_py, globals);
thing_ = globals["Object"].cast<py::object>();
thing_.attr("start")();
对于顶级代码(不在任何模块内部),globals
变量看起来像是在此范围内保存值。
答案 1 :(得分:0)
因此,经过一些实验,我发现问题是由pybind无法检测到功能范围之外的导入模块引起的。
import foo
def bar():
foo.func()
将始终导致错误。但是,
def bar():
import foo
foo.func()
将按预期运行。