SWIG4 --- 19 Working with Modules

19 Working with Modules

19.1 Modules Introduction

  每次调用SWIG都需要指定一个模块名。模块名称用于命名生成的目标语言扩展模块。确切地说,这意味着什么以及名称的用途取决于目标语言,例如,名称可以定义目标语言名称空间,或者仅仅是用于命名文件或帮助类的有用名称。从本质上讲,一个模块包含了针对全局变量/函数、结构体/类和其他C/ c++类型的目标语言包装器。

  模块名可以通过两种方式之一提供。第一种方法是使用特殊的%module指令来指定它。这个指令必须出现在接口文件的开头。该指令的一般形式是:

%module(option1="value1", option2="value2", ...) modulename

  其中模块名是强制性的,选项添加了一个或多个可选的附加特性。通常不指定选项,例如:

%module mymodule

  指定模块名的第二种方法是使用-module命令行选项,例如-module mymodule。如果在命令行中提供了模块名,它将覆盖由%module指令指定的名称。

  当首次使用SWIG时,用户通常从创建单个模块开始。也就是说,您可以定义一个单独的SWIG接口来包装一些C/ c++代码集。然后将所有生成的包装器代码编译在一起并使用它。然而,对于大型应用程序,这种方法是有问题的——生成的包装器代码的大小可能相当大。此外,当目标语言接口被分成更小的部分时,管理它可能更容易。

  本章描述在需要创建模块集合的程序中使用SWIG的问题。集合中的每个模块都是通过单独调用SWIG创建的。

19.2 Basics

  使用多个模块的基本情况是模块没有交叉引用(例如。当包装多个独立的C api时)。在这种情况下,swig输入文件应该是开箱即用的——您只需创建多个包装器.cxx文件,将它们链接到您的应用程序,并在脚本语言运行时中插入/加载每个文件,就像您在单个模块的情况下所做的那样。

  更复杂的情况是模块需要共享信息。例如,当一个模块通过派生来扩展另一个模块的类:

// File: base.h
class base {
public:
  int foo();
};
// File: base_module.i
%module base_module

%{
#include "base.h"
%}
%include "base.h"
// File: derived_module.i
%module derived_module

%import "base_module.i"

%inline %{
class derived : public base {
public:
  int bar();
};
%}

  为了正确地创建包装器,模块derived_module需要知道基类的情况,以及它的接口在另一个模块中被覆盖。%import "base_module.i "这一行让SWIG确切地知道这一点。通常情况下,.h 文件被传递给 %import 而不是 .i,不幸的是,这并不是对所有语言模块都有效。例如,Python需要基类存在的模块名称,以便代理类可以完全继承基类的方法。通常情况下,当模块名称缺失时,你会得到一个警告,例如。

derived_module.i:8: Warning 401: Base class 'base' ignored - unknown module name for base. Either
import
the appropriate module interface file or specify the name of the module in the %import directive.

  有时,最好是导入头文件而不是接口文件,以克服上述警告。例如,在导入的接口相当大的情况下,可能需要简化问题,只导入一个依赖类型的小头文件。这可以通过在%import指令中指定可选的模块属性来实现。上面显示的derived_module.i文件可以被替换成以下内容。

// File: derived_module.i
%module derived_module

%import(module="base_module") "base.h"

