Search code examples
c++cc-preprocessor

Checking emptiness of X macros


I am trying to build a X macro that would allow to generate an enum with a bunch of useful functions like .toString() for instance. The version I got can be implemented like this:

#define MAKE_ENUM \
  ENUM_NAME( LanguageId ) \
  ENUM_TYPE( unsigned int ) \
  ENUM_ENTRY( English, 0 )\
  ENUM_ENTRY( French ) \
  ENUM_ENTRY( German ) \
  ENUM_ENTRY( Italian ) \
  ENUM_ENTRY( Spanish ) \
  ENUM_ENTRY( Polish ) \
  ENUM_ENTRY( Korean, 128 ) \
  ENUM_ENTRY( ChineseTraditional, 129 ) \
  ENUM_ENTRY( ChineseSimplified, 130 ) \
  ENUM_ENTRY( Japanese, 131 )
#include "GenericToolbox.MakeEnum.h"
#undef MAKE_ENUM

The deploying of the X macro MAKE_ENUM is done under the header GenericToolbox.MakeEnum.h.

This version works so far as expected. However I want ENUM_TYPE to be an optional argument, therefore sometimes it wouldn't be set by the user and the enum would be defined as the default int.

For that I need to figure out if the unfolding of the MAKE_ENUM is empty. The version I came up with is such:

#define TEMP_EXPAND(x) x
#define TEMP_SELECT_THIRD(_1,_2,num,...) num
#define TEMP_IS_NOT_EMPTY_IMPL(...) TEMP_EXPAND(TEMP_SELECT_THIRD(__VA_ARGS__,1,0))
#define TEMP_ARGS_DUMMY( ... ) dummy,##__VA_ARGS__
#define TEMP_IS_NOT_EMPTY( val ) TEMP_IS_NOT_EMPTY_IMPL(TEMP_ARGS_DUMMY( val ))

// [...]

#define ENUM_TYPE(type_) type_ // MAKE_ENUM will only return type_ if it exists
#if TEMP_IS_NOT_EMPTY( MAKE_ENUM ) == 1
  typedef MAKE_ENUM EnumType;
#else
  typedef int EnumType;
#endif

// [...]

The issue is that it seems the TEMP_IS_NOT_EMPTY macro only works with Clang but not GCC. Do you have any idea why? And would there be a viable workaround?

To clarify the issue, I've added a test withing the code:

#define MAKE_ENUM_EMPTY
#if TEMP_IS_NOT_EMPTY( MAKE_ENUM_EMPTY ) == 1
// NOT empty
#warning "TEMP_IS_NOT_EMPTY does not work correctly for empty macro"
#else
// empty
#endif

#define MAKE_ENUM_NOT_EMPTY unsigned int
#if TEMP_IS_NOT_EMPTY( MAKE_ENUM_NOT_EMPTY ) == 1
// NOT empty
#else
// empty
#warning "TEMP_IS_NOT_EMPTY does not work correctly for NOT empty macro"
#endif

With GCC I get the warning message: #warning "TEMP_IS_NOT_EMPTY does not work correctly for empty macro"

Cheers!


Solution

  • Check out the following design:

    #define ENUM_NAME  LanguageId
    #define ENUM_TYPE  unsigned int
    #define ENUM_FIELDS \
      ENUM_ENTRY( English, 0 )\
      ENUM_ENTRY( French ) \
      ENUM_ENTRY( German ) \
      ENUM_ENTRY( Italian ) \
      ENUM_ENTRY( Spanish ) \
      ENUM_ENTRY( Polish ) \
      ENUM_ENTRY( Korean, 128 ) \
      ENUM_ENTRY( ChineseTraditional, 129 ) \
      ENUM_ENTRY( ChineseSimplified, 130 ) \
      ENUM_ENTRY( Japanese, 131 )
    #include "GenericToolbox.MakeEnum.h"
    

    Then:

    // "GenericToolbox.MakeEnum.h"
    #ifndef ENUM_TYPE
    // default:
    #define ENUM_TYPE int
    #endif
    typedef ENUM_TYPE ENUM_NAME;
    // process ENUM_FIELDS ...
    #undef ENUM_TYPE
    #undef ENUM_FIELDS
    #undef ENUM_NAME
    

    Fun fact: this is the design that STC library is using.