Swig: как написать код оболочки для Tcl, чтобы отобразить член типа enum как строковые константы - PullRequest
0 голосов
/ 10 мая 2019

Я прочитал здесь , что «константы C / C ++ установлены как глобальные переменные Tcl, содержащие соответствующее значение», что также относится и к enum. Я пытаюсь создать оболочку Tcl, используя swig для класса enum (называемого «Statement»), который приведет к тому, что соответствующие переменные 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 вообще не создаются из перечислений в моей оболочке. Я прочитал из этой последующей ссылки , что когда вы используете статическое связывание, переменные Tcl, используемые для хранения констант, будут помещены в пространство имен :: swig. Но это не моя проблема: у меня нет пространства имен :: swig, и в info vars также нет переменных в верхнем пространстве имен.

Ответы [ 2 ]

0 голосов
/ 13 мая 2019

Мне удалось решить эту проблему с помощью карты типов в форме:

    %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 должны быть предоставлены до того, как код оболочки объявит константы перечисления.

Как видите, возвращаемое имя переменной является именем переменной массива Tcl. Чтобы все было согласованно, глобальные переменные, установленные сгенерированным кодом, которые содержат фактические значения перечисления, также должны быть изменены. Я смог добиться этого с помощью другой раскладки, например, так:

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

В случае, когда вам нужно обернуть несколько типов перечислений, просто вставьте подобный набор типов карт для каждого перечисления перед его объявлением SWIG, сопоставляя часть имени массива Tcl с отображаемым типом перечисления. В случае если в вашем SWIG-коде должны быть объявлены не перечислимые константы или другие перечисляемые типы, которые вы не хотите переносить таким образом, добавьте последнюю таблицу типов (constcode) для сброса поведения по умолчанию перед добавлением кода 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
0 голосов
/ 10 мая 2019

Я нашел ответ на второй вопрос. Мой SWIG-код оболочки использует директиву% init, которая использует магию для использования библиотеки readline. Он оценивал сценарий Tcl, который запускает цикл обработки команд readline до того, как оставшаяся часть инициализации приложения сможет завершиться. Постоянный код инициализации был сгенерирован после блока кода, предоставленного блоку% init SWIG, поэтому он никогда не выполнялся. При перемещении объявления SWIG перечисления над разделом% init относительный порядок вставленного константы кода инициализации и сегмента% init был изменен, и проблема была решена.

Итог: имеет значение относительный порядок объявления и сегмент% init в вашем коде оболочки SWIG.

...