3

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.

11
  • 1
    How about template<> struct my_int<foo::a_type> { enum { result = 5 }; }; Commented Nov 29, 2013 at 13:28
  • Well the point is, that this specialization is part of a larger macro, which needs to be used in different namespaces. So, I would need to get out of every namespace and back in ih the preceding namespace in a macro... Commented Nov 29, 2013 at 13:31
  • try to use ::my_int or ::otherNS::my_int Commented Nov 29, 2013 at 13:39
  • @Paranaix: error: class template specialization of 'my_int' must originally be declared in namespace 'otherNS'. Commented Nov 29, 2013 at 13:47
  • @Paranaix: same here, I tried that: error: global qualification of class name is invalid before '{' token Commented Nov 29, 2013 at 13:47

4 Answers 4

2

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);
};
Sign up to request clarification or add additional context in comments.

3 Comments

Now, you propose something different that what the OP try :-) And it seems correct. Sorry if my remarks and OP were not clear. (previous remarks deleted)
Thank you, that is a very neat idea, it is very short and works well. Sorry, if my problem description was not clear enough. Considering your namespace m example, no that won't work correctly, but using DEF_ENUM in a plain state you get additional compiler checks for enum related data, that's what I'm after. I haven't seem a failsafe solution of this task yet.
@MikeM Looking back I should have gotten it. Oh, well. The confusion gave it time to steep a bit and all's well.
2

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

3 Comments

So he has to include the whole word to use this solution :-/
Yeah, it's not ideal. But the unique numbering mechanism would also need to include everything I guess, so hopefully this doesn't add more dependencies?
Although I'm not going to use this in production since it's more then one place to change to add a new item, I learned something about metaprogramming. Thank you for your efforts.
1

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.

9 Comments

This would mean, that for each new specialization there are two edits necessary? One entry for the enum and one TInfo.... Can one accomplish this with a macro?
@MikeM You can't extend an enum after if it's been declared, so no, I don't think you could do that with a macro. You could not use an enum and use integer constants instead but that means you have to select yourself a unique integer for each class and you won't get an error for duplicate values (unless you explicitly test for that but that again means 2 updates necessary for each new class)
I think we missunderstand. The types for which I want to store additional information are enum classes. So I want to store e.g. the length of an enum class as a compile time constant, to work with it. There is no type trait for the length of an enum class. But I want as little overhead as possible for storing that additional information, and since we only need to count the arguments in the enum class definition to get the size, I do this with a macro.
I don't know if that is possible, but something like TInfo<TypeA> TypeInfoFunc<5>(A) would do, meaning TypeInfoFunc needs to be a template function over int.
Thank you, but I don't really understand it. How would I add a specialization for e.g. TypeA, 5. A TypeInfoFunc(TInfo<TypeA>); doesn't allow for an integer, or does it need to be encoded in enum TypeInfo but then, what if I want to store 5 for two different enum classes?
|
0

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.

6 Comments

This works as long as you have control of every class you want to use this for.
@GuyGreer yes , it's hard if you already have a large codebase, but then again i don't think anyone wants custom static type info on all the code, you would need too many numbers.
Well, the types are mainly enum classes. It doesn't seem to work here, does it?
@MikeM just use a std::integral_constant<TypeInfo, type> to derive from, and then you have a nested value to get the enum constant back
@GuyGreer My types are enum classes so I cannot derive from anything, or can I? Though it was not possible.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.