0

Let's assume I have an array of ints which may get big over time. To save some memory I have constructed a class which takes a maximal value that will be stored in the array, and converts it into one of the following integers types: uint8_t, uint16_t, uint32_t, uint64_t.

The code:

#include <limits>
#include <cstdint>

template <uint64_t MAX_VAL>
class SELECTOR_INT
{
private:

    using  enum_type = uint64_t;

    static enum : enum_type { success_8 =  1, success_16 =  2, success_32 =  3, success_64 =  4 };
    static enum : enum_type { failure_8 = 11, failure_16 = 12, failure_32 = 13, failure_64 = 14 };

    #define TYPE_MAX(_TYPE) static_cast<enum_type>(std::numeric_limits<_TYPE>::max())
    static enum : enum_type { result_8  = (                                   (MAX_VAL <= TYPE_MAX(uint8_t )) ) ? success_8  : failure_8  };
    static enum : enum_type { result_16 = ( (MAX_VAL > TYPE_MAX(uint8_t )) && (MAX_VAL <= TYPE_MAX(uint16_t)) ) ? success_16 : failure_16 };
    static enum : enum_type { result_32 = ( (MAX_VAL > TYPE_MAX(uint16_t)) && (MAX_VAL <= TYPE_MAX(uint32_t)) ) ? success_32 : failure_32 };
    static enum : enum_type { result_64 = ( (MAX_VAL > TYPE_MAX(uint32_t))                                    ) ? success_64 : failure_64 };
    #undef TYPE_MAX

    #define MIN(_LOP, _ROP) ((_LOP < _ROP) ? _LOP : _ROP)
    static enum : enum_type { RESULT = MIN(result_8, MIN(result_16, MIN(result_32, MIN(result_32, result_64)))) };
    #undef MIN

private:

    template <enum_type num>
    struct CHOOSED;

    template <> struct CHOOSED <success_8 > { using TYPE = typename uint8_t;  };   // Line 162
    template <> struct CHOOSED <success_16> { using TYPE = typename uint16_t; };   // Line 163
    template <> struct CHOOSED <success_32> { using TYPE = typename uint32_t; };   // Line 164
    template <> struct CHOOSED <success_64> { using TYPE = typename uint64_t; };   // Line 165

public:

    using TYPE = CHOOSED<RESULT>::TYPE;

};

using int_type = SELECTOR_INT<200>::TYPE;

int main()
{
    int_type test_variable = 0;
    return 0;
}

unfortunately, I have messed something up because I get the following errors:

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(163): error C2766: explicit specialization; 'SELECTOR_INT<MAX_VAL>::CHOOSED<>' has already been defined

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(162): note: see previous definition of 'CHOOSED<, ?? :: ?? :: ?? ::<unnamed-enum-success_8> >'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(171): note: see reference to class template instantiation 'SELECTOR_INT<MAX_VAL>' being compiled

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(164): error C2766: explicit specialization; 'SELECTOR_INT<MAX_VAL>::CHOOSED<>' has already been defined

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(162): note: see previous definition of 'CHOOSED<, ?? :: ?? :: ?? ::<unnamed-enum-success_8> >'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(165): error C2766: explicit specialization; 'SELECTOR_INT<MAX_VAL>::CHOOSED<>' has already been defined

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(162): note: see previous definition of 'CHOOSED<, ?? :: ?? :: ?? ::<unnamed-enum-success_8> >'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(169): error C2027: use of undefined type 'SELECTOR_INT<4>::CHOOSED<0>'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(169): note: see declaration of 'SELECTOR_INT<4>::CHOOSED<0>'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(187): note: see reference to class template instantiation 'SELECTOR_INT<4>' being compiled

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(169): error C2061: syntax error: identifier 'TYPE'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(169): error C2238: unexpected token(s) preceding ';'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(187): error C2039: 'TYPE': is not a member of 'SELECTOR_INT<4>'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(187): note: see declaration of 'SELECTOR_INT<4>'

