简体   繁体   中英

How to access C++ typedef'd structures in python with SWIG interface

I am trying to configure my SWIG interface to expose all defined typedefs.

Example: For the following in my C++ header file I want my python code to be able to create objects A, B, C, D, E.

//MyHeader.h
struct Common
{
    uint8_t     eId;
};
typedef Common A, B, C, D, E;

I have tested with the following structs in my header file and the objects available through the SWIG interface are Test, Test2Typedef, and Test3Typedef1, but not TestTypedef, Test2, Test3, or Test3Typedef2.

//MyHeader.h
struct Test {
    uint8_t uValue;
};
typedef Test TestTypedef;

typedef struct Test2 {
    uint8_t uValue;
} Test2Typedef;

typedef struct Test3 {
    uint8_t uValue;
} Test3Typedef1, Test3Typedef2;

I have tried adding the following typedefs to my.i file, and still cannot access TestTypedef:

//MyHeader.i
%{
#include "MyHeader.h"
typedef Test TestTypedef;
%}

typedef Test TestTypedef;
%include "MyHeader.h"

As a general rule SWIG tries to mirror the behaviour of C as closely as possible in the target language. Sometimes that's a little bit tricky though when there's no general case mapping of typedef semantics into many of the languages SWIG targets. In this particular instance though you can still achieve the behaviour you're looking for in Python, using one of two possible options. To simplify things though you're going to want to be more consistent in your header, so either always typedef the TestN structs, or never typedef them.

Firstly you could write a little bit of extra Python code, inside %pythoncode which makes sure there's an alias for every type in Python that matches what you're expecting. The following interface shows that:

%module test


%inline %{

struct Test {
    uint8_t uValue;
};
typedef Test TestTypedef;

struct Test2 {
    uint8_t uValue;
};
typedef Test2 Test2Typedef;

struct Test3 {
    uint8_t uValue;
};
 typedef Test3 Test3Typedef1, Test3Typedef2;

%}

%pythoncode %{
  TestTypedef = Test
  Test2Typedef = Test2
  Test3Typedef1 = Test3
  Test3Typedef2 = Test3
%}

The alternative approach however is to do some trickery within the C++ layer. All we have to do in reality is make sure that SWIG generate the interface we want and it's all legal, correct, compilable C++ code. It doesn't matter however if we lie to SWIG about what our C++ code is really like. So in practice if we claim that each of our typedefs is actually a derived class, but in reality they're simply typedefs then we'll still end up with a perfectly working interface from it. And as a bonus things will mostly be more typesafe in the target language which is probably good:

%module test


%{
// This is what the C++ compiler sees:    
struct Test {
    uint8_t uValue;
};
typedef Test TestTypedef;

struct Test2 {
    uint8_t uValue;
};
typedef Test2 Test2Typedef;

struct Test3 {
    uint8_t uValue;
};
typedef Test3 Test3Typedef1, Test3Typedef2;

%}

// This is the lie we tell SWIG, but it's compatible with what the C++ code really is doing
struct Test {
    uint8_t uValue;
};

struct Test2 {
    uint8_t uValue;
};

struct Test3 {
    uint8_t uValue;
};

struct Test2Typedef : Test2 {};
struct Test3Typedef1 : Test3 {};
struct Test3Typedef2 : Test3 {};

Either of these lets us run this Python code:

import test

a = test.Test3Typedef2()

If it were me doing this I'd define a macro for the typedef generation:

#ifndef SWIG
#define MAKE_TYPEDEF(original, renamed) typedef original renamed
#else
#define MAKE_TYPEDEF(original, renamed) struct renamed : original {}
#endif 

Which could live in a header file then and would allow you to use %include still.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM