Typedef、Ruby 和 SWIG

Posted

技术标签:

【中文标题】Typedef、Ruby 和 SWIG【英文标题】:Typedefs, Ruby and SWIG 【发布时间】:2015-09-04 20:00:58 【问题描述】:

我正在尝试为一些 C++ 类生成一个 Ruby 包装器。生成成功,所有方法都创建好了,但问题是很多 C++ 方法都使用了这个:

#ifdef USE_LONGLONG_COUNTS
typedef unsigned long long Count;   /* a count of something */
#else
typedef unsigned long Count;        /* a count of something */
#endif

当我在 irb 中运行一个返回 Count 的方法时,我得到如下信息:

irb(main):006:0> ngram.numNgrams(0)
=> #<SWIG::TYPE_p_Count:0x00000001c52280>

我期待一个数字...我尝试使用反射来查看是否可以通过某种方式获得该值,但没有雪茄。有什么建议吗?

【问题讨论】:

也许会有所帮助swig.org/Doc1.3/SWIG.html#SWIG_nn20 【参考方案1】:

typedef 阻止 SWIG 确定 Counter 是它可以直接转换的未装箱类型。在没有其他信息的情况下,它将Counter 视为只能由库中的函数生成和使用的抽象对象类型。

您需要告诉 SWIG Counter 等价于内在 longlong long

typemap 是 SWIG 的做法。此外,SWIG 语言本身提供了typedefs 来声明Counter 的等价性及其底层的内在类型。

这是它的工作原理。您应该能够轻松地将其转换为您的问题的解决方案:

// hacking.i
%module hacking
%
// "Simulation" of the header included verbatim in the glue code.
#ifdef USE_LONGLONG_COUNTS
typedef unsigned long long Count;   /* a count of something */
#else
typedef unsigned long Count;        /* a count of something */
#endif
Count foo(Count count)  return count; 
%

// The typemaps...
%typemap(in) Count n 
#ifdef USE_LONGLONG_COUNTS
 $1 = ULL2INT($input);
#else
 $1 = ULONG2INT($input);
#endif


%typemap(out) Count 
#ifdef USE_LONGLONG_COUNTS
 $result = ULL2NUM($1);
#else
 $result = ULONG2NUM($1);
#endif


// Now tell SWIG about the type equivalences and function prototype.
#ifdef USE_LONGLONG_COUNTS
typedef unsigned long long Count;   /* a count of something */
#else
typedef unsigned long Count;        /* a count of something */
#endif
Count foo(Count count);

请注意,如果定义了USE_LONGLONG_COUNTS,那么您使用的Ruby 必须支持long long 类型。并非所有人都这样做。

Typemap 功能强大,有很多选项。您没有提供足够的信息来为您的问题提供更具体的解决方案。在您提供更多内容之前,引用Ruby SWIG docs 几乎是所有可能的事情。

现在构建,制作一个文件:

# extconf.rb
require 'mkmf'
create_makefile('hacking')

并且(如果您需要unsigned long,请省略-DUSE_LONGLONGCOUNTS):

$ swig -c++ -DUSE_LONGLONG_COUNTS -ruby -o wrap.cpp hacking.i
$ ruby extconf.rb
$ make
compiling wrap.cpp
linking shared-object hacking.bundle
$ make install
/usr/bin/install -c -m 0755 hacking.bundle ...  
$ irb
2.2.1 :001 > require 'hacking'
 => true 
2.2.1 :002 > Hacking.foo(1234567890123454567)
 => 1234567890123454567 

【讨论】:

【参考方案2】:

这很奇怪。

您确定 swig 正在“看到”您的 typedefCount?因为否则Count 只是 swig 不知道的某种类型,它会将其包装为指向 C 对象的指针,在这种情况下,swig 应该生成警告。

无论如何,我刚刚从您的问题中创建了一个示例,并按原样使用 swig 3.0.7 对其进行了测试,对我来说它只是按预期工作(没有类型映射):

dummy.i(其他情况请评论#define USE_LONGLONG_COUNTS

%module dummy

%inline %

#define USE_LONGLONG_COUNTS

#ifdef USE_LONGLONG_COUNTS
typedef unsigned long long Count;   /* a count of something */
#else
typedef unsigned long Count;        /* a count of something */
#endif

Count returnCount(Count count)  return count; 

%

extconf.rb

require 'mkmf'
create_makefile('dummy')

要编译和运行 ruby​​,我会这样做:

swig -c++ -ruby dummy.i
ruby extconf.rb
make
irb

那么在irb,我可以做到:

irb(main):001:0> require_relative 'dummy'
=> true
irb(main):002:0> Dummy.returnCount(2**64-1)
=> 18446744073709551615
irb(main):003:0> Dummy.returnCount(-1)
=> 18446744073709551615
irb(main):004:0> Dummy.returnCount(2**64)
TypeError: Expected argument 0 of type Count, but got Bignum 18446744073709551616
in SWIG method 'returnCount'
from (irb):4:in `returnCount'
from (irb):4
from /usr/bin/irb:12:in `<main>'

请注意,unsigned long longunsigned long 在我的系统/编译器上都是 64 位的,我的 ruby​​ 版本支持这些。如果这不是真的,也许转换可能不起作用,我不知道。

在任何情况下,都不是 typedef 导致问题的原因,正如另一个答案所述。

【讨论】:

以上是关于Typedef、Ruby 和 SWIG的主要内容,如果未能解决你的问题,请参考以下文章

Typedef 用法

typedef和#define的用法与区别

C语言 typedef 和 define 区别

typedef的使用

探究C/C++ typedef的秘密

#define和typedef定义变量