Search code examples
c++cx-macros

define anonymous enum with x-macros produces compilation error


Im working on a big project and I have a lot of errno macros.
I want to write a helper functions for the logger that stringify each of these errno to a string. i decided to use x-macros but Im getting compilation errors

in the first place the code was like this:

// project_errno.h
#define PROJECT_ERR_KEY_FAILURE                         12222
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
#define PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY             14004

the way i sort it out is as the following:

  • In a different file i places the x-macros:
// project_errno.hx
PROJECT_ERR_FUNC(PROJECT_ERR_KEY_FAILURE)                         12222
PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING)                 12345
PROJECT_ERR_FUNC(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY)             14004
  • then I turned it into an enum:
// project_errno.h
enum {
#define PROJECT_ERR_FUNC(name, value) name=value,
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};
  • then i added a function that will be used by the logger:
// logging.h (declaration) and (definition) logging.c
const char* stringify_errno(int errno) {
    switch (errno) {
#define PROJECT_ERR_FUNC(name, value) case name: return #value ;
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
    }
}

So, looks pretty good, but i can't get it to compile, Im getting the following compilation errros:

project_errno.h:8:53: error: error: expected identifier before numeric constant
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
                                                        ^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)

project_errno.h:8:53: error: error: expected ‘}’ before numeric constant 
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
                                                        ^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)

project_errno.h:8:53: error: expected unqualified-id before numeric constant 
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
                                                        ^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)
                  ^~~~~~~~~~~~~~~~~~~~~~~
In file included from ......../project_errno.h:20:1: error: expected declaration before ‘}’ token
};
^

..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)
                  ^~~~~~~~~~~~~~~~~~~~~~~
    

I can't understand why im getting those errors (im getting the same error message multiple time in the same compilation session), and i hope you guys could help me. Also, if you have any other solution to solve the problem i intended to solve in the first place (using the errno macros and add a functions to stringify those errnos whenever Im adding an errno to the project [in only one place]), i'd love to hear about it Thanks


Solution

  • I'd follow the recipe shown in the Wikipedia page about X Macros:

    Implementation

    An X macro application consists of two parts:

    1. The definition of the list's elements.
    2. Expansion(s) of the list to generate fragments of declarations or statements.

    The list is defined by a macro or header file (named, LIST) which generates no code by itself, but merely consists of a sequence of invocations of a macro (classically named X) with the elements' data. Each expansion of LIST is preceded by a definition of X with the syntax for a list element. The invocation of LIST expands X for each element in the list.

    In particular the second example, the one with X macro as argument

    Pass name of the worker macro into the list macro. This both avoids defining an obscurely named macro (X), and alleviates the need to undefine it.

    Which, in OP's use case, leads to the following three files:

    // project_errno.h
    #ifndef PROJECT_ERRNO_H
    #define PROJECT_ERRNO_H
    
    #define FOR_EACH_ERR_ID_VALUE_PAIR(DO) \
        DO(PROJECT_ERR_KEY_FAILURE,             12222) \
        DO(PROJECT_ERR_CIPHER_ZERO_PADDING,     12345) \
        DO(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY, 14004)
    
    #define DEFINE_ENUM_ITEM(err, value) err = value,
    enum project_errs {
        FOR_EACH_ERR_ID_VALUE_PAIR( DEFINE_ENUM_ITEM )
    };
    #undef DEFINE_ENUM_ITEM
    
    #endif
    
    // logging.h
    #ifndef LOGGING_H
    #define LOGGING_H
    
    const char* stringify_errno(int errno);
    
    #endif
    
    // logging.c
    #include <stdio.h>
    #include "project_errno.h"
    
    #define STRINGIFY_ERR_VALUE_NAME(name, value) case name: \
        return "[" #value "] " #name;
     
    const char* stringify_errno(int errno)
    {
        switch (errno) {
            FOR_EACH_ERR_ID_VALUE_PAIR(STRINGIFY_ERR_VALUE_NAME)
            default:
                return "[-----] UNKWOWN";    
        }
    }
    
    #undef STRINGIFY_ERR_VALUE_NAME
    

    Testable here: https://wandbox.org/permlink/aNJCI7lQihkFnYzp