1>D:/Dev/visual_studio/nevada_test_site/source/workspace/internship/hamiltonian/start.cu(187): error C2061: syntax error: identifier 'TYPE'

Question:

How can i fix this?

PatrykB
  • 1,579
  • 1
  • 15
  • 24
  • `static enum` ? – LogicStuff Aug 17 '17 at 21:05
  • I thought it was the case. I believe (but I am not sure) the error reports that I created multiple instances of this class. If i delete "template" declaration from the `SELECTOR_INT` class everything works fine. e.g. `error C2766: explicit specialization; 'SELECTOR_INT::CHOOSED<>' has already been defined ` – PatrykB Aug 17 '17 at 21:06
  • Well, a [mcve] would help, or at least saying what line is what in your question. That would fix your question. – Yakk - Adam Nevraumont Aug 17 '17 at 21:10
  • 2
    Other random ill-formedness; use of identifiers starting with `_` followed by a capital letter makes your program ill-formed; theae are explicitly reserved by the standard for your compiler and its standard library implementation. That is not why your compiler is complaining however. – Yakk - Adam Nevraumont Aug 17 '17 at 21:14
  • @Yakk sorry, working on it. I believe I know the reason. I am creating a class template which holds another class template. The outer one is specified for every int type. The inner one is not. The class `SELECTOR_INT` creates multiple definitions of the class `CHOOSED` which are indistinguishable. – PatrykB Aug 17 '17 at 21:18
  • It is not the case... – PatrykB Aug 17 '17 at 21:21
  • Possible duplicate of [C++ syntax for explicit specialization of a template function in a template class?](https://stackoverflow.com/questions/2097811/c-syntax-for-explicit-specialization-of-a-template-function-in-a-template-clas) – Daniel H Aug 17 '17 at 21:32
  • 2
    That’s not an *exact* duplicate, because you want to specialize a template class inside another template class, but it is close enough. Either moving `CHOOSED` (the correct English term is would be `CHOSEN`) outside the class, or using the convoluted syntax of some of the answers, should work. – Daniel H Aug 17 '17 at 21:33

1 Answers1

3

There are many problems with your code. Bad identifiers, attempting to specialize inner classes, etc. And maybe stuff wrong in the logic.

Regardless, I don't like the approach; it involves magic numbers and macros and is a mess. Simply solve the problem directly:

template<std::size_t I>
struct index {
  static constexpr std::size_t value=I;
  constexpr operator std::size_t()const{return I;}
  constexpr index(){}
};

This just makes the below simpler.

first_ takes a sequence of bools, and tells you which one is the first true one:

template<bool...>
struct first_ {};
template<bool... bs>
struct first_<true, bs...>:index<0>{};
template<bool... bs>
struct first_<false, bs...>:index<first_<bs...>::value+1>{};

nth takes an index and a sequence of types, and returns the the nth type:

template<std::size_t N, class...Ts>
struct nth_impl{};
template<class T0, class...Ts>
struct nth_impl<0, T0,Ts...>{using type=T0;};
template<std::size_t N, class T0, class...Ts>
struct nth_impl<N, T0,Ts...>:nth_impl<N-1,Ts...>{};
template<std::size_t N, class...Ts>
using nth=typename nth_impl<N,Ts...>::type;

Then we solve the problem in a generic way:

template<class T, T x, class...Ts>
using smallest_that_fits = nth< first_<(x<=std::numeric_limits<Ts>::max())...>::value, Ts... >;

Finally, we solve your specific sub-problem:

template<std::int64_t x>
using choice = smallest_that_fits<
  std::int64_t, x,
  std::int8_t, std::int16_t, std::int32_t, std::int64_t
>;

You can write nth using std::tuple if you prefer. And index can be an alias forstd::integral_constant<std::size_t.

first_ has a trailing _ as otherwise my compiler explodes in a compiler bug.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Thank you. I just began with the `metaprogramming`... And I have much to learn... The tutorial I read solved the issues just as I did it above with `enum` types but `C++11` brought us something better - the `constexpr` – PatrykB Aug 17 '17 at 22:16