Swig:如何编写Tcl的包装器代码以将enum类型的成员映射为字符串常量

时间:2019-05-09 23:34:43

标签: enums tcl wrapper swig

我已经读过here,“ C / C ++常量作为包含适当值的全局Tcl变量安装”,这也适用于枚举。我正在尝试使用swig为一个枚举类(称为“声明”)构建一个Tcl包装器,该包装将导致将相应的Tcl变量存储为字符串对象。 C ++代码提供了一些我认为可以用来执行转换的ostream转换功能,但是我找不到有效的配方。我尝试了以下方法:

    //%typemap(argout) Statement *out {
    //  ostringstream oss;
    //  oss << $1;
    //  $result = Tcl_NewStringObj(oss.str()->c_str(), oss.str().size());
    //}
    //%typemap(constcode) Statement {
    //  ostringstream oss;
    //  oss << $1;
    //  $result = Tcl_NewStringObj(oss.str()->c_str(), oss.str().size());
    //}
    //%typemap(out) Statement {
    //  ostringstream oss;
    //  oss << $1;
    //  $result = Tcl_NewStringObj(oss.str()->c_str(), oss.str().size());
    //}

另一个(可能是相关的问题)是,我的包装器中的枚举根本没有创建Tcl变量。我从this follow up link中了解到,当您使用静态链接时,用于存储常量的Tcl变量将放在:: swig命名空间中。但这不是我的问题:我没有:: swig命名空间,info vars也没有在顶部命名空间中列出任何变量。

2 个答案:

答案 0 :(得分:0)

我找到了第二期的答案。我的包装器SWIG代码使用%init指令,该指令使用一些魔术来利用readline库。它正在评估一个Tcl脚本,该脚本将在其余应用程序初始化有机会完成之前启动readline命令处理循环。常量初始化代码是在提供给%init SWIG块的代码块之后生成的,因此从未执行过。通过将枚举的SWIG声明移到%init部分上方,更改了插入的常量初始化代码和%init段的相对顺序,从而解决了该问题。

底线:SWIG包装程序代码中声明和%init段的相对顺序很重要。

答案 1 :(得分:0)

我能够使用以下形式的类型映射来解决此问题:

    %typemap(out) enum NS::Statement  {
        ostringstream oss;
        oss << "NS_Statement(" << $1 << ")";
        Tcl_SetObjResult(interp,Tcl_NewStringObj(oss.str().c_str(), oss.str().size()));
    }

以前无法正常工作的原因是该枚举是在名称空间语句中定义的。即使我已经“使用名称空间NS”;在typemap声明之前的语句,直到我提供了枚举的完整名称空间限定符后,该语句才被应用。此外,必须在包装代码声明枚举常量之前提供两个typemap语句。

如您所见,返回的变量名称是Tcl数组变量名称。为了保持一致,还需要更改由生成的代码设置的包含枚举的实际值的全局变量。我能够使用另一个像这样的类型图来实现这一点:

    %typemap(constcode,noblock=1) int {
      %set_constant("NS_Statement($symname)", SWIG_From_long(static_cast< int >($1)));
    }

在需要包装多个枚举类型的情况下,只需在每个枚举的SWIG声明之前插入一组相似的类型映射,即可将Tcl数组名称部分与要映射的枚举类型匹配。如果在您的SWIG代码中声明了非枚举常量,或者您不想以这种方式包装其他枚举类型,请在添加SWIG代码之前添加最后一个typemap(constcode)以重置为默认行为声明其他常量。

我创建了一个小例子来说明这种方法:

// file example.h
enum TOPETYPE {BI, DUL, BUC};
class MyClass {
public:
  enum ETYPE {ONE,TWO, THREE};
  static void Foo(ETYPE);
  static ETYPE Bar(int);
};
namespace NS {
  enum LIBENUM {LIB1, LIB2, LIB3};
}
extern const char * ETYPE2Str(MyClass::ETYPE);
extern const char * TOPETYPE2Str(TOPETYPE);
extern const char * LIBENUM2Str(NS::LIBENUM);
/* File : example.i */
%module example

%{
#include "example.h"
#include <sstream>
using namespace std;
%}

#define XX 0
%typemap(out) enum TOPETYPE {
#include <iostream>
   std::ostringstream oss;
   oss << "TOPETYPE(" << TOPETYPE2Str($1) << ")";
   Tcl_SetObjResult(interp,Tcl_NewStringObj(oss.str().c_str(), -1));
 }
%typemap(constcode,noblock=1) int {
  %set_constant("TOPETYPE($symname)", SWIG_From_long(static_cast< int >($1)));
 }
