问题
I've been using the REFLECTABLE macro from this answer in my C++ header file, which looks like:
#ifndef TIMER_H
#define TIMER_H
// From the linked question, but (deliberately) ignored by SWIG here,
// only relevant is as much as it defines REFLECTABLE for other preprocessors
#include "reflection.hh"
struct Timer {
REFLECTABLE (
(float) startTime,
(float) endTime,
)
};
#endif
The SWIG preprocessor doesn't follow #include and doesn't handle the definition of REFLECTABLE from the linked question anyway, so my plan is to ignore that entirely and replace it with a more appropriate SWIG specific definition of REFLECTABLE.
My goal is for the SWIG preprocessor to expand Timer as though there was nothing special going on, i.e.:
struct Timer {
float startTime;
float endTime;
};
To that end I have written a simple reflection.i that makes a recursive call to a %define SWIG macro:
#define REM(...) __VA_ARGS__
#define PAIR(x) REM x
%define EXPAND(tail, ...)
PAIR(tail);
EXPAND(__VA_ARGS__)
%enddef
#define REFLECTABLE(...) EXPAND(__VA_ARGS__)
That I can use in test.i:
%module test
%include "reflection.i"
%include "timer.h"
When we run swig -python -Wall -E test.i to inspect the result of running this through the SWIG preprocessor it almost works, but the definition at the end of recursion doesn't quite match what I'd like:
struct Timer {
/*@SWIG:reflection.i,4,EXPAND@*/
float startTime;
/*@SWIG:reflection.i,4,EXPAND@*/
float endTime;
EXPAND
};
The problem is that recursion gets terminated by the bareword EXPAND when ___VA_ARGS__ is empty. I looked at using map.h from this answer, but that also doesn't work with the SWIG preprocessor.
How can I modify my definition of REFLECTABLE so that the SWIG preprocessor just generates the flat structure I'm looking for?
回答1:
I came up with a solution in the end that required two changes:
There's more indirection to the recursion, we paste two tokens together to get the token
EXPAND_STOPorEXPAND_MORE, depending on whether or not the__VA_ARGS__is empty during this evaluation. This selection takes place because SWIG evaluates#define STOP(...) MOREas
MOREwhen there are arguments, but simply as STOP when there are no arguments.The
TOKENmacro then adds the required indirection for the token paste operator to work.There's a fake first argument to both
EXPAND_MOREandEXPAND_STOPthat prevents the same behaviour from stopping us eating the call toEXPAND_STOPwith no arguments.
#define REM(...) __VA_ARGS__
#define PAIR(x) REM x
#define TOKEN_(x,y) x##y
#define TOKEN(x,y) TOKEN_(x, y)
#define STOP(...) MORE
%define EXPAND_MORE(fake, ...)
EXPAND(__VA_ARGS__)
%enddef
#define EXPAND_STOP(fake, ...)
%define EXPAND(tail, ...)
PAIR(tail);
TOKEN(EXPAND_, STOP(__VA_ARGS__)) ## (fake, ##__VA_ARGS__)
%enddef
#define REFLECTABLE(...) EXPAND(__VA_ARGS__)
Although possibly not the most elegant solution this is tested and working with SWIG 3.0.2.
来源:https://stackoverflow.com/questions/27632244/preprocessor-for-each-witin-swig-interface