TL; DR - можно ли реализовать шаблон генерации макроса с использованием шаблонов?
У меня есть код, который использует классы C ++ std :: error_code и error_category.Я обнаружил, что как только количество кодов ошибок начало расти, количество написанного для каждого из них шаблона также росло очень быстро.
Чтобы обойти это, я написал несколько макросов, которые должны статически проверять и генерировать большую часть шаблонов из того, что нас действительно волнует - коды enum и прикрепленные к ним сообщения.Эти макросы принимают перечисление и сообщения в виде const std :: map.
Мой вопрос - можно ли заменить этот тип генерации шаблонов каким-либо шаблоном?Прямо сейчас, если это не сработало, совет goto для других был «проверять static_asserts», что делает его немного PITA для использования.
Если его нельзя просто заменить шаблонами, можно ли добавить код для улучшения вывода неудачной компиляции, чтобы их было менее болезненно использовать?Сейчас неудачная компиляция выводит как статические утверждения, так и множество других ненужных выходных данных.
Я включил ниже некоторый код, который демонстрирует макросы - я удалил весь наш связанный с пространством имен код, так что он может быть немного неправильным, но должен продемонстрировать цель достаточно хорошо.
//C++14 definition - we are using C++11
template< bool B, class T = void >
using enable_if_t = typename std::enable_if<B,T>::type;
//Generic template test for any other type
template <typename T, typename = void>
struct is_std_map : std::false_type {};
//Specialised test for a std::map type
template <typename T>
struct is_std_map<T, enable_if_t<
std::is_same<typename T::value_type,
std::pair<const typename T::key_type,
typename T::mapped_type>
>::value>
> : std::true_type {};
#define MAKE_ERROR_CODE_CATEGORY(EC, EC_MESSAGE_MAP) \
/* Check that we have an enum type as the first arg, and a const std::map arg for the second */ \
static_assert(std::is_enum<EC>::value, "!"); \
static_assert(std::is_const<decltype(EC_MESSAGE_MAP)>::value, "!" ); \
static_assert(is_std_map<decltype(EC_MESSAGE_MAP)>::value, "!"); \
/* Validate that the non-const types for our EC and our EC_MESSAGE_MAP are as expected*/ \
static_assert(std::is_same< \
std::remove_const<EC>::type, \
std::remove_const<decltype(EC_MESSAGE_MAP)::key_type \
>::type>::value, \
"!"); \
static_assert(std::is_same< \
std::remove_const<std::string>::type, \
std::remove_const<decltype(EC_MESSAGE_MAP)::mapped_type \
>::type>::value, \
"!"); \
/*Generates a standardised category for the provided EC */ \
struct EC## _category : std::error_category \
{ \
const char* name() const noexcept override \
{ \
return #EC ; \
} \
std::string message(int c) const override \
{ \
EC code = static_cast<EC>(c); \
auto itr = EC_MESSAGE_MAP.find(code); \
if (itr != EC_MESSAGE_MAP.end()) \
{ \
return itr->second; \
} \
else \
{ \
return "(unrecognized error)"; \
} \
} \ \
};
namespace std \
{ \
template <> \
struct is_error_code_enum< EC > : true_type {}; \
} \
/* Declare a global function returning a static instance of the custom category */ \
const EC## _category& EC## _category_generator() \
{ \
static EC## _category c; \
return c; \
} \
/* Adds the standard error code construction call */ \
inline std::error_code make_error_code(EC e) \
{ \
return {static_cast<int>(e), EC## _category_generator()}; \
}