在使用许多 SWIG 生成的模块时避免重复的 SWIG 样板

Posted

技术标签:

【中文标题】在使用许多 SWIG 生成的模块时避免重复的 SWIG 样板【英文标题】:avoiding duplicate SWIG boilerplate when using many SWIG-generated modules 【发布时间】:2011-11-18 06:14:33 【问题描述】:

使用 SWIG 生成接口模块时,生成的 C/C++ 文件包含大量静态样板函数。因此,如果想要通过在同一个应用程序中使用许多单独编译的小接口来模块化 SWIG 生成的接口的使用,那么由于这些重复的功能,最终会导致很多臃肿。

使用 gcc 的 -ffunction-sections 选项和 GNU 链接器的 --icf=safe 选项(-Wl,--icf=safe 编译器),可以删除一些重复,但绝不是全部(我认为它不会合并任何有重定位的东西——这些函数中的许多都是这样做的)。

我的问题:我想知道是否有办法删除更多这种重复的样板,最好是不依赖于 GNU 特定的编译器/链接器选项。

特别是,是否有 SWIG 选项/标志/某些内容显示“不在每个输出文件中包含样板”?实际上一个 SWIG 选项,-external-runtime,它告诉它生成一个“仅样板”输出文件,但没有明显的方法来抑制每个正常输出文件中包含的副本。 [我认为这种事情在 SWIG 中实现起来应该相当简单,所以我很惊讶它似乎不存在......但我似乎找不到任何文档记录。]

这是一个小例子:

给定模块swt_oink的接口文件swg-oink.swg

%module swt_oink
% extern int oinker (const char *x); %
extern int oinker (const char *x);

...和swg-barf.swg 的类似接口swt_barf

%module swt_barf
% extern int barfer (const char *x); %
extern int barfer (const char *x);

...和一个测试主文件,swt-main.cc:

extern "C"

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

extern int luaopen_swt_oink (lua_State *);
extern int luaopen_swt_barf (lua_State *);


int main ()

  lua_State *L = lua_open();
  luaopen_swt_oink (L);
  luaopen_swt_barf (L);


int oinker (const char *)  return 7; 
int barfer (const char *)  return 2; 

并像这样编译它们:

swig -lua -c++ swt-oink.swg
g++ -c -I/usr/include/lua5.1 swt-oink_wrap.cxx
swig -lua -c++ swt-barf.swg
g++ -c -I/usr/include/lua5.1 swt-barf_wrap.cxx
g++ -c -I/usr/include/lua5.1 swt-main.cc
g++ -o swt swt-main.o swt-oink_wrap.o swt-barf_wrap.o

那么每个xxx_wrap.o文件的大小约为16KB,其中95%是样板文件,最终可执行文件的大小大致是这些的总和,大约39K。如果用-ffunction-sections编译每个接口文件,用-Wl,--icf=safe链接,最终的可执行文件大小为34KB,但显然还是有很多重复(在可执行文件上使用nm可以看到大量定义的函数多次,并查看它们的来源,很明显对它们中的大多数使用单一的全局定义就可以了)。

【问题讨论】:

【参考方案1】:

我很确定 SWIG 没有这样做的选项。我现在在猜测,但我认为原因很可能是担心使用不同版本的 SWIG 构建的模块的可见性。想象以下场景:

两个库 X 和 Y 都使用 SWIG 为其代码提供接口。他们都选择让“SWIG 胶水”在不同的翻译单元中可见,以减少代码大小。如果 X 和 Y 都使用相同版本的 SWIG,这一切都会很好。如果 X 使用 SWIG 1.1 而 Y 使用 SWIG 1.3 会发生什么?两个模块都可以自己正常工作,但取决于平台如何处理共享对象以及语言本身如何加载它们(RTLD_GLOBAL?)在同一 VM 中使用的两个模块的组合可能会发生一些非常糟糕的事情.

我怀疑代码重复的代价非常低 - 在 VM 和本机代码之间交换的成本通常非常高,这可能会使略微减少的指令缓存命中相形见绌,尽管看到真正的基准测试可能会很有趣。从好的方面来说,这是用户无需担心的代码,因为它都是自动生成的,并且都正确地保存在为相应版本编写的接口中。

【讨论】:

好点。我想可以通过在名称中包含某种 ABI 版本控制来处理它,但是将所有内容都设为静态肯定要简单得多,而且不容易出错。添加优化确实也有助于减少开销:在使用优化的实际案例中,每个目标文件似乎更多约为 4KB;仍然有点臃肿,但我想 SWIG 无论如何都不会真正轻量级......【参考方案2】:

我可能有点晚了,但这里有一个解决方法:

在 SWIG (-noruntime 命令行选项 由于 SWIG 2.0 -noruntime 已被弃用,所以现在应该将 -DSWIG_NOINCLUDE 传递给 C 预处理器 - 而不是传递给 swig 本身

我完全不确定这是否正确,但至少对我有用。我将在 SWIG 的邮件列表中澄清这个问题。

【讨论】:

以上是关于在使用许多 SWIG 生成的模块时避免重复的 SWIG 样板的主要内容,如果未能解决你的问题,请参考以下文章

SWIG - python 中的 C++ 代码

Python 错误:在 SWIG 生成的 C++ 模板代码模块中未定义构造函数

Swig/python:啥时候需要 SWIG_init()?

没有 DLL 的 SWIG + Python

swig 生成的代码链接到错误的 python 安装

TypeError:在方法“...”中,使用 swig 模块时类型为“unsigned char const *”的参数 1