We use one big enum for message passing and there are optional parts of our code base that use specific messages that really only need to be added to the enum if those parts are to be compiled in. Is there a way to define a macro that could do this or something similar? I would like to be able to just add REGISTER_MSG(MESSAGE_NAME); to the beginning of a file in the optional code. I guess the real problem is that macros only replace the code at the location they are written.
views:
96answers:
5Indeed, you can't have macros modifying code in other compilation units. And the enum should be the same for all the compilation units.
(I wouldn't probably use enum with automatically assigned value for identifying messages in a protocol, BTW.)
You could do something like this with some ugly template hacks:
#define REGISTER_MSG(MESSAGE_NAME) \
struct tag_##MESSAGE_NAME { }; \
static const int &MESSAGE_NAME = assign_message_id<tag_##MESSAGE_NAME>::value;
template<typename Tag>
struct assign_message_id {
static const int value;
};
// should be compiled only once; don't put the definition in a header
int next_message_id() {
static int id = 0;
return id++;
}
template<typename Tag>
const int assign_message_id<Tag>::value = next_message_id();
However, since these are not constant, they could not be used in a switch
. Additionally, they could change from compile to compile - or even from one run to the next.
Another option would be to write a script to just scan your entire codebase for these REGISTER_MSG tags, and emit a header based on them. Then you'd just need:
#define REGISTER_MSG(MESSAGE_NAME) /* no-op */
I would rethink this design, but if you really want to do it this way you can construct the enum header at compile time using Make.
BigEnum.h: # First put in everything up to the opening brace, cat BigEnumTop > $@ # then extract all of the message names and add them (with comma) to BigEnum.h, cat $(OPTIONAL_SOURCES) | grep REGISTER_MSG | sed 's/.*(\(.*\))/\1/' >> $@ # then put in the non-optional message names, closing brace, and so on. cat BigEnumBottom >> $@
Easy. Put a #include in the enum, and tweak the include paths so that the appropriate file is included.
enum myenum
{
x,
y,
#include "enum_extender.h"
}
Variant of the Makefile solution:
# BigEnum.h #includes BigEnum.inc
BigEnum.h: BigEnum.inc
BigEnum.inc:
# Extract all of the message names for inclusion (with comma) in BigEnum.h
cat $(OPTIONAL_SOURCES) | grep REGISTER_MSG | sed 's/.*(\(.*\))/\1/' >> $@
The advantages are that support tooling will see a reasonable BigEnum.h with at least some enum values, and the source code repository will contain a single file instead of a seperate header and footer.
You obviously need to have a single definition of BigEnum throughout your code, or else you might violate the ODR. Also consider what happens if your "optional" value happens to be the 256th or 65536th in an enum.