我读过here,"C/C++常量作为包含适当值的全局Tcl变量安装“,这也适用于枚举。我试图使用swig为枚举类(称为“语句”)构建一个Tcl包装器,该类将生成相应的Tcl变量,并将其存储为string对象。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也没有列出顶部名称空间中的任何变量。
发布于 2019-05-13 13:35:32
我能够使用表单的类型地图解决这个问题:
%typemap(out) enum NS::Statement {
ostringstream oss;
oss << "NS_Statement(" << $1 << ")";
Tcl_SetObjResult(interp,Tcl_NewStringObj(oss.str().c_str(), oss.str().size()));
}它以前不起作用的原因是枚举是在命名空间语句中定义的。即使在类型地图声明之前有了“使用命名空间NS;”语句,但在提供枚举的完整名称空间限定符之前,不会应用它。另外,必须在声明枚举常量的包装代码之前提供两个类型地图语句。
如您所见,返回的变量名是Tcl数组变量名。为了保持一致,还需要更改由包含枚举的实际值的生成代码设置的全局变量。我可以使用另一个类型地图实现这一点,如下所示:
%typemap(constcode,noblock=1) int {
%set_constant("NS_Statement($symname)", SWIG_From_long(static_cast< int >($1)));
}在需要包装多个枚举类型的情况下,只需在每个枚举的SWIG声明之前插入类似的类型映射集,将Tcl数组名称部分与映射的枚举类型匹配。如果要在SWIG代码中声明非枚举常量,或者您不希望以这种方式包装的其他枚举类型,则在添加声明这些其他常量的SWIG代码之前,添加最后一个类型映射(构造代码)以重置为默认行为。
我创造了一个小例子,说明了这种方法:
// 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发布于 2019-05-10 00:20:15
我已经找到了第二个问题的答案。我的包装器SWIG代码使用%init指令,它使用一些魔术来使用readline库。它正在评估一个Tcl脚本,该脚本在应用程序初始化的其余部分有机会完成之前启动readline命令处理循环。常量初始化代码是在提供给%init SWIG块的代码块之后生成的,因此它从未被执行。通过将enum的SWIG声明移动到%init节之上,更改了插入常量初始化代码和%init段的相对顺序,并解决了该问题。
底线:声明的相对顺序和%init段在您的口包装代码中很重要。
https://stackoverflow.com/questions/56068855
复制相似问题