How to get reflected functionality in C, without x macros

In connection with this question about software engineering about the simple serialization of various structural data on demand , I found an article that uses x-macros to create the structure metadata necessary for "out fields" structuring the structure. I also saw similar methods for smart enumerations, but it comes down to the same principle, getting a string representation of the enumeration or the value of a structure field by its name or something like that.

However, experienced C programmers in mode argue that x-macros should be avoided as a "last resort":

Perhaps I could find many more related streams, but unfortunately I did not bookmark them, so these are just some of the google foo.

Perhaps the correct answer is something like protocol buffers ? But why create a structure definition in another language (definitions .proto), and then run the build step to generate C files, is it preferable to use the built-in preprocessor for the same thing? And the problem is that these methods still do not allow me to get one structure by name, I have to use the same definition between two projects and keep them in sync.

, : x- "", ( ) " " -, ?

+4
2

, Boost, .

, .


-, . :

ReflEnum(MyEnum,
    (first)
    (second , 42)
    (third)
)

:

enum MyEnum
{
    first,
    second = 42,
    third,
};

const char *EnumToString_MyEnum(enum MyEnum param)
{
    switch (param)
    {
      case first:
        return "first";
      case second:
        return "second";
      case third:
        return "third";
      default:
        return "<invalid>";
    }
}

, :

#include <stdio.h>

/*
 * Following is generated by the below ReflEnum():
 *   enum MyEnum {first, second = 42, third};
 *   const char *EnumToString_MyEnum(enum MyEnum value) {}
*/
ReflEnum(MyEnum,
    (first)
    (second , 42)
    (third)
)

int main()
{
    enum MyEnum foo = second;
    puts(EnumToString_MyEnum(foo));  // -> "second"
    puts(EnumToString_MyEnum(43));   // -> "third"
    puts(EnumToString_MyEnum(9001)); // -> "<invalid>"
}

.

. , Boost.

:

#define ReflEnum_impl_Item(...) PPUTILS_VA_CALL(ReflEnum_impl_Item_, __VA_ARGS__)(__VA_ARGS__)
#define ReflEnum_impl_Item_1(name)        name,
#define ReflEnum_impl_Item_2(name, value) name = value,

#define ReflEnum_impl_Case(...) case PPUTILS_VA_FIRST(__VA_ARGS__): return PPUTILS_STR(PPUTILS_VA_FIRST(__VA_ARGS__));

#define ReflEnum(name, seq) \
    enum name {PPUTILS_SEQ_APPLY(seq, ReflEnum_impl_Item)}; \
    const char *EnumToString_##name(enum name param) \
    { \
        switch (param) \
        { \
            PPUTILS_SEQ_APPLY(seq, ReflEnum_impl_Case) \
            default: return "<invalid>"; \
        } \
    }

string- > enum; , .

:

, script, . ​​ .

Boost 64, 4.

#define PPUTILS_E(...) __VA_ARGS__

#define PPUTILS_VA_FIRST(...) PPUTILS_VA_FIRST_IMPL_(__VA_ARGS__,)
#define PPUTILS_VA_FIRST_IMPL_(x, ...) x

#define PPUTILS_PARENS(...) (__VA_ARGS__)
#define PPUTILS_DEL_PARENS(...) PPUTILS_E __VA_ARGS__

#define PPUTILS_CC(a, b) PPUTILS_CC_IMPL_(a,b)
#define PPUTILS_CC_IMPL_(a, b) a##b

#define PPUTILS_CALL(macro, ...) macro(__VA_ARGS__)

#define PPUTILS_VA_SIZE(...) PPUTILS_VA_SIZE_IMPL_(__VA_ARGS__,4,3,2,1,0)
#define PPUTILS_VA_SIZE_IMPL_(i1,i2,i3,i4,size,...) size

#define PPUTILS_STR(...) PPUTILS_STR_IMPL_(__VA_ARGS__)
#define PPUTILS_STR_IMPL_(...) #__VA_ARGS__

