分解SWIG Python接口 - 容器会产生命名空间冲突

Breaking up SWIG Python interface -- containers create namespace conflict

本文关键字:命名空间 冲突 SWIG Python 接口 分解      更新时间:2023-10-16

我们的代码库目前支持单个SWIG接口文件(用于Python(,该文件多年来已经发展到包括大约300个C++类(技术接口(,所有这些类都继承自单个基类,并且都存在于单个全局命名空间中。 这使我们能够使用最少量的 SWIG 代码在 SWIG 类表示的C++类之间实现动态转换,同时通过将C++继承结构排除在 SWIG 之外来简化。

只要我们在单个模块中编译我们的 SWIG 接口,这种机制就可以很好地工作——但随着 SWIG 接口文件的增长,它变得难以管理,编译/链接时间也增加了。 为了解决这个问题,我按派生类的名称将接口文件拆分为单独的模块——一个模块用于以"A"到"G"开头的类名,一个用于以"H"到"N"开头的名称,等等,从而产生了四个派生类模块和一个基类模块。 我能够按照此处概述的方法编译和链接这些模块,并展示动态强制转换的预期行为:(http://www.swig.org/Doc3.0/SWIGDocumentation.html#Modules_nn1(

但是,当容器发挥作用时,将单个模块分成四个部分(计算基类的五个部分(会导致命名空间出现问题。 考虑以下函数,来自我的 v-to-z 接口文件中的类:

void RemoveIsolated(const std::vector<global::IFoo*> spRemoveIsolated) {

。 }

这需要全局命名空间中存在的派生类之一的向量。 当我只有一个模块时,这没有问题,但现在类 IFoo 存在于 a-to-g 模块中——所以如果我将某些内容转换为 IFoo*,那就是 a-to-g。呜*。 但是,该函数需要一个全局::IFoo*。

这种情况似乎可以通过SWIG模板机制来解决。 我见过这样的讨论,其中人们通过一次(可能在基类的接口文件中??(声明

%template(FooVector) std::vector<global::Foo*>;

在另一点(可能在派生类的接口文件中??

%template () std::vector<global::Foo*>;

但我实施这一点的尝试并没有成功。 讨论有些模棱两可,可能是我做错了什么。 任何人都可以提供澄清,最好是举例说明吗?

看起来你缺少的信息是%import指令,它允许模块与类型的定义合作,而无需重复它们,并且仍然以单个包装类型结束。文档建议使用它来减小模块大小。

可能您需要做的就是将您的 v 到 z 模块%importa 到 g 模块,以使其为您工作。(就个人而言,我会尝试按功能而不是按字母顺序划分它们,因此两者之间的依赖关系不会成为问题(

感谢您的建议柔版印刷。 导入 a-to-g 模块不起作用;C++编译器抱怨说,当它尝试编译为 V-to-Z 包装器文件时,声明的所有类(接口(都不是全局命名空间的一部分。 然而,通过练习,我质疑为什么我们以前在编译单个模块时取得了成功。 事实证明,我们在接口文件中为单个模块使用了一个类型映射宏,该模块将采用

const std::vector<global::IFoo*>

并这样映射它:

TYPEMAPMACRO(global::IFoo,  SWIGTYPE_p_global__IFoo)

对于矢量容器。 对于任何感兴趣的人来说,宏本身是:

%define TYPEMAPMACRO(type, name) %typemap(in) const std::vector { /*Check if is a list */ std::vector vec; void *pobj = 0; if(PyTuple_Check($input)) { size_t size = PyTuple_Size($input); for (size_t j = 0; j < size; j++) { PyObject *o = PyTuple_GetItem($input, j); void *argp1 = 0 ; int res1 = SWIG_ConvertPtr(o, &argp1, name, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Typemap of std::vector" "', argument " "1"" of type '" """'"); } vec.push_back(reinterpret_cast< type * >(argp1)); } $1 = vec; } else if (SWIG_IsOK(SWIG_ConvertPtr($input, &pobj, name, 0 | 0 ))) {
PyObject *o = $input; void *argp1 = 0 ; int res1 = SWIG_ConvertPtr(o, &argp1, name, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Typemap of std::vector" "', argument " "1"" of type '" """'"); } vec.push_back(reinterpret_cast< type * >(argp1)); $1 = vec; } else { PyErr_SetString(PyExc_TypeError, "not a list"); return NULL; } } %typecheck(SWIG_TYPECHECK_POINTER) std::vector { void *pobj = 0; if(!PyTuple_Check($input) && !SWIG_IsOK(SWIG_ConvertPtr($input, &pobj, name, 0 | 0 ))) { $1 = 0; PyErr_Clear(); } else { $1 = 1; } } %enddef

我的感觉是,这是标准的样板内容,我并不声称理解它,因为它是别人的代码,但是我现在理解的是,我以前没有理解的是,我需要将类型映射的宏放在使用类型映射的函数之前(例如上面的"RemoveIsolated"示例(。 当我把我的大模块分成小模块时,这个顺序被打破了。