Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting preprocessor macros

I have this macro code, which allows me to define both a C enum and a list of the enumerated names as strings using one construct. It prevents me from having to duplicate enumerator names (and possibly introducing errors for large lists).

#define ENUM_DEFINITIONS(F) \
  F(0, Item1) \
  F(5, Item2) \
  F(15, Item3) \
  ...
  F(63, ItemN)

then:

enum Items {
  #define ITEM_ENUM_DEFINE(id, name) name = id,
    ENUM_DEFINITIONS(ITEM_ENUM_DEFINE)
  #undef ITEM_ENUM_DEFINE

which, when expanded, should produce:

enum Items {
  Item1 = 0,
  Item2 = 5,
  Item3 = 15,
  ...
  ItemN = 63,
}

In the implementation file, I have this code:

const char* itemNames[TOTAL_ITEMS];
int iter = 0;

#define ITEM_STRING_DEFINE(id, name) itemNames[iter++] = #name;
  ENUM_DEFINITIONS(ITEM_STRING_DEFINE)
#undef ITEM_STRING_DEFINE

which, when expanded, produces:

itemNames[iter++] = "Item1";
itemNames[iter++] = "Item2";
itemNames[iter++] = "Item3";
...
itemNames[iter++] = "ItemN";

I'd like to know how many enumerator items I've created in this fashion and be able to pass it to compile-time arrays. In the example above, this would be determining that TOTAL_ITEMS = N at compile-time. Is it possible to count macro invocations in this way?

I've seen mention of a non-standard COUNTER macro, similar to the FILE and LINE macros, but I'm hoping there is a more standard way.

Would also be interested in hearing if there is a better way to achieve this without having to use macros.

like image 524
Eyal Avatar asked Dec 28 '11 23:12

Eyal


People also ask

Are macros removed after preprocessing?

Explanation: True, After preprocessing all the macro in the program are removed.

What does ## do in macros?

The '##' pre-processing operator performs token pasting. When a macro is expanded, the two tokens on either side of each '##' operator are combined into a single token, which then replaces the '##' and the two original tokens in the macro expansion.

What does ## mean in C macro?

The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.


4 Answers

You can use the same technique to count the invocations.

enum itemscounter  { 
  #define ITEM_ENUM_DEFINE(id, name) name##counter,
    ENUM_DEFINITIONS(ITEM_ENUM_DEFINE) 
  #undef ITEM_ENUM_DEFINE 
 TOTAL_ITEMS
};
like image 69
Raymond Chen Avatar answered Oct 12 '22 06:10

Raymond Chen


The following should work:

#define ITEM_STRING_DEFINE(id, name) #name, // note trailing comma
const char *itemNames[] = {
  ENUM_DEFINITIONS(ITEM_STRING_DEFINE)
};

#define TOTAL_ITEMS (sizeof itemNames / sizeof itemNames[0])

Edit: Thank you to Raymond Chen for noting we don't have to worry about the unnecessary final comma in the list. (I had been misremenbering the problem for enums with strict C89 compilers, as in Is the last comma in C enum required?.)

like image 22
Joseph Quinsey Avatar answered Oct 12 '22 06:10

Joseph Quinsey


Would also be interested in hearing if there is a better way to achieve this without having to use macros.

You could always use a scripting language such as ruby or python to generate .c and .h files for you. If you do it well, you can integrate your script into your Makefile.

like image 22
David Grayson Avatar answered Oct 12 '22 06:10

David Grayson


I know this isn't a complete answer. You can create a macro around something like this.

#include <stdio.h>

const char * array[] = {
  "arr1", "arr2", "arr3", "arr4"
};

int main (int argc, char **argv)$
{
  printf("%d\n", sizeof(array)/sizeof(const char *));
}

If you can modify your enum so it has continous elements you can do sth like this (from Boost)

enum { A=0,B,C,D,E,F,N };
const char arr[N]; // can contain a character for each enum value
like image 1
Alexander Oh Avatar answered Oct 12 '22 08:10

Alexander Oh