SWIG - C++ 到 python - 将 ULL 值转换为 0 的枚举

SWIG - C++ to python - enum that has ULL values being converted to 0

我有一个遗留的 C++ 代码库,我正在使用 SWIG 为其生成 python 绑定。 在此代码库中,到处都是枚举,它们具有特定的值,然后用于二进制操作。

使用的典型头文件如下所示:

namespace doom
{
class Bar
{
public:
    struct FooIdent
    {
        enum Ident
        {
            UnknownFoo = 0,
            KnownFoo = 1,
            MainFoo = 2,
            SecondaryFoo = 3
        };
    };

    enum FooPresence
    {
        Boo = 0x0,
        Foo1 = 0x8000000000ULL,
        Foo2 = 0x4000000000ULL,
        Foo3 = 0x2000000000ULL,
        FooWithA1 = 0x1000000000ULL,
        FooWithA2 = 0x0800000000ULL,
        FooWithA3 = 0x0400000000ULL,
        FooWithA4 = 0x0200000000ULL,
        FooWithB1 = 0x0100000000ULL,
        FooWithB2 = 0x0080000000,
        FooWithB3 = 0x0040000000
    };

    Bar();

    void setVesODee( int ves, doom::Bar::FooPresence pr );
    void setVesOGoo( int goo, doom::Bar::FooIdent::Ident ide );
    int doSomething();

private:
    int m_vdee;
    int m_vgoo;
};
} // namespace doom

相应的 .cpp 文件将是:

#include "bar.h"
#include <iostream>

namespace doom
{

Bar::Bar()
{
    m_vdee = 0;
    m_vgoo = 0;
}

void Bar::setVesODee( int ves, doom::Bar::FooPresence pr ) {
    m_vdee = static_cast< doom::Bar::FooPresence >( ves | pr );
}

void Bar::setVesOGoo( int goo, doom::Bar::FooIdent::Ident ide ) {
    m_vgoo = static_cast< doom::Bar::FooIdent::Ident >( goo | ide );
}

int Bar::doSomething() {
    return m_vgoo + m_vdee;
}

} // namespace doom


int main() {
    doom::Bar b = doom::Bar();

    b.setVesODee(3, doom::Bar::FooWithB2);
    b.setVesOGoo(4, doom::Bar::FooIdent::MainFoo);

    int c = b.doSomething();
    std::cout << c << std::endl;

    return 0;
}

.i 文件看起来像这样:

%feature ("flatnested");

%module bar

%{

#include "bar.h";

%}

%rename("Bar_%s", %$isnested) "";
%include "bar.h"

我使用以下命令构建:

swig -python -c++ -py3 bar.i
g++ -fPIC -c $(pkg-config --cflags --libs python3) bar.cpp bar_wrap.cxx
g++ -shared -o _bar.so bar.o bar_wrap.o

然后我使用以下方法测试代码:

import bar

for i in dir(bar.Bar_FooIdent):
    if i[0].isupper():
        print(f'{i} = 0x{getattr(bar.Bar_FooIdent, i):X}')

print()

for i in dir(bar.Bar):
    if i[0].isupper():
        print(f'{i} = 0x{getattr(bar.Bar, i):X}')

这输出:

KnownFoo = 0x1
MainFoo = 0x2
SecondaryFoo = 0x3
UnknownFoo = 0x0

Boo = 0x0
Foo1 = 0x0
Foo2 = 0x0
Foo3 = 0x0
FooWithA1 = 0x0
FooWithA2 = 0x0
FooWithA3 = 0x0
FooWithA4 = 0x0
FooWithB1 = 0x0
FooWithB2 = 0x-80000000
FooWithB3 = 0x40000000

似乎 SWIG 没有正确转换 ULL 文字,我不知道如何告诉 SWIG 将这些解释为更大的类型。

我不太习惯使用 SWIG,但我现在已经能凑合使用了一段时间,并且能够生成我需要的大部分代码。我在网上搜索了文档和问题,但无法完成这项工作。进行此转换的任何指示?

默认情况下,SWIG 将枚举常量视为 int,即使您重写为:

enum FooPresence : unsigned long long { ... }

这会在 test_wrap.cxx 文件中生成如下代码:

SWIG_Python_SetConstant(d, "Foo1",SWIG_From_int(static_cast< int >(Foo1)));
SWIG_Python_SetConstant(d, "Foo2",SWIG_From_int(static_cast< int >(Foo2)));

我能够通过覆盖 constcode 类型映射来假设 64 位整数来获得正确的结果。

test.i

%module test

%typemap(constcode) int %{SWIG_Python_SetConstant(d, "",PyLong_FromLongLong(static_cast<long long>()));%}

%inline %{
enum FooPresence : unsigned long long
{
    Foo1 = 0x8000000000ULL,
    Foo2 = 0x4000000000ULL,
    Foo3 = 0x2000000000ULL,
    FooWithA1 = 0x1000000000ULL,
    FooWithA2 = 0x0800000000ULL,
    FooWithA3 = 0x0400000000ULL,
    FooWithA4 = 0x0200000000ULL,
    FooWithB1 = 0x0100000000ULL,
    FooWithB2 = 0x0080000000,
    FooWithB3 = 0x0040000000
};
%}

您可以在构建时使用 -debug-tmsearch 标志查看 SWIG 类型映射匹配搜索。请注意,它正在搜索 int Foo1 而不是 enum FooPresencelong long Foo1:

C:\test>swig -c++ -python -debug-tmsearch test.i
test.i(8) : Searching for a suitable 'consttab' typemap for: int Foo1
  Looking for: int Foo1
  Looking for: int
  Looking for: SWIGTYPE Foo1
  Looking for: SWIGTYPE
  None found
test.i(8) : Searching for a suitable 'constcode' typemap for: int Foo1
  Looking for: int Foo1
  Looking for: int
  Using: %typemap(constcode) int
...

演示:

>>> import test
>>> hex(test.Foo1)
'0x8000000000'
>>> hex(test.FooWithA1)
'0x1000000000'