当前位置: 首页 > 知识库问答 >
问题:

SWIG和Boost::variant

汪翰墨
2023-03-14

我正在尝试使用SWIG将c项目包装到pythonapi中,遇到了以下格式的代码问题。

class A
{
//constructors and such.
};

class B
{
//constructors and such.
};

class C
{
//constructors and such.
};

typedef boost::variant<A,B,C> VariantType;
typedef std::vector<boost::variant<A,B,C>> VariantTypeList;

A、B类

%template(VariantType) boost::variant<A,B,C>;
%template(VariantTypeList) std::vector<boost::variant<A,B,C>>;

我得到一个错误,上面写着

Boost\x64\include\booost\Variant\variant.hpp(148): error:输入(3)中的语法错误。

所以我去看看这个错误和它的一行,其中有一个宏是在另一个头文件中定义的,特别是“boost/mpl/aux\uvalue\u wknd.hpp”,所以我用%include将它添加到接口文件中,现在看起来是SWIG。exe崩溃,并出现一个错误

访问违规

长话短说,有没有办法包装boost::variant模板类型?不幸的是,这个模板定义被嵌入了我们库的核心,我现在无法更改它。如果有关系的话,我也在MSVC 2013编译器上。

如果无法直接包装模板类型,是否可以解决此问题?我正在阅读SWIG文档,看看是否有一些可以应用的类型映射魔法,但我对SWIG总体上还是比较陌生的。

共有2个答案

谭景福
2023-03-14

如果你决定使用SWIG(正如你在帖子中所说,这对我来说并不清楚,所以我认为这是一个新项目),那么停止阅读,忽略这个答案。