%inline %{
class derived : public base {
public:
  int bar();
};

  注意,"base_module "是模块名称,与base_module.i中的%module以及derived_module.i中的%import所指定的名称相同。

  另一个需要注意的问题是,多个依赖的包装器不应该从多个线程中并行地链接/加载,因为SWIG不提供锁——关于这个问题的更多信息,请继续阅读。

19.3 The SWIG runtime code

  许多SWIG的目标语言都会产生一组通常被称为 "SWIG运行时 "的函数。这些函数主要与运行时类型系统有关,该系统检查指针类型并执行其他任务,如在C++中正确转换指针值。一般来说,静态类型的目标语言,如Java,使用语言内置的静态类型检查,不需要SWIG运行时。所有动态类型/解释型语言都依赖于SWIG的运行时。

  运行时函数对每个SWIG生成的模块都是私有的。也就是说,运行时函数是以 "静态 "链接方式声明的,并且只对该模块中定义的封装函数可见。这种方法的唯一问题是,当在同一个程序中使用一个以上的SWIG模块时,这些模块往往需要共享类型信息。这对C++程序来说尤其如此,因为SWIG必须收集和共享跨越模块边界的继承关系的信息。

  为了解决跨模块共享信息的问题,一个指向类型信息的指针被存储在目标语言命名空间的全局变量中。在模块初始化期间,类型信息被加载到所有模块的类型信息的全局数据结构中。

  这种方法有一些不足之处。这种类型信息在所有加载的SWIG模块中都是全局性的,可能会造成模块之间的类型冲突,而这些模块在设计上并不是一起工作的。为了解决这种方法,SWIG运行时代码使用了一个定义SWIG_TYPE_TABLE来提供一个唯一的类型表。在编译生成的_wrap.cxx或_wrap.c文件时,可以通过在命令行参数中加入-DSWIG_TYPE_TABLE=myprojectname来启用这一行为。

  然后,只有在SWIG_TYPE_TABLE设置为myprojectname的情况下编译的模块才能共享类型信息。因此,如果你的项目有三个模块,所有三个模块都应该用-DSWIG_TYPE_TABLE=myprojectname来编译,然后这三个模块将共享类型信息。但任何其他项目的类型都不会与你的模块中的类型发生干扰或冲突。

  与全局类型表有关的另一个问题是线程安全。如果两个模块试图同时加载,类型信息就会被破坏。SWIG目前没有提供任何锁定功能,如果你使用线程,你必须确保模块是串行加载的。如果你使用线程和一些脚本语言提供的自动加载模块的功能,要小心。一个解决方案是在生成任何线程之前加载所有模块,或者使用SWIG_TYPE_TABLE来分离类型表,这样它们就不会相互冲突。

  最后,SWIG使用一个#define SWIG_RUNTIME_VERSION,位于Lib/swigrun.swg中,靠近每个生成模块的顶部。当数据结构发生变化时,这个数字会被递增,这样,以不同版本生成的SWIG模块可以和平共处。所以类型结构被(SWIG_TYPE_TABLE, SWIG_RUNTIME_VERSION)对分开,默认情况下SWIG_TYPE_TABLE为空。只有用同一对编译的模块才能共享类型信息。

19.4 External access to the runtime

  如运行时类型检查器所述,有时需要调用SWIG_TypeQuery、SWIG_NewPointerObj等函数。支持从typemap中调用这些函数,因为typemap代码嵌入到_wrap.c文件中,其中有这些声明。如果需要从另一个C文件调用SWIG运行时函数,则需要包含一个头文件。要生成需要包含的头文件,可以通过-external-runtime以不同的模式运行SWIG,以生成运行时,而不是处理输入接口文件的常规模式。例如

$ swig -python -external-runtime <filename>

  filename参数是可选的,如果它没有被传递,那么默认文件名将是swigpyrun.h之类的东西,这取决于语言。应该像对待任何其他_wrap.c输出文件一样对待这个头文件,并且应该在_wrap文件被重新生成时重新生成。在包含这个头文件之后,你的代码将能够调用SWIG_TypeQuery, SWIG_NewPointerObj, SWIG_ConvertPtr和其他。这些函数的实参参数可能在不同的语言模块之间有所不同;请查看语言模块章节以获得更多信息。

  在这个头文件中,函数被声明为静态的,并内联地包含在文件中,因此文件不需要链接到任何SWIG库或代码(您可能仍然需要链接到语言库,如libpython-2.3)。该文件和_wrap.c文件通过脚本语言中的一个全局变量共享数据。也可以复制这个头文件和生成的包装文件到您自己的包,这样你就可以发布一个包,可以编译没有痛饮安装(这工作,因为头文件是自给自足的,不需要与任何东西)。

  这个头文件还将使用上面描述的-DSWIG_TYPE_TABLE,因此在编译任何包含生成的头文件的代码时,应该将SWIG_TYPE_TABLE定义为与您试图访问的类型相同的模块。

  

 

posted @ 2021-12-06 10:17  神龙逗勇士  阅读(107)  评论(0编辑  收藏  举报