#define PPUTILS_VA_CALL(name, ...) PPUTILS_CC(name, PPUTILS_VA_SIZE(__VA_ARGS__))

#define PPUTILS_SEQ_CALL(name, seq) PPUTILS_CC(name, PPUTILS_SEQ_SIZE(seq))

#define PPUTILS_SEQ_DEL_FIRST(seq) PPUTILS_SEQ_DEL_FIRST_IMPL_ seq
#define PPUTILS_SEQ_DEL_FIRST_IMPL_(...)

#define PPUTILS_SEQ_FIRST(seq) PPUTILS_DEL_PARENS(PPUTILS_VA_FIRST(PPUTILS_SEQ_FIRST_IMPL_ seq,))
#define PPUTILS_SEQ_FIRST_IMPL_(...) (__VA_ARGS__),

#define PPUTILS_SEQ_SIZE(seq) PPUTILS_CC(PPUTILS_SEQ_SIZE_0 seq, _VAL)
#define PPUTILS_SEQ_SIZE_0(...) PPUTILS_SEQ_SIZE_1
#define PPUTILS_SEQ_SIZE_1(...) PPUTILS_SEQ_SIZE_2
#define PPUTILS_SEQ_SIZE_2(...) PPUTILS_SEQ_SIZE_3
#define PPUTILS_SEQ_SIZE_3(...) PPUTILS_SEQ_SIZE_4
#define PPUTILS_SEQ_SIZE_4(...) PPUTILS_SEQ_SIZE_5
// Generate PPUTILS_SEQ_SIZE_i
#define PPUTILS_SEQ_SIZE_0_VAL 0
#define PPUTILS_SEQ_SIZE_1_VAL 1
#define PPUTILS_SEQ_SIZE_2_VAL 2
#define PPUTILS_SEQ_SIZE_3_VAL 3
#define PPUTILS_SEQ_SIZE_4_VAL 4
// Generate PPUTILS_SEQ_SIZE_i_VAL

#define PPUTILS_SEQ_APPLY(seq, macro) PPUTILS_SEQ_CALL(PPUTILS_SEQ_APPLY_, seq)(macro, seq)
#define PPUTILS_SEQ_APPLY_0(macro, seq)
#define PPUTILS_SEQ_APPLY_1(macro, seq) PPUTILS_CALL(macro, PPUTILS_SEQ_FIRST(seq))
#define PPUTILS_SEQ_APPLY_2(macro, seq) PPUTILS_CALL(macro, PPUTILS_SEQ_FIRST(seq)) PPUTILS_SEQ_CALL(PPUTILS_SEQ_APPLY_, PPUTILS_SEQ_DEL_FIRST(seq))(macro, PPUTILS_SEQ_DEL_FIRST(seq))
#define PPUTILS_SEQ_APPLY_3(macro, seq) PPUTILS_CALL(macro, PPUTILS_SEQ_FIRST(seq)) PPUTILS_SEQ_CALL(PPUTILS_SEQ_APPLY_, PPUTILS_SEQ_DEL_FIRST(seq))(macro, PPUTILS_SEQ_DEL_FIRST(seq))
#define PPUTILS_SEQ_APPLY_4(macro, seq) PPUTILS_CALL(macro, PPUTILS_SEQ_FIRST(seq)) PPUTILS_SEQ_CALL(PPUTILS_SEQ_APPLY_, PPUTILS_SEQ_DEL_FIRST(seq))(macro, PPUTILS_SEQ_DEL_FIRST(seq))
// Generate PPUTILS_SEQ_APPLY_i
+2

" " :

  • /, , . ( " " ). , .

  • OO. . . . ( ), . "ADT" API.

, X- , , . , , .

, #define X(dir) {dir, #dir}, , :

/*
  Create a temporary X-macro that expands the DIRECTION_LIST, to form 
  an array initialization list. The format will be:

  {north, "north"},  
  {south, "south"},
  ...
*/
#define X(dir) {dir, #dir}
  DIRECTION_LIST
#undef X
+1

Source: https://habr.com/ru/post/1683745/


All Articles