但是,如果要使用的绑定技术还没有固定下来,您只需要绑定Python,而不需要绑定其他语言,那么另一种选择就是使用cppyy(http://cppyy.org完整免责声明:我是主要作者)。这样,boost::variant类型就可以直接在Python中使用,然后您可以通过编写Python代码而不是SWIG来让它看起来/行为更像Python。我需要密码。

示例(请注意,cppyy在PyPI上有Windows控制盘,但是用MSVC2017而不是MSVC2013构建的,所以我将保留关于MSVC2013是否足够现代,可以像我没有尝试过的那样构建代码的警告):

import cppyy

cppyy.include("boost/variant/variant.hpp")
cppyy.include("boost/variant/get.hpp")

cpp   = cppyy.gbl
std   = cpp.std
boost = cpp.boost

cppyy.cppdef("""
class A
{
//constructors and such.
};

class B
{
//constructors and such.
};

class C
{
//constructors and such.
};
""")

VariantType = boost.variant['A, B, C']
VariantTypeList = std.vector[VariantType]

v = VariantTypeList()
v.push_back(VariantType(cpp.A()))
print(v.back().which())
v.push_back(VariantType(cpp.B()))
print(v.back().which())
v.push_back(VariantType(cpp.C()))
print(v.back().which())

print(boost.get['A'](v[0]))
try:
    print(boost.get['B'](v[0]))
except Exception as e:
    print(e)   # b/c of type-index mismatch above
print(boost.get['B'](v[1]))  # now corrected
print(boost.get['C'](v[2]))

产生以下预期输出:

$ python variant.py
0
1
2
<cppyy.gbl.A object at 0x5053704>
Could not instantiate get<B>:
  B& boost::get(boost::variant<A,B,C>& operand) =>
    Exception: boost::bad_get: failed value get using boost::get (C++ exception)
<cppyy.gbl.B object at 0x505370c>
<cppyy.gbl.C object at 0x5053714>
公冶森
2023-03-14

你可以做到。我花了很长时间思考了boost::variant最简洁的Python接口到底是什么。我的结论是,99%的Python用户甚至不应该意识到有一种变体类型正在被使用——联合和变体基本上只是在某种程度上限制了C的duck类型。

所以我的目标是:

  • 只要可能,就可以从现有的类型映射中获益——我们不想从头开始编写自己的std::stringint,类型映射

在不向事物中添加一些机械的情况下完成所有这些是相当繁琐的。我将所有内容都包装在一个可重复使用的文件中,这是我的boost_variant. i的最终工作版本:

%{
#include <boost/variant.hpp>

static PyObject *this_module = NULL;
%}

%init %{
  // We need to "borrow" a reference to this for our typemaps to be able to look up the right functions
  this_module = m; // borrow should be fine since we can only get called when our module is loaded right?
  // Wouldn't it be nice if $module worked *anywhere*
%}

#define FE_0(...)
#define FE_1(action,a1) action(0,a1)
#define FE_2(action,a1,a2) action(0,a1); action(1,a2)
#define FE_3(action,a1,a2,a3) action(0,a1); action(1,a2); action(2,a3)
#define FE_4(action,a1,a2,a3,a4) action(0,a1); action(1,a2); action(2,a3); action(3,a4)
#define FE_5(action,a1,a2,a3,a4,a5) action(0,a1); action(1,a2); action(2,a3); action(3,a4); action(4,a5)

#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
%define FOR_EACH(action,...)
  GET_MACRO(__VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)(action,__VA_ARGS__)
%enddef

#define in_helper(num,type) const type & convert_type ## num () { return boost::get<type>(*$self); }
#define constructor_helper(num,type) variant(const type&)

%define %boost_variant(Name, ...)

%rename(Name) boost::variant<__VA_ARGS__>;

namespace boost {
  struct variant<__VA_ARGS__> {
    variant();
    variant(const boost::variant<__VA_ARGS__>&);
    FOR_EACH(constructor_helper, __VA_ARGS__);
    int which();
    bool empty();

    %extend {
      FOR_EACH(in_helper, __VA_ARGS__);
    }
  };
}

%typemap(out) boost::variant<__VA_ARGS__> {
  // Make our function output into a PyObject
  PyObject *tmp = SWIG_NewPointerObj(&$1, $&1_descriptor, 0); // Python does not own this object...

  // Pass that temporary PyObject into the helper function and get another PyObject back in exchange
  const std::string func_name = "convert_type" + std::to_string($1.which());
  $result = PyObject_CallMethod(tmp, func_name.c_str(),  "");
  Py_DECREF(tmp);
}

%typemap(in) const boost::variant<__VA_ARGS__>& (PyObject *tmp=NULL) {
  // I don't much like having to "guess" the name of the make_variant we want to use here like this...
  // But it's hard to support both -builtin and regular modes and generically find the right code.
  PyObject *helper_func = PyObject_GetAttrString(this_module, "new_" #Name );
  assert(helper_func);
  // TODO: is O right, or should it be N?
  tmp = PyObject_CallFunction(helper_func, "O", $input);
  Py_DECREF(helper_func);
  if (!tmp) SWIG_fail; // An exception is already pending

  // TODO: if we cared, we chould short-circuit things a lot for the case where our input really was a variant object
  const int res = SWIG_ConvertPtr(tmp, (void**)&$1, $1_descriptor, 0);
  if (!SWIG_IsOK(res)) {
    SWIG_exception_fail(SWIG_ArgError(res), "Variant typemap failed, not sure if this can actually happen"); 
  }
}

%typemap(freearg) const boost::variant<__VA_ARGS__>& %{
  Py_DECREF(tmp$argnum);
%}

%enddef

这为我们提供了一个可以在SWIG中使用的宏,%boost\u variant。然后,您可以在界面文件中使用以下内容:

%module test

%include "boost_variant.i"

%inline %{
  struct A {};
  struct B {};
%}

%include <std_string.i>
%boost_variant(TestVariant, A, B, std::string);

%inline %{
  void idea(const boost::variant<A, B, std::string>&) {
  }

  boost::variant<A,B,std::string> make_me_a_thing() {
    struct A a;
    return a;
  }

  boost::variant<A,B,std::string> make_me_a_string() {
    return "HELLO";
  }

%}

其中,%boost_variant宏将第一个参数作为类型的名称(很像%template那样),其余参数作为变量中所有类型的列表。

这足以让我们运行以下Python:

import test

a = test.A();
b = test.B();

test.idea(a)
test.idea(b)

print(test.make_me_a_thing())
print(test.make_me_a_string())

那这到底是怎么回事?

  • 我们在这里基本上复制了SWIG的%模板支持。(它在这里作为一个选项记录)
  • 我的文件中的大部分繁重工作都是使用FOR_EACH可变宏完成的。很大程度上这与我之前在std::function上的回答相同,它本身源自几个较旧的Stack Overflow答案,并适用于与SWIG的预处理器一起工作。
  • 使用FOR_EACH宏我们告诉SWIG为变体可以容纳的每种类型包装一个构造函数。这使我们可以从Python代码显式构造变体,并添加了两个额外的构造函数
  • 通过使用这样的构造函数,我们可以严重依赖SWIG的重载解析支持。因此,给定一个Python对象,我们可以简单地依靠SWIG来确定如何从中构建变体。这为我们节省了大量额外的工作,并为变体中的每种类型使用现有的类型映射。
  • typemap中的基本上只是通过稍微复杂的路线委托给构造函数,因为以编程方式在同一模块中查找其他函数出奇地困难。一旦委托发生,我们使用函数参数的正常转换将临时变体传递给函数,就好像它是我们被赋予的一样。
  • 我们还合成了一组额外的成员函数,convert_typeN内部只调用提升::get

 类似资料:
  • 我想使用SWIG将暴露到Tcl层。但目前我不知道我可以暴露这个。我发现SWIG/Lib文件夹包含的接口文件。但在内容中我发现我不能直接使用它。它必须包含在“”之后。但在“”文件夹中没有像这样的东西但我们有类似的界面,我可以包含在Java中。

  • SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP。支持语言列表中 也包括非脚本编译语言,例如C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, OCA

  • 问题内容: boost.python如何处理Python 3?仅限Python 2吗? 问题答案: 较新版本的Boost应该可以在Python V3.x上正常工作。早在2009年成功完成Google Summer of Code项目后,我相信很早以前就已经添加了此支持。 将Python V3与Boost结合使用的方法是,通过添加以下示例来正确配置构建系统: 到您的user-config.jam文件

  • 根据http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/overview/cpp2011/futures.html,我们可以将boost::asio与一起使用。但是我找不到任何有关使用的信息,它具有更多的功能,例如。我怎么用?

  • Boost 库是一个经过千锤百炼、可移植、提供源代码的 C++ 库,作为标准库的后备,是 C++ 标准化进程的发动机之一。 Boost 库由 C++ 标准委员会库工作组成员发起,在 C++ 社区中影响甚大,其成员已近 2000 人。 Boost 库为我们带来了最新、最酷、最实用的技术,是不折不扣的“准”标准库。

  • swig 是node端的一个优秀简洁的模板引擎,类似Python模板引擎Jinja,目前不仅在node端较为通用,相对于jade、ejs优秀,而且在浏览器端也可以很好地运行。 特性: 支持大多数主流浏览器。 表达式兼容性好。 面向对象的模板继承。 将过滤器和转换应用到模板中的输出。 可根据路劲渲染页面。 支持页面复用。 支持动态页面。 可扩展、可定制。 使用示例: 模板代码 <h1>{{ page