I have a C++ code base which uses SWIG to generate Python 3 interfaces.
I had a problem where I couldn't convert enum
values into large enough types. This was solved with good help.
Now I have a new problem that relates to the other one. Methods that take the corrected enum
values as arguments are throwing an OverflowError
when the largest enum
values are passed in as an argument.
I was wondering if there is a generic way to solve this using the typemap
function (like the way with the enum objects). To identify all methods that take the enum
constants as arguments. Or do I need to define an in
typemap
for each of the enum
types?
What I have tried, and works, is including this in the .i
file:
%include "typemaps.i"
%apply unsigned long long { doom::Bar::FooPresence };
But it would be great if there was a "catch all" kind of thing, like with the constcode
typemap.
The code necessary to reproduce the current behavior:
bar.h
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
bar.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;
}
bar.i
%module bar
%feature ("flatnested");
%{
#define SWIG
#include "bar.h"
#define SWIG_PYTHON_STRICT_BYTE_CHAR
%}
%typemap(constcode) int %{
SWIG_Python_SetConstant(d, "$symname", PyLong_FromLongLong(static_cast<long long>($1)));
%}
%rename("Bar_%s", %$isnested) "";
%include "bar.h"
test_bar.py
import bar
b = bar.Bar()
b.setVesODee(10, bar.Bar.Foo1)
build-and-test.sh
#!/bin/bash
set -e
echo "Cleanup..."
rm -rf __pycache__
rm -f _bar.so bar.o bar_wrap.*
echo "Building..."
swig -python -c++ -py3 -debug-tmsearch 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
echo "Testing..."
python3 test_bar.py
output
Traceback (most recent call last):
File "test_bar.py", line 15, in <module>
b.setVesODee(10, bar.Bar.Foo1)
File "/workspace/ctmp/bar.py", line 83, in setVesODee
return _bar.Bar_setVesODee(self, ves, pr)
OverflowError: in method 'Bar_setVesODee', argument 3 of type 'doom::Bar::FooPresence'
SWIGTYPE
is the default type match when a type-specific match is not found. You can use the following to apply to all enumerations:
%apply unsigned long long { enum SWIGTYPE };
See 13.3.3 Default typemap matching rules in the SWIG documentation.
Example:
test.i
%module test
%typemap(constcode) int %{SWIG_Python_SetConstant(d, "$1",PyLong_FromLongLong(static_cast<long long>($1)));%}
%apply unsigned long long { enum SWIGTYPE };
%{
#include <iostream>
%}
%inline %{
enum FooPresence : unsigned long long
{
Foo1 = 0x8000000000ULL,
};
void func(FooPresence x) {
std::cout << std::hex << static_cast<unsigned long long>(x) << std::dec << std::endl;
}
%}
Demo:
>>> import test
>>> test.func(test.Foo1)
8000000000