0

I am using the following smart enum structure:

/* MySmartEnum.hpp */

#pragma once

#include <array>

template <class E>
static const auto& value_string_pairs();

#define MY_EXPAND(x) x
#define MY_ARRAY_PAIR(name, x) { #x, name::x }

#define MY_STR_CONCAT_(a, ...) a##__VA_ARGS__
#define MY_STR_CONCAT(a, ...) MY_STR_CONCAT_(a, __VA_ARGS__)

#define MY_ARG_COUNT_(_1,_2,_3,_4,VAL,...) VAL
#define MY_ARG_COUNT(...) MY_EXPAND(MY_ARG_COUNT_(__VA_ARGS__,4,3,2,1))

#define MY_LOOP_1(f, n, x)      f(n,x)
#define MY_LOOP_2(f, n, x, ...) f(n,x) , MY_EXPAND(MY_LOOP_1(f, n, __VA_ARGS__))
#define MY_LOOP_3(f, n, x, ...) f(n,x) , MY_EXPAND(MY_LOOP_2(f, n, __VA_ARGS__))

#define MY_ENUM_IMPL(name, loop_func_name, ...)                                             \
    template<> inline const auto& value_string_pairs<name>() {                              \
        using element_t = std::pair<const char*, name>;                                     \
        static const std::array<element_t, MY_ARG_COUNT(__VA_ARGS__)> values = { element_t  \
            MY_EXPAND(loop_func_name(MY_ARRAY_PAIR, name, __VA_ARGS__)) };                  \
        return values; }

#define MY_ENUM_CLASS(name, ...) enum class name { __VA_ARGS__ }; \
    MY_ENUM_IMPL(name, MY_STR_CONCAT(MY_LOOP_, MY_ARG_COUNT(__VA_ARGS__)), __VA_ARGS__)

Then I maintain an additional file with all the enum types:

/* Enums.hpp */

#pragma once

#include "MySmartEnum.hpp"

MY_ENUM_CLASS(Timespan, Week, Month, Year)
MY_ENUM_CLASS(Interpolation, Linear, Cubic)

For example MY_ENUM_CLASS(Timespan, Week, Month, Year) expands to

enum class Timespan { Week, Month, Year };

template<>
inline const auto& value_string_pairs<Timespan>() {
    using element_t = std::pair<const char*, Timespan>;
    static const std::array<element_t, 3> values = { element_t
        { "Week",  Timespan::Week  },
        { "Month", Timespan::Month },
        { "Year",  Timespan::Year  } };
    return values;
}

So right now, everything is implemented in the header. But I want to move the implementation to an Enums.cpp file. Firstly, I can easily change MY_ENUM_CLASS(...) to only define the enum & function signature in Enums.hpp:

#define MY_ENUM_CLASS(name, ...) enum class name { __VA_ARGS__ };  \
    template<> const auto& value_string_pairs<name>();

But how do I move the implementation of value_string_pairs<Timespan>() into Enums.cpp, but without having to list all enum values again? Is there a way to #include "Enums.hpp" in Enums.cpp, un-define MY_ENUM_CLASS and then re-define MY_ENUM_CLASS inclusive of MY_ENUM_IMPL such that MY_ENUM_CLASS(Timespan, Week, Month, Year) expands to the full function implementation (but only in Enums.cpp)?

Phil-ZXX
  • 2,359
  • 2
  • 28
  • 40
  • 4
    All of this macro-based, tangled hairball should simply be replaced by plain C++. Trying to be smart in C++ often results in code that's too smart for anyone else to understand. – Sam Varshavchik Aug 15 '22 at 11:47
  • 3
    @SamVarshavchik As long as we don't have reflection in C++, macros are the best way to produce string-to-enum conversion. Writing it manually is prone to typos. – Yksisarvinen Aug 15 '22 at 11:52
  • 2
    You could use an [x macro](https://en.wikipedia.org/wiki/X_Macro) idiom, to have different expansions for different contexts (in this case, header and source). – Eljay Aug 15 '22 at 12:25
  • "*So right now, everything is implemented in the header. But I want to move the implementation to an `Enums.cpp` file*" - just be careful, because [you can't split a template between `.h`/`.cpp` files](https://stackoverflow.com/questions/495021/). Not sure if that includes template specializations or not, though. – Remy Lebeau Aug 15 '22 at 15:45

0 Answers0