Is it possible to use X-Macro with std::variant (or with template in general)?

187 Views Asked by At

I hope to do the following using X-macro with c++17, but since template parameter does not support trailing comma, it does not work for the std::variant part. Is there someway around it?

#define LIST_OF_TYPES(X) \
  X(Type1)               \
  X(Type2)               \
  X(Type3)

#define MAKE_TYPE(name) class name {};
LIST_OF_TYPES(MAKE_TYPE)
#undef MAKE_TYPE

std::variant<
#define MAKE_VARIANT(name) name,
LIST_OF_TYPES(MAKE_VARIANT)
#undef MAKE_VARIANT
>
2

There are 2 best solutions below

0
HolyBlackCat On BEST ANSWER

Yes, there's a workaround:

#define EMPTY(...)
#define IDENTITY(...) __VA_ARGS__
#define IDENTITY2(...) __VA_ARGS__

std::variant<
#define MAKE_VARIANT(name) (,) name IDENTITY
IDENTITY2(EMPTY LIST_OF_TYPES(MAKE_VARIANT) () )
#undef MAKE_VARIANT
>

Without IDENTITY2(...) this expands to EMPTY(,) Type1 IDENTITY(,) Type2 IDENTITY(,) Type3 IDENTITY(). IDENTITY2 forces it to expand again, this time to Type1, Type2, Type3.


Or, with my own macro looping library:

run on gcc.godbolt.org

#include <macro_sequence_for.h>

#define LIST_OF_TYPES (Type1)(Type2)(Type3)

#define DECLARE_CLASSES(seq) SF_FOR_EACH(DECLARE_CLASSES_BODY, SF_NULL, SF_NULL,, seq)
#define DECLARE_CLASSES_BODY(n, d, x) class x {};

#define MAKE_VARIANT(seq) std::variant<SF_FOR_EACH(MAKE_VARIANT_BODY, USE_COMMA, SF_NULL, EMPTY, seq)>
#define MAKE_VARIANT_BODY(n, d, x) d() x
#define EMPTY(...)
#define COMMA(...) ,
#define USE_COMMA(n, d, x) COMMA

DECLARE_CLASSES(LIST_OF_TYPES) // class Type1 {}; class Type2 {}; class Type3 {};
MAKE_VARIANT(LIST_OF_TYPES) // std::variant<Type1, Type2, Type3>

Slightly more verbose, but more readable in my taste.

Here, #define MAKE_VARIANT_BODY(n, d, x) d() x is called for each element, with x being the element, and d initially set to EMPTY (4th argument of SF_FOR_EACH()). After the first (and any subsequent) iteration, d is reassigned to USE_COMMA(...) (aka COMMA), so starting from the second iteration d() expands to , instead of .

0
user17732522 On

Put the comma at the beginning and add a first element of some kind, whether it is relevant or not. One possibility:

template<template<typename...> class Tmpl, typename... Ts>
using specialization = Tmpl<Ts...>;

using my_variant = specialization<std::variant
    #define MAKE_VARIANT(name) ,name
    LIST_OF_TYPES(MAKE_VARIANT)
    #undef MAKE_VARIANT
>;