MrSpartan
MrSpartan

Reputation: 37

What is this define declaration?

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

Answers (6)

bruno
bruno

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 :

  • first in the form 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)
  • then each 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

Bodo
Bodo

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

Lundin
Lundin

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

Loc Tran
Loc Tran

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

Govind Parmar
Govind Parmar

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

Gunther Schulz
Gunther Schulz

Reputation: 625

enum mxt_object_type {
  T0 = 0,
  T1 = 1,
  T2 = 2,
  T3 = 3,
  T4 = 4,
  T5 = 5,
};

Try googleing XMACROs

Upvotes: 0

Related Questions