質問

I want to store integers for given types which should be used during compilation and during runtime.

Up to now I have the following:

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

And I specialize for each type:

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

And I can check during compile time (of course the check here would be against another compile time constant):

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

Problem: This works well, as long as the specialization is in the same namespace. But that is an inconvenience I want to get rid of. So I want to be able to use it in every namespace:

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

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

Any ideas how I could do this?

C++11 and boost is ok for my situation, if really needed.

Update: Seems I gave to little information. The types are mainly enum classes. If you're really interested you can see the real implementation here, http://www.codeduce.com/extra/enum_tools, download the zip and in the header line 33, 34.

役に立ちましたか?

解決

For some reason I found the problem description easy to misunderstand, but the linked code makes it clear. In C++11 it's easy:

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

and a

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

in the right place. Here's a sample:

#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';
}

As a side note, that static_cast<Enum>(i) looks troublesome, does it really do the right thing with the m::demo enum?

To preserve the original templated-enum_length usage and so make the array-allocation usage a bit prettier is easy from here, rename the function enum_length_helper and then

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

他のヒント

If it is possible for your use-case, you could do specialization on a namespace basis and then aggregate as follows, using C++11 since you mentioned it but can work without.

Assume you have a number of namespaces ns_1 to ns_k like this:

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> {};
    /*...*/
}

I assume you already have the means to do a unique numbering. Then you aggregate the my_int from all namespaces like this:

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 */
}

Here's a solution using functions and 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;
    }

The TypeInfoFunc is found using ADL meaning that it can be defined in the same namespace as the class your specializing it for.

EDIT
Based on the comments, I think I understand a bit better now. The solution doesn't change much, simply make your function:

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

and change GetTypeInfo to

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

This way you can call GetTypeInfo<TypeA> and access all the information in (in this case) class A.

you can avoid the need to specialize a structure if you move the type information in the type itself:

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);

If you do this you will never have the problem with namespaces if the type is declared you will always have access to type info.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top