问题
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_STOP
orEXPAND_MORE
, depending on whether or not the__VA_ARGS__
is empty during this evaluation. This selection takes place because SWIG evaluates#define STOP(...) MORE
as
MORE
when there are arguments, but simply as STOP when there are no arguments.The
TOKEN
macro then adds the required indirection for the token paste operator to work.There's a fake first argument to both
EXPAND_MORE
andEXPAND_STOP
that prevents the same behaviour from stopping us eating the call toEXPAND_STOP
with 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