enum TOPETYPE {BI, DUL, BUC};
%typemap(out) enum MyClass::ETYPE {
#include <iostream>
   std::ostringstream oss;
   oss << "MyClass_ETYPE(MyClass_" << ETYPE2Str($1) << ")";
   Tcl_SetObjResult(interp,Tcl_NewStringObj(oss.str().c_str(), -1));
 }
%typemap(constcode,noblock=1) int {
  %set_constant("MyClass_ETYPE($symname)", SWIG_From_long(static_cast< int >($1)));
 }
class MyClass {
public:
  enum ETYPE {ONE,TWO, THREE};
  static void Foo(ETYPE);
  static ETYPE Bar(int);
};
%typemap(out) enum NS::LIBENUM {
#include <iostream>
   std::ostringstream oss;
   oss << "NS_LIBENUM(" << LIBENUM2Str($1) << ")";
   Tcl_SetObjResult(interp,Tcl_NewStringObj(oss.str().c_str(), -1));
 }
%typemap(constcode,noblock=1) int {
  %set_constant("NS_LIBENUM($symname)", SWIG_From_long(static_cast< int >($1)));
 }
namespace NS {
  enum LIBENUM {LIB1, LIB2, LIB3};
}
// file example.cpp
#include "example.h"
#include <iostream>
using namespace std;
void MyClass::Foo(MyClass::ETYPE typ)
{
  cout << "Enum value = " << typ << endl;
}
MyClass::ETYPE MyClass::Bar(int val)
{
  switch (static_cast<MyClass::ETYPE>(val)) {
  case MyClass::ETYPE::ONE: {return MyClass::ETYPE::ONE;}
  case MyClass::ETYPE::TWO: {return MyClass::ETYPE::TWO;}
  case MyClass::ETYPE::THREE: {return MyClass::ETYPE::THREE;}
  default: {return MyClass::ETYPE::THREE;}
  }
}
const char * ETYPE2Str(MyClass::ETYPE val) {
  switch (val) {
  case MyClass::ETYPE::ONE: {return "ONE";}
  case MyClass::ETYPE::TWO: {return "TWO";}
  case MyClass::ETYPE::THREE: {return "THREE";}
  default: {return "unknown";}
  }
}
const char * TOPETYPE2Str(TOPETYPE val) {
  switch (val) {
  case TOPETYPE::BI: {return "BI";}
  case TOPETYPE::DUL: {return "DUL";}
  case TOPETYPE::BUC: {return "BUC";}
  default: {return "unknown";}
  }
}
const char * LIBENUM2Str(NS::LIBENUM val) {
  switch (val) {
  case NS::LIB1: {return "LIB1";}
  case NS::LIB2: {return "LIB2";}
  case NS::LIB3: {return "LIB3";}
  default: {return "unknown";}
  }
}

您可以尝试以下方法:

swig -c++ -tcl8 example.i
g++ -c -fpic example_wrap.cxx example.cpp -I/usr/local/include
g++ -shared example.o example_wrap.o -o example.so

然后,在tclsh中:

% load example4.so
% info vars
XX tcl_rcFileName tcl_version argv0 argv tcl_interactive auto_path errorCode NS_LIBENUM errorInfo auto_execs auto_index env tcl_pkgPath MyClass_ETYPE TOPETYPE tcl_patchLevel swig_runtime_data_type_pointer4 argc tcl_library tcl_platform
% info commands
MyClass_Bar tell socket subst open eof pwd glob list pid exec auto_load_index time unknown eval lassign lrange fblocked lsearch auto_import gets case lappend proc break variable llength auto_execok return linsert error catch clock info split array if fconfigure concat join lreplace source fcopy global switch auto_qualify update close cd for auto_load file append lreverse format unload read package set binary namespace scan delete_MyClass apply trace seek while chan flush after vwait dict continue uplevel foreach lset rename fileevent regexp new_MyClass lrepeat upvar encoding expr unset load regsub history interp exit MyClass puts incr lindex lsort tclLog MyClass_Foo string
% array names NS_LIBENUM
LIB1 LIB2 LIB3
% array names MyClass_ETYPE
MyClass_TWO MyClass_ONE MyClass_THREE
% array names TOPETYPE
DUL BUC BI
% puts $XX
0
% MyClass_Bar $MyClass_ETYPE(MyClass_ONE)
MyClass_ETYPE(MyClass_ONE)
% MyClass_Foo $MyClass_ETYPE(MyClass_ONE)
Enum value = 0
% exit