代码之家  ›  专栏  ›  技术社区  ›  Andrew Lipscomb

C++代码错误模板的模板与宏

  •  2
  • Andrew Lipscomb  · 技术社区  · 7 年前

    可以使用模板实现样板生成宏吗?

    我有一些代码,使用C++的STD::Error代码和ErrRoSype类。我发现,一旦错误代码的数量开始增长,为每个错误编写的样板文件的数量也在迅速增长。

    为了解决这个问题,我编写了一些宏,这些宏应该是静态检查的,并根据我们真正关心的东西生成大多数样板文件——枚举代码和附加到它们的消息。这些宏将枚举和消息作为const STD::MAP。

    我的问题-这种样板的一代能用某种模板来代替吗?现在,如果这个方法失败了,对其他人的goto建议是“检查静态断言”,这使得它有点像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()};                                                   \
    }                                 
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Jarod42    7 年前

    需要宏来对枚举进行字符串化,并有助于避免std中专门化的样板文件,但是可以提取一些代码来创建模板:

    // Traits to retrieve name and mapping from enum.
    template <typename E>
    struct enum_traits
    {
        static_assert(std::is_enum<E>::value, "!");
    
        static const char* const name;
        static const std::map<E, std::string> mapping;
    };
    
    template <typename E>
    struct ECategory_impl : std::error_category
    {
        static_assert(std::is_enum<E>::value, "!");
    
        const char* name() const noexcept override
        {
          return enum_traits<E>::name;
        }
    
        std::string message(int c) const override
        {
            const auto& Map = enum_traits<E>::mapping;
            E code = static_cast<E>(c);
            auto itr = Map.find(code);
            if (itr != Map.end())
            {
                return itr->second;
            }
            else
            {
                return "(unrecognized error)";
            }
        }
    };
    
    template <typename E>
    std::error_code make_error_code(E e)
    {
        static_assert(std::is_enum<E>::value, "!");
        static const ECategory_impl<E> categ{};
        return {static_cast<int>(e), categ};
    }
    

    然后是宏(如果您认为现在要重复的内容足够少,可以忽略这个宏):

    #define MAKE_ERROR_CODE_CATEGORY(E)                      \
    /* Stringification for the name*/                        \
    template <> const char* const enum_traits<E>::name = #E; \
    /* Specialization in std */                              \
    namespace std                                            \
    {                                                        \
        template <>                                          \
        struct is_error_code_enum<E> : true_type {};         \
    }                                                        \
    /* Alias for custom naming */                            \
    using E##_category = ECategory_impl<E>;
    //                                    ^
    // You might remove that final ';' for a usage `MAKE_ERROR_CODE_CATEGORY(E);`
    // instead of current `MAKE_ERROR_CODE_CATEGORY(E)`
    

    用法类似于:

    enum class E {A, B};
    template <>
    const std::map<E, std::string> enum_traits<E>::mapping{
        {E::A, "A"},
        {E::B, "E"}
    };
    
    MAKE_ERROR_CODE_CATEGORY(E)
    

    Demo