Reputation: 37
I saw this code in an application but I don't recognize the syntax. I have to make an equivalent in C++ but this code is in C.
How can make this run in a C++ application? What's the name of this kind of define? This is the same that define every constant on the list?
/*! Object types */
#define F_ENUM(x, y) x = y,
#define F_SWITCH(x, y) case x: return ( #x );
#define OB_LIST(f) \
f(T0, 0) \ //this declaration has no storage class or type specifier
//identifier "T0" is undefined
f(T1, 1) \ //expected a ")"
//unrecognized token
//expected a ";"
f(T2, 2) \
f(T3, 3) \
f(T4, 4) \
f(T5, 5)
enum mxt_object_type {
OB_LIST(F_ENUM)
};
Moreover when I compile this in a C++ complier, I get errors like this:
this declaration has no storage class or type specifier
identifier "T0" is undefined
expected a ")"
unrecognized token
expected a ";"
They are marked on the code. What do these errors mean?
Upvotes: 0
Views: 222
Reputation: 32594
When you want to see what is the result after the processing just ask to see it, with gcc/g++ the options is -E
, if I do on your code the result is :
pi@raspberrypi:/tmp $ gcc -E d.c
# 1 "d.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "d.c"
# 13 "d.c"
enum mxt_object_type {
T0 = 0, T1 = 1, T2 = 2, T3 = 3, T4 = 4, T5 = 5,
};
Of course here F_SWITCH
is not used
i have to make an equivalent in C++ but this code is in C
You have nothing to do, that does the same in C++
These macros are for the preprocessor, not for C or C++ by themselves who see the result I shown at the beginning
Example of compilation in C++ :
pi@raspberrypi:/tmp $ cat d.cc
/*! Object types */
#define F_ENUM(x, y) x = y,
#define F_SWITCH(x, y) case x: return ( #x );
#define OB_LIST(f) \
f(T0, 0) \
f(T1, 1) \
f(T2, 2) \
f(T3, 3) \
f(T4, 4) \
f(T5, 5)
enum mxt_object_type {
OB_LIST(F_ENUM)
};
pi@raspberrypi:/tmp $ g++ -c -pedantic -Wall d.cc
pi@raspberrypi:/tmp $
How that works :
OB_LIST(F_ENUM)
OB_LIST
is expanded producing F_ENUM(T0, 0) F_ENUM(T1, 1) F_ENUM(T2, 2) F_ENUM(T3, 3) F_ENUM(T4, 4) F_ENUM(T5, 5)
F_ENUM
are expanded producing the final result T0 = 0, T1 = 1, T2 = 2, T3 = 3, T4 = 4, T5 = 5,
Classical use of F_SWITCH
:
const char * nameIt(mxt_object_type v)
{
switch (v) {
OB_LIST(F_SWITCH)
default:
return "unknown";
}
}
that produces :
const char * nameIt(mxt_object_type v)
{
switch (v) {
case T0: return ( "T0" ); case T1: return ( "T1" ); case T2: return ( "T2" ); case T3: return ( "T3" ); case T4: return ( "T4" ); case T5: return ( "T5" );
default:
return "unknown";
}
}
the strings like "T0"
are produced by #x
who place x in a literal string after its substitution by its value
Upvotes: 4
Reputation: 9895
If you add code like this
const char * enum2str(enum mxt_object_type type)
{
switch(type) {
OB_LIST(F_SWITCH)
}
return "invalid";
}
you will get a function that translates enum
values to strings without duplicating all enum values. This is useful for output, e.g.
enum mxt_object_type type = T2;
printf("type is %s\n", enum2str(type));
Sample code:
$ cat enum.c
/*! Object types */
#define F_ENUM(x, y) x = y,
#define F_SWITCH(x, y) case x: return ( #x );
#define OB_LIST(f) \
f(T0, 0) \
f(T1, 1) \
f(T2, 2) \
f(T3, 3) \
f(T4, 4) \
f(T5, 5)
enum mxt_object_type {
OB_LIST(F_ENUM)
};
const char * enum2str(enum mxt_object_type type)
{
switch(type) {
OB_LIST(F_SWITCH)
}
return "invalid";
}
Preprocessor output:
$ gcc -E enum.c
# 1 "enum.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "enum.c"
# 13 "enum.c"
enum mxt_object_type {
T0 = 0, T1 = 1, T2 = 2, T3 = 3, T4 = 4, T5 = 5,
};
const char * enum2str(enum mxt_object_type type)
{
switch(type) {
case T0: return ( "T0" ); case T1: return ( "T1" ); case T2: return ( "T2" ); case T3: return ( "T3" ); case T4: return ( "T4" ); case T5: return ( "T5" );
}
return "invalid";
}
Upvotes: 0
Reputation: 215330
This is known as "X macros", which is about making a list of constants of any kind and centralize maintenance of that list to a single place in the source code, to avoid code repetition. At the cost of readability.
In this case the OB_LIST
, which is apparently a list of enum names and their corresponding values.
Normally it goes like this (hence the name "X macros"):
#define OB_LIST \
/* type value */ \
X(T0, 0) \
X(T1, 1) \
X(T2, 2) \
X(T3, 3) \
X(T4, 4) \
X(T5, 5) \
enum mxt_object_type {
#define X(type, value) type = value,
OB_LIST
#undef X
};
Where type = value,
is what I want each line in my enum declaration to look like. When the macro OB_LIST
gets expanded, this "X macro" is then expaned for all values in the list, with the values specified.
After pre-processing you end up with:
enum mxt_object_type {
T0 = 0,
T1 = 1,
...
};
You can then write similar X macros for every piece of code repeating all these values. Think of it as a manner of loop unrolling, but done at compile-time.
The person why wrote your code has taken this practice/obfuscation one step further by not declaring the X macro locally, but instead centralizing them and passing the specific behavior on to the list of objects.
I wouldn't call that good practice, since you end up with a lot of weird macros in a header somewhere, taken away from any context.
Upvotes: 2
Reputation: 1180
It’s using macro definition for defining another enumerated type. F_ENUM is a macro define each item in enum. F_SWICH is a macro will be used in switch statement. This technique is used for avoiding duplicating code
Upvotes: 0
Reputation: 21572
Well, if you preprocess that code out to a file, you get:
enum mxt_object_type {
T0 = 0, T1 = 1, T2 = 2, T3 = 3, T4 = 4, T5 = 5,
};
So this is a set of macros for creating an enum
with member names T#
.
Upvotes: 0
Reputation: 625
enum mxt_object_type {
T0 = 0,
T1 = 1,
T2 = 2,
T3 = 3,
T4 = 4,
T5 = 5,
};
Try googleing XMACROs
Upvotes: 0