C ++元编程:按类型存储整数

时间:2013-11-29 13:23:01

标签: c++ templates c++11 template-meta-programming

我想存储应该在编译期间和运行时使用的给定类型的整数。

到目前为止,我有以下内容:

template<typename T>
struct my_int { enum { result = -1 }; };

我专注于每种类型:

template<> struct my_int<a_type> { enum { result = 5 }; };

我可以在编译期间检查(当然这里的检查将是另一个编译时常量):

static_assert(my_int<a_type>::result == 5, "Bla");

问题: 只要专门化在同一名称空间中,这种方法就可以正常工作。但这是一个我想摆脱的不便。所以我希望能够在每个命名空间中使用它:

namespace foo {
  template<> struct my_int<a_type> { enum { result = 5 }; };
}

namespace bar {
  template<> struct my_int<b_type> { enum { result = 7 }; };
}

我有什么想法可以做到这一点?

如果真的需要,C ++ 11和boost对我的情况来说是好的。

更新:似乎我提供的信息很少。类型主要是enum classes。如果您真的感兴趣,可以在这里看到真正的实现,http://www.codeduce.com/extra/enum_tools,下载zip并在标题行33,34中。

4 个答案:

答案 0 :(得分:2)

如果您的用例可能,您可以在命名空间的基础上进行专业化,然后使用C ++ 11进行聚合,因为您提到它但可以不使用。

假设您有许多名称空间ns_1ns_k,如下所示:

namespace ns_i {
    template<class T> struct my_int: std::integral_constant<int, -1> {};
    /*...*/
    enum e_1 { /*...*/ };
    template<> struct my_int<e_1>: std::integral_constant<int, 101> {};
    /*...*/
    enum e_n { /*...*/ };
    template<> struct my_int<e_n>: std::integral_constant<int, 142> {};
    /*...*/
}

我假设您已经有办法进行唯一编号。然后从所有命名空间聚合my_int,如下所示:

namespace your_lib {
    template<
      class T,
      template<class> class sources... /* any number of template classes,
                                          each taking one type argument */
    >
    struct Union:
      std::integral_constant<int, -1> {}; // default -1 for (empty template list)

    template<
      class T,
      template<class> class source,    // match first template
      template<class> class sources... // match all but first template
    >
    struct Union<T, source, sources...>:
      std::conditional<
        source::value == -1,
        union<T, sources...>, // recursively call union on all but first tempalte
        source                // or if there's a value in first, use it
      > {};

    template<class T> struct my_int :
      Union<T, ns_1::my_int, /*...,*/ ns_k::my_int> {};
    /* here you could use boost preprocessor to iterate over the namespaces
       since you mentionned it */
}

答案 1 :(得分:2)

出于某种原因,我发现问题描述很容易被误解,但链接的代码清楚地表明了这一点。在C ++ 11中,它很简单:

#define SETUP_ENUM_LENGTH(enum_type, length)                                   \
  static constexpr int enum_length(enum_type*) { return length; }

  for (int i = 0; i < enum_length((Enum*)0); ++i) {

在正确的地方。这是一个示例:

#include <iostream>
#include <functional>
#include <boost/preprocessor/variadic/size.hpp>

/**
 * Macro to setup an enum completely.
 * First parameter is the name, following are the states as plain text.
 */
#define DEF_ENUM(name, ...)                                                    \
  enum class name : uint8_t { __VA_ARGS__ };                                   \
  SETUP_ENUM_LENGTH(name, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__))

/**
 * Once an enum class is defined, this macro makes the size publicly available.
 * Needed by enum_array. Already included in DEF_ENUM.
 */
#define SETUP_ENUM_LENGTH(enum_type, length)                                   \
  static constexpr int enum_length(enum_type*) { return length; }

/**
 * Function to iterate over all elements of an enum.
 */
template<typename Enum>
void enum_for_each(const std::function<void(Enum e)> &fct) {
  for (int i = 0; i < enum_length((Enum*)0); ++i) {
    fct(static_cast<Enum>(i));
  }
}

namespace n {
    DEF_ENUM(demo,u,v,w,x,y,z,a,b,c);
}
namespace m {
    DEF_ENUM(demo,a=3,b=1,c=4,d=1,e=5);
}

using std::cout;
int main()
{
    enum_for_each<n::demo>([](n::demo e) { cout<<int(e); });
    cout<<'\n';
    enum_for_each<m::demo>([](m::demo e) { cout<<int(e); });
    cout<<'\n';

    int ndemo[enum_length((n::demo*)0)];
    int mdemo[enum_length((m::demo*)0)];

    cout << sizeof ndemo << ' ' << sizeof mdemo << '\n';
}

作为旁注,static_cast<Enum>(i)看起来很麻烦,它是否真的用m::demo enum做了正确的事情?

要保留原始的模板化enum_length用法,请从此处轻松使数组分配用法更加简洁,重命名函数enum_length_helper然后

template<typename Enum>
struct enum_length {
   enum result=enum_length_helper((Enum*)0);
};

答案 2 :(得分:1)

这是使用函数和ADL的解决方案:

    #include <type_traits>

    enum TypeInfo
        {
            Unknown = 0,
            TypeA,
            TypeB
        };

    template <TypeInfo x>
    using TInfo = std::integral_constant<TypeInfo, x>;

    template <class T>
    TInfo<Unknown> TypeInfoFunc(T);

    template <class T>
    struct GetTypeInfo : decltype(TypeInfoFunc(std::declval<T>())){};

    namespace a{
        class A{};
        TInfo<TypeA> TypeInfoFunc(A);
    };

    namespace b {
        class B{};
        TInfo<TypeB> TypeInfoFunc(B);
    }

    int main()
    {
        static_assert(GetTypeInfo<a::A>::value == TypeA, "");
        static_assert(GetTypeInfo<b::B>::value == TypeB, "");
        return 0;
    }

使用ADL找到TypeInfoFunc,这意味着它可以在与您专门用于的类相同的命名空间中定义。

修改
根据评论,我想我现在明白了一点。解决方案没有太大变化,只需简单地完成您的功能:

    namespace a
    {
        struct A{};//Or whatever class you want to hold data about your type
        A TypeInfoFunc(TInfo<TypeA>);
    }

并将GetTypeInfo更改为

    template <TypeInfo x>
    struct GetTypeInfo : decltype(TypeInfoFunc(TypeInfo<X>())) {};

这样您就可以调用GetTypeInfo<TypeA>并访问(在本例中)类A中的所有信息。

答案 3 :(得分:0)

如果您在类型本身中移动类型信息,则可以避免专门化结构的需要:

template <int V>
struct TypeInfo { enum { result = V, }; };

class yourClass : TypeInfo<2> //works better if you have an enum instad of number
{}
//...

static_assert(a_type::result == 2);

如果你这样做,你将永远不会遇到名称空间的问题,如果声明了类型,你将始终可以访问类型信息。