Stuck on a metaprogramming problem

The following function inRange, which returns true only if 'value' is among RANGE... works correctly:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <type_traits>

enum Ability {Strength, Intelligence};
enum Skill {Athletics, Acrobatics, Arcana, History, Investigation, Nature, Religion};

template <int KEY, int... RANGE> struct Map {}; // one-to-many map (mapping KEY to RANGE...)

template <int KEY, int FIRST, int... REST>
struct Map<KEY, FIRST, REST...> : Map<KEY, REST...> {
	using Base = Map<KEY, REST...>;
	enum {key = KEY};
	static bool inRange (int value) {return (value == FIRST) ? true : Base::inRange(value);}  // returns true only if value is found in REST...
};

template <int KEY, int LAST>
struct Map<KEY, LAST> {
	enum {key = KEY};
	static bool inRange (int value) {return value == LAST;}
};

int main() {
	std::cout << Map<Intelligence, Arcana, History, Investigation, Nature, Religion>::inRange (Arcana) << std::endl;  // true
	std::cout << Map<Intelligence, Arcana, History, Investigation, Nature, Religion>::inRange (Religion) << std::endl;  // true
	std::cout << Map<Intelligence, Arcana, History, Investigation, Nature, Religion>::inRange (Athletics) << std::endl;  // false
}

But when I use it to define InverseMap below, it won't compile. Given
1
2
3
4
using SKILLS = M< 
	Map<Strength, Athletics>,
	Map<Intelligence, Arcana, History, Investigation, Nature, Religion>
>;

InverseMap<Investigation, SKILLS>::value is supposed to return Intelligence, because Intelligence maps to the range Arcana, History, Investigation, Nature, Religion.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <type_traits>

enum Ability {Strength, Intelligence};
enum Skill {Athletics, Acrobatics, Arcana, History, Investigation, Nature, Religion};

template <int KEY, int... RANGE> struct Map {}; // one-to-many map (mapping KEY to RANGE...)

template <int KEY, int FIRST, int... REST>
struct Map<KEY, FIRST, REST...> : Map<KEY, REST...> {
	using Base = Map<KEY, REST...>;
	enum {key = KEY};
	static bool inRange (int value) {return (value == FIRST) ? true : Base::inRange(value);}  // returns true only if value is found in REST...
};

template <int KEY, int LAST>
struct Map<KEY, LAST> {
	enum {key = KEY};
	static bool inRange (int value) {return value == LAST;}
};

template <typename...> struct M {};

using SKILLS = M< 
	Map<Strength, Athletics>,
	Map<Intelligence, Arcana, History, Investigation, Nature, Religion>
>;

template <typename T> struct Identity {using type = T;};

struct NotFound {using type = NotFound;  static const int key = -1;};

template <int, typename> struct InverseMap {};

template <int N, typename LAST>
struct InverseMap<N, M<LAST>> {
	using meta = typename std::conditional<LAST::inRange(N), Identity<LAST>, NotFound>::type;
	using type = typename meta::type;
	enum {value = type::key};
};

template <int N, typename FIRST, typename... REST>
struct InverseMap<N, M<FIRST, REST...>> {
	using meta = typename std::conditional< FIRST::inRange(N), Identity<FIRST>, InverseMap<N, M<REST...>> >::type;
	using type = typename meta::type;
	enum {value = type::key};
};

int main() {
	std::cout << Investigation << " is mapped from " << InverseMap<Investigation, SKILLS>::value << std::endl;
}
Last edited on
The error is that the 'value' parameter of inRange must be a compile-time constant, so I redefined inRange to this:
1
2
3
4
5
6
7
8
9
10
11
12
template <int KEY, int FIRST, int... REST>
struct Map<KEY, FIRST, REST...> : Map<KEY, REST...> {
	using Base = Map<KEY, REST...>;
	enum {key = KEY};
	template <int VALUE> static bool inRange() {return (VALUE == FIRST) ? true : Base::inRange<VALUE>();}
};

template <int KEY, int LAST>
struct Map<KEY, LAST> {
	enum {key = KEY};
	template <int VALUE> static bool inRange() {return (VALUE == LAST);}
};

But it won't compile and I don't know why.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <int KEY, int... RANGE> struct Map {}; // one-to-many map (mapping KEY to RANGE...)

template <int KEY, int FIRST, int... REST>
struct Map<KEY, FIRST, REST...> : Map<KEY, REST...> {
	using Base = Map<KEY, REST...>;
	enum {key = KEY};
	static constexpr bool inRange (int value) {return (value == FIRST) ? true : Base::inRange(value);}  // ************
};

template <int KEY, int LAST>
struct Map<KEY, LAST> {
	enum {key = KEY};
	static constexpr bool inRange (int value) {return value == LAST;} // *************
};
Topic archived. No new replies allowed.