17

I was trying to get a specialisation based on integer value as the template type. If I use a single integer, specialisation is straight forward. Is it possible to have a simple template specialisation based on range without using boost foundation.

This is representative code translates to

template <typename int val>
class Param
{
public:

};

template <> class Param<0 to 100>
{

};

template <> class Param<100 to 175>
{

};
Ram
  • 3,045
  • 3
  • 27
  • 42

3 Answers3

18

Here is one (simple) way to implement your requirements using SFINAE:

template<bool> struct Range;

template<int val, typename = Range<true> >
class Param
{};

template<int val>
class Param<val, Range<(0 <= val && val <= 100)> >
{};

template<int val>
class Param<val, Range<(100 < val && val <= 175)> >
{};

Demo.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Ah cool. Though really the only difference is that you have to write `(x < val && val <= y)` on every template class (though it's true you don't have to do `::type` either). I just wanted to move the conditional into the range class on mine. – Seth Carnegie Mar 01 '12 at 14:51
  • 1
    It's good that this question now has both ways to do it for future visitors. + 1 btw. – Seth Carnegie Mar 01 '12 at 14:58
  • Why a reference to SFINAE? There're no substitution failures in this example, are there? – Igor G Aug 27 '22 at 14:09
  • @IgorG, good question. May be it's just a specialization. Or may be the substitution fails when `Range` is evaluated, if a range is not met. Someone better versed with standard technicality can answer that. :-) – iammilind Aug 27 '22 at 14:51
16

You can use SFINAE with std::enable_if to make your own handy compile-time range testing class:

#include <iostream>
#include <type_traits>

using namespace std;

template<int Start, int End, int Val, class Enable = void>
struct crange { };

template<int Start, int End, int Val>
struct crange<Start, End, Val, typename std::enable_if<Val >= Start && Val <= End>::type> {
    typedef void enabled;
};

template<int Val, class Enable = void>
class Param {
public:
    Param() : value(422) { }

    int value;
};

template<int Val>             // V  VV the range [x, y]
class Param<Val, typename crange<0, 10, Val>::enabled> {
public:
    Param() : value(1.32) { }

    double value;
};

int main() {
    Param<1> pdouble;
    Param<50> pint;

    cout << pdouble.value << endl; // prints 1.32
    cout << pint.value    << endl; // prints 422
}
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • 1
    Seth, Thanks a lot. If there was a way to mark your answer as useful multiple times then I would click that button a hundred times! – Ram Mar 01 '12 at 05:11
  • 2
    +1 for the good answer, but it's lot more complex than needed. See my answer. – iammilind Mar 01 '12 at 13:21
-3

Is it possible to have a simple template specialisation based on range without using boost foundation.

Not for C++03 in a really elegant, concise, maintainable and generic way where arbitarily large ranges can be easily specified. To keep the code concise, you'd need something like the boost preprocessor library to loop over a range of values.

Sans BOOST or reimplementing huge chunks of it, you could create some sad little macros to call the initial macro for a specific number of consecutive values. For example:

#define X(N) template <> class Param<N> { ... };
#define X2(N) X(N) X(N+1)
#define X4(N) X2(N) X2(N+2)
#define X8(N) X4(N) X4(N+4)
...

// template <> class Param<100 to 175> then becomes
X64(100); // 100..163
X8(164);  // 164..171
X4(172);  // 172..175

Alternatively, you could use a program/script to write your C++ code, as an earlier compilation step, sometimes that works out better - sometimes worse - than preprocessor hackery.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252