Let have a type T and a struct having ONLY uniform elements of T type.
struct Foo {
T one,
T two,
T three
};
I'd like to access them in fallowing way:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
where cpp_offsetof macro (it is considered to be correct) is:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
The C++ standard doesn't guarantee it, but can we assume that members are distanced by a fixed offset and above is correct, cross-platform solution?
100% compatible solution would be:
struct Foo {
T one,
T two,
T three
T &operator [] (int i) {
const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) };
return *(T*)((size_t)this + offsets[i]);
}
};
[edit]standard, compliant and faster version was presented by snk_kid using pointers to data members[/edit]
but it requires extra lookup table which I'm trying to avoid.
//EDIT
And one more. I cannot use just an array and constants to index these fields, they have to be named fields of a struct (some macro requires that).
//EDIT2
Why those have to be named fields of a struct? What is the macro? It is settings system of a bigger project. Simplifying it's sth like this:
struct Foo {
int one;
int two;
}
foo;
struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type }
#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE }
Setting settings[] = {
SETTING(Foo, foo, one, INT_FIELD),
SETTING(Foo, foo, two, INT_FIELD)
};
And once again: I'm not looking form 100% compatible solution but 99%. I'm asking if we can expect that some compilers will put non-uniform padding between uniform fields.
Your code doesn't work with NON-POD types such those which using virtual member functions. There is a standard compliant (and efficient) way to achieve what you're trying to do, using pointer to data members:
template< typename T >
struct Foo {
typedef size_t size_type;
private:
typedef T Foo<T>::* const vec[3];
static const vec v;
public:
T one;
T two;
T three;
const T& operator[](size_type i) const {
return this->*v[i];
}
T& operator[](size_type i) {
return this->*v[i];
}
};
template< typename T >
const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three };
Just make sure you use const every with the table of pointer to data-members to get optimizations. Check here to see what I'm talking about.
Another way is with template specialization if what you are trying to achieve is still a compile time feature.
class Foo {
T one;
T two;
T three;
};
template <int i> T & get(Foo& foo);
template T& get<1>(Foo& foo){ return foo.one;}
template T& get<2>(Foo& foo){ return foo.two;}
template T& get<3>(Foo& foo){ return foo.three;}
It would be nice to define get as a member function but you cannot
specialize template member functions. Now if this is only a compile time
expansion you are looking for then this will avoid the lookup table
issue of one of the previous posts. If you need runtime resolution
then you need a lookup table obviously.
--
Brad Phelan
http://xtargets.heroku.com
You might be able to achieve what you want using an array to hold the data (so you can get indexed access without using a lookup table) and having references to the various array elements (so you can have 'named' elements for use by your macros).
I'm not sure what your macros require, so I'm not 100% sure this will work, but it might. Also, I'm not sure that the slight overhead of the lookup table approach is worth jumping through too many hoops to avoid. On the other hand, I don't think the approach I suggest here is any more complex than the table-of-pointers approach, so here it is for your consideration:
#include <stdio.h>
template< typename T >
struct Foo {
private:
T data_[3];
public:
T& one;
T& two;
T& three;
const T& operator[](size_t i) const {
return data_[i];
}
T& operator[](size_t i) {
return data_[i];
}
Foo() :
one( data_[0]),
two( data_[1]),
three( data_[2])
{};
};
int main()
{
Foo<int> foo;
foo[0] = 11;
foo[1] = 22;
foo[2] = 33;
printf( "%d, %d, %d\n", foo.one, foo.two, foo.three);
Foo<int> const cfoo( foo);
printf( "%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]);
return 0;
}
You can't because the compiler can add dead bytes between members to allow padding.
There is two ways to do what you want.
The first is to use your compiler-specific keyword or pragma macro that will force the compiler to not add padding bytes. But that is not portable.
That said it might be the easiest way to do it with your macro requirements, so I suggest you explore this possibility and prepare for adding more pragma when using different compilers.
The other way is to first make sure your members are aligned, then add accessors :
struct Foo {
T members[ 3 ]; // arrays are guarrantied to be contigu
T& one() { return members[0]; }
const T& one() const { return members[0]; }
//etc...
};
If you're sure the compilers you're using are going to generate the right code for this (and I'd imagine they would, assuming T isn't a reference type anyway) the best thing to do is put in some kind of check that the struct is laid out as you think. I can't think of any particular reason to insert non-uniform padding between adjacent members of the same type, but if you check the struct layout by hand then you'll at least know if it happens.
If the struct (S) has exactly N members of type T, for example, you can check at compile time that they are tightly packed simply using sizeof:
struct S {
T a,b,c;
};
extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
If this compiles, then they're tightly packed, as there's no space for anything else.
If you just happen to have N members, that you want to ensure are placed directly one after the other, you can do something similar using offsetof:
class S {
char x;
T a,b,c;
};
extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1];
extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1];
Depending on the compiler, this might have to become a runtime check, possibly not using offsetof -- which you might want to do for non-POD types anyway, because offsetof isn't defined for them.
S tmp;
assert(&tmp.b==&tmp.a+1);
assert(&tmp.c==&tmp.b+1);
This doesn't say anything about what to do if the asserts start failing, but you should at least get some warning that the assumptions aren't true...
(By the way, insert appropriate casts to char references and so on where appropriate. I left them out for brevity.)
Related
Toy example:
template<typename T, std::size_t N>
class static_vector
{
public:
T& operator[](std::size_t i) { return m_elements[i]; }
T const& operator[](std::size_t i) const { return m_elements[i]; }
private:
std::array<T, N> m_elements;
};
template<typename T>
class vector3
: public static_vector<T, 3>
{
public:
using vector_type = static_vector<T, 3>;
// x = vector_type::operator[](0);
// y = vector_type::operator[](1);
// z = vector_type::operator[](2);
};
Let vector3<float> pos;. I want to access pos[0] via pos.x. Clearly, if pos is declared to be const, I want pos.x to be read-only.
Is this possible?
Let me stress the fact that I don't want to use accessor functions of the form
T& x() { return (*this)[0]; }
T const& x() const { return (*this)[0]; }
There is no zero cost way to do this using the exact syntax you want.
Relaxing either cost (compile, maintenance, memory usage and runtime) or syntax (which your () is an example of) gets you what you want.
I added a comment to your question but I thought I'd add an answer with some code to clarify. Be warned, what follows is not a good idea.
You can use simple pointer arithmetic to interpret members of a struct as if they were elements in an array. Because the type of the struct members and the type of elements in the pseudo array are the same, we're safe to reinterpret one as the other with the caveat that there is no padding in between the struct members.
The C++ standard gives no method for defining padding in a struct so you will have to rely on compiler specific directives. I believe however that both MSVC and GCC support #pragma pack.
#pragma pack(push, 1)
template <typename T>
struct Vec3
{
T x;
T y;
T z;
T& operator[](size_t i) { return *(&x + i); }
const T& operator[](size_t i) const { return *(&x + i); }
};
#pragma pack(pop)
So why isn't this a good solution?
You are relying on compiler specific directives making your code less portable.
You need to explicitly declare each member which means you'll need separate templates for Vec2, Vec3 and Vec4.
1 byte alignment isn't supported on all architectures again making your code less portable.
Even on architectures where unaligned memory access is supported (such as x86,) it comes with a performance penalty.
Let have a type T and a struct having ONLY uniform elements of T type.
struct Foo {
T one,
T two,
T three
};
I'd like to access them in fallowing way:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
where cpp_offsetof macro (it is considered to be correct) is:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
The C++ standard doesn't guarantee it, but can we assume that members are distanced by a fixed offset and above is correct, cross-platform solution?
100% compatible solution would be:
struct Foo {
T one,
T two,
T three
T &operator [] (int i) {
const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) };
return *(T*)((size_t)this + offsets[i]);
}
};
[edit]standard, compliant and faster version was presented by snk_kid using pointers to data members[/edit]
but it requires extra lookup table which I'm trying to avoid.
//EDIT
And one more. I cannot use just an array and constants to index these fields, they have to be named fields of a struct (some macro requires that).
//EDIT2
Why those have to be named fields of a struct? What is the macro? It is settings system of a bigger project. Simplifying it's sth like this:
struct Foo {
int one;
int two;
}
foo;
struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type }
#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE }
Setting settings[] = {
SETTING(Foo, foo, one, INT_FIELD),
SETTING(Foo, foo, two, INT_FIELD)
};
And once again: I'm not looking form 100% compatible solution but 99%. I'm asking if we can expect that some compilers will put non-uniform padding between uniform fields.
Your code doesn't work with NON-POD types such those which using virtual member functions. There is a standard compliant (and efficient) way to achieve what you're trying to do, using pointer to data members:
template< typename T >
struct Foo {
typedef size_t size_type;
private:
typedef T Foo<T>::* const vec[3];
static const vec v;
public:
T one;
T two;
T three;
const T& operator[](size_type i) const {
return this->*v[i];
}
T& operator[](size_type i) {
return this->*v[i];
}
};
template< typename T >
const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three };
Just make sure you use const every with the table of pointer to data-members to get optimizations. Check here to see what I'm talking about.
Another way is with template specialization if what you are trying to achieve is still a compile time feature.
class Foo {
T one;
T two;
T three;
};
template <int i> T & get(Foo& foo);
template T& get<1>(Foo& foo){ return foo.one;}
template T& get<2>(Foo& foo){ return foo.two;}
template T& get<3>(Foo& foo){ return foo.three;}
It would be nice to define get as a member function but you cannot
specialize template member functions. Now if this is only a compile time
expansion you are looking for then this will avoid the lookup table
issue of one of the previous posts. If you need runtime resolution
then you need a lookup table obviously.
--
Brad Phelan
http://xtargets.heroku.com
You might be able to achieve what you want using an array to hold the data (so you can get indexed access without using a lookup table) and having references to the various array elements (so you can have 'named' elements for use by your macros).
I'm not sure what your macros require, so I'm not 100% sure this will work, but it might. Also, I'm not sure that the slight overhead of the lookup table approach is worth jumping through too many hoops to avoid. On the other hand, I don't think the approach I suggest here is any more complex than the table-of-pointers approach, so here it is for your consideration:
#include <stdio.h>
template< typename T >
struct Foo {
private:
T data_[3];
public:
T& one;
T& two;
T& three;
const T& operator[](size_t i) const {
return data_[i];
}
T& operator[](size_t i) {
return data_[i];
}
Foo() :
one( data_[0]),
two( data_[1]),
three( data_[2])
{};
};
int main()
{
Foo<int> foo;
foo[0] = 11;
foo[1] = 22;
foo[2] = 33;
printf( "%d, %d, %d\n", foo.one, foo.two, foo.three);
Foo<int> const cfoo( foo);
printf( "%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]);
return 0;
}
You can't because the compiler can add dead bytes between members to allow padding.
There is two ways to do what you want.
The first is to use your compiler-specific keyword or pragma macro that will force the compiler to not add padding bytes. But that is not portable.
That said it might be the easiest way to do it with your macro requirements, so I suggest you explore this possibility and prepare for adding more pragma when using different compilers.
The other way is to first make sure your members are aligned, then add accessors :
struct Foo {
T members[ 3 ]; // arrays are guarrantied to be contigu
T& one() { return members[0]; }
const T& one() const { return members[0]; }
//etc...
};
If you're sure the compilers you're using are going to generate the right code for this (and I'd imagine they would, assuming T isn't a reference type anyway) the best thing to do is put in some kind of check that the struct is laid out as you think. I can't think of any particular reason to insert non-uniform padding between adjacent members of the same type, but if you check the struct layout by hand then you'll at least know if it happens.
If the struct (S) has exactly N members of type T, for example, you can check at compile time that they are tightly packed simply using sizeof:
struct S {
T a,b,c;
};
extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
If this compiles, then they're tightly packed, as there's no space for anything else.
If you just happen to have N members, that you want to ensure are placed directly one after the other, you can do something similar using offsetof:
class S {
char x;
T a,b,c;
};
extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1];
extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1];
Depending on the compiler, this might have to become a runtime check, possibly not using offsetof -- which you might want to do for non-POD types anyway, because offsetof isn't defined for them.
S tmp;
assert(&tmp.b==&tmp.a+1);
assert(&tmp.c==&tmp.b+1);
This doesn't say anything about what to do if the asserts start failing, but you should at least get some warning that the assumptions aren't true...
(By the way, insert appropriate casts to char references and so on where appropriate. I left them out for brevity.)
I have a chunk of memory populated by external code which I'm trying to reverse engineer. I don't know the complete structure of this memory, but I do know a few fields (e.g. the chunk starts off with an int32 named 'foo' and there's a double at offset 0xC called 'bar'). I want to define a structure and essentially reinterpret-cast a pointer to this memory chunk to that structure, and have it line up. I'm not sure if there's a more conventional name for this technique but I'll refer to it as creating an 'overlay type'.
Here's a sketch of what I'd like to be able to do:
START_OVERLAY_TYPE(my_type, 0xFF) // struct named my_type, size 0xFF
FIELD(0x00, int32_t foo); // field int32_t foo at 0x00
FIELD(0x0C, double bar); // field double bar at 0x0C
END_OVERLAY_TYPE
Not having to use macros would be a plus, but I don't see a good way around them.
With my current implementation, I expand this to (something like):
__pragma(pack(push, 1))
template<size_t p> struct padding_t { unsigned char pad[p]; };
template<> struct padding_t<0> {};
struct my_type
{
union
{
struct : padding_t<0xFF> {}; // ensure total size is 0xFF
struct : padding_t<0x00> { int32_t foo; }; // field at 0x00
struct : padding_t<0x0C> { double bar; }; // field at 0x0C
};
};
__pragma(pack(pop))
This compiles and works great, at least in the versions I tried of clang, gcc, and VC++ (with appropriate changes to the pragma). Unfortunately, warnings abound due to the non-standard use of anonymous structs.
Is there any way to achieve the same effect while staying within the standard? The requirements are that it be reasonably simple to declare (like the current macro is), and that to the consumer, the usage is indistinguishable from
struct my_type { int32_t foo; double bar; }
at least to the casual observer.
The current code will work for my purposes, I'm just curious if there is a better approach I am overlooking.
You could try something like this with implicit type conversions and assignment operators for the internal struct containing the value. This way instead of using unnamed structs the struct bears the name, but the internals become the unnamed part through operator overloading.
I tried this out with some client code (passing to functions, getting/setting values) and everything seemed fine. It's of course possible that I missed a scenario somewhere.
__pragma(pack(push, 1))
template<size_t p, typename t>
struct padding_t
{
unsigned char pad[p];
t val;
operator t () const {return val;}
operator t& () {return val;}
padding_t<p, t>& operator= (const t& rhs) {val = rhs; return *this;}
};
template<typename t> struct padding_t<0, t>
{
t val;
operator t () const {return val;}
operator t& () {return val;}
padding_t<0, t>& operator= (const t& rhs) {val = rhs; return *this;}
};
template<size_t p>
struct sizing_t
{
unsigned char pad[p];
};
struct my_type
{
union
{
sizing_t<0xFF> size; // ensure total size is 0xFF
padding_t<0x00, int32_t> foo; // field at 0x00
padding_t<0x0C, double> bar; // field at 0x0C
};
};
__pragma(pack(pop))
I've recently run into a quite a few situations where the Named Parameter Idiom would be useful, but I'd like it to be guaranteed in compile-time. The standard method of returning references in a chain almost always appears to invoke a run-time constructor (compiling with Clang 3.3 -O3).
I haven't been able to find anything with reference to this so I tried to get this to work with constexpr and got something functional:
class Foo
{
private:
int _a;
int _b;
public:
constexpr Foo()
: _a(0), _b(0)
{}
constexpr Foo(int a, int b)
: _a(a), _b(b)
{}
constexpr Foo(const Foo & other)
: _a(other._a), _b(other._b)
{}
constexpr Foo SetA(const int a) { return Foo(a, _b); }
constexpr Foo SetB(const int b) { return Foo(_a, b); }
};
...
Foo someInstance = Foo().SetB(5).SetA(2); //works
While this is okay for a small number of parameters, for larger numbers it quickly blows up into a mess:
//Unlike Foo, Bar takes 4 parameters...
constexpr Bar SetA(const int a) { return Bar(a, _b, _c, _d); }
constexpr Bar SetB(const int b) { return Bar(_a, b, _c, _d); }
constexpr Bar SetC(const int c) { return Bar(_a, _b, c, _d); }
constexpr Bar SetD(const int d) { return Bar(_a, _b, _c, d); }
Is there a better way? I'm looking at doing this with classes that have many (30+) parameters and this seems like it would be prone to error if extended in the future.
EDIT: Removed C++1y tag -- while C++1y does appear to fix the problem (thanks TemplateRex!) this is for production code, and we are stuck with C++11. If that means its impossible, then I guess that's just the way it is.
EDIT2: To show why I'm looking for this, here's a use case. Currently with our platform, developers need to explicitly set bit vectors for hardware configurations, and while this is okay it's very error prone. Some are using designated initializers from the C99 extension, which is okay but non-standard:
HardwareConfiguration hardwareConfig = {
.portA = HardwareConfiguration::Default,
.portB = 0x55,
...
};
Most, however, aren't even using this, and are just inputting a blob of numbers. So as a working improvement, I'd like to move towards something like this (since it also forces better code):
HardwareConfiguration hardwareConfig = HardwareConfiguration()
.SetPortA( Port().SetPolarity(Polarity::ActiveHigh) )
.SetPortB( Port().SetPolarity(Polarity::ActiveLow) );
Which might be far more verbose, but much clearer when reading later.
Using Template Metaprogramming
Here is something I came up with to solve your problem (at least partially). By using template metaprogramming, you can leverage the compiler to do most of the job for you. These techniques look weird for those who have never seen such code before, but thankfully most of the complexity can be hidden away in a header and the users only interact with the library in a neat and terse manner.
A Sample Class Definition and its Use
Here is an example of what defining a class would entail on your part:
template <
//Declare your fields here, with types and default values
typename PortNumber = field<int, 100>,
typename PortLetter = field<char, 'A'>
>
struct MyStruct : public const_obj<MyStruct, PortNumber, PortLetter> //Derive from const_obj like this, passing the name of your class + all field names as parameters
{
//Your setters have to be declared like this, by calling the Set<> template provided by the base class
//The compiler needs to be told that Set is part of MyStruct, probably because const_obj has not been instantiated at this point
//in the parsing so it doesn't know what members it has. The result is that you have to use the weird 'typename MyStruct::template Set<>' syntax
//You need to provide the 0-based index of the field that holds the corresponding value
template<int portNumber>
using SetPortNumber = typename MyStruct::template Set<0, portNumber>;
template<int portLetter>
using SetPortLetter = typename MyStruct::template Set<1, portLetter>;
template<int portNumber, char portLetter>
using SetPort = typename MyStruct::template Set<0, portNumber>
::MyStruct::template Set<1, portLetter>;
//You getters, if you want them, can be declared like this
constexpr int GetPortNumber() const
{
return MyStruct::template Get<0>();
}
constexpr char GetPortLetter() const
{
return MyStruct::template Get<1>();
}
};
Using the Class
int main()
{
//Compile-time generation of the type
constexpr auto myObject =
MyStruct<>
::SetPortNumber<150>
::SetPortLetter<'Z'>();
cout << myObject.GetPortNumber() << endl;
cout << myObject.GetPortLetter() << endl;
}
Most of the job is done by the const_obj template. It provides a mechanism to modify your object at compile time. Much like a Tuple, the fields are accessed with 0-based indices but this does not stop you from wrapping the setters with friendly names, as is done with SetPortNumber and SetPortLetter above. (They just forward to Set<0> and Set<1>)
About Storage
In the current implementation, after all the setters have been called and the object declared, the fields end up being stored in a compact array of const unsigned char's named data in the base class. If you use fields that are not unsigned chars (as in done above with PortNumber for example) the field is divided in big endien unsigned char's (could be changed to little endien as needed). If you don't need an actual storage that has an actual memory address, you could omit it altogether by modifying the packed_storage (see full implementation link below) and the values would still be accessible at compile time.
Limitations
This implementation only allows integral types to be used as fields (all flavors of shorts, ints, longs, bool, char). You can still provide setters that act on more than one field though. Example:
template<int portNumber, char portLetter>
using SetPort = typename MyStruct::template Set<0, portNumber>::
MyStruct::template Set<1, portLetter>;
Full Code
The full code for the implementation of this little library can be found here:
Full Implementation
Additional Notes
This code has been tested and works with the C++11 implementation of both g++ and clang.
It has not been tested for hours and hours so of course there may be bugs but it should provide you with a good base to start with. I hope this helps!
In C++14, constraints on constexpr function will be relaxed, and the usual chaining of reference-returning setters will work at compile-time:
#include <iostream>
#include <iterator>
#include <array>
#include <utility>
class Foo
{
private:
int a_ = 0;
int b_ = 0;
int c_ = 0;
int d_ = 0;
public:
constexpr Foo() = default;
constexpr Foo(int a, int b, int c, int d)
:
a_{a}, b_{b}, c_{c}, d_{d}
{}
constexpr Foo& SetA(int i) { a_ = i; return *this; }
constexpr Foo& SetB(int i) { b_ = i; return *this; }
constexpr Foo& SetC(int i) { c_ = i; return *this; }
constexpr Foo& SetD(int i) { d_ = i; return *this; }
friend std::ostream& operator<<(std::ostream& os, const Foo& f)
{
return os << f.a_ << " " << f.b_ << " " << f.c_ << " " << f.d_ << " ";
}
};
int main()
{
constexpr Foo f = Foo{}.SetB(5).SetA(2);
std::cout << f;
}
Live Example using Clang 3.4 SVN trunk with std=c++1y.
I'm not sure if classes with 30 parameters are a good idea (Single Responsiblity Principle and all that) but at least the above code scales linearly in the number of setters, with only 1 argument per setter. Note also that there are only 2 constructors: the default one (which takes its arguments from the in-class initializers) and the full one which takes 30 ints in your ultimate case).
I have:
const unsigned int hash_1 = 0xaf019b0c;
const unsigned int hash_2 = 0xf864e55c;
const unsigned int hash_3 = 0xfaea8ed5;
Hashes come from an automatically generated header. These hashes are indirectly associated with tags 1, 2, 3. The tags are associated with classes through a simple compile-time generated id. That way I can GetTag<Class1>() and get my int-tag for Class1.
My goal is to simplify the hash->tag association. Preferably this should be compile-time generated and O(1) access time. Memory in this case is not an issue. I can not use any third-party software.
I have tried the following:
template<uint32 t> size_t GetTagByHash() { return 0; }
with specific implementations like:
template<> size_t GetTagByHash<hash_1>() { return GetTag<Class1>(); }
but that kind of implementation is difficult to use since if I have a local variable uint32 my_hash; that the compiler can't determine what value it has in compile-time then the compiler is unable to resolve the correct implementation of GetTagByHash() to call.
As I understand it, your problem is how to do this lookup with run-time values as well as compile-time ones.
You've really got two questions. First, what algorithm do you want to use to do the lookup, and second, how do you tell C++ to implement that?
The algorithm to use is somewhat of a non-obvious question; you've got a list of effectively-random numbers, and you want to look up something in that list and return an associated tag. Probably you want some sort of hashtable, but to start with, I'll show some examples with something simpler -- and likely better for small numbers of hashes: A simple O(N) lookup, in pseudocode:
if i = N return tag_N
else if i = N-1 ...
...
else if i = 1 return tag_1
else return tag_0
Now, how do you tell C++ to do this? You've got to create a list of all your hash tags, and instructions for doing that. Here's a simple way:
template<int i> struct lookup
{
int result(int j) { return 0; }
};
const unsigned int hash_1 = 0xaf019b0c;
template<> struct lookup<1>
{
int result(int j)
{
if (j == hash_1)
return GetTag<Class1>();
return lookup<0>::result(j);
}
};
const unsigned int hash_2 = 0xf864e55c;
template<> struct lookup<2>
{
int result(int j)
{
if (j == hash_2)
return GetTag<Class2>();
return lookup<1>::result(j);
}
};
And so forth. Then, at the end, you can have
int hash_lookup(int j)
{
return lookup<last_hash_number>::result(j);
}
Writing out all those identical definitions is a pain, though, so it's best to let C++ do that -- and, to do that, you need to define the hashes in such a way that they can be iterated over. Let's do that:
template<int> struct hash_tag {
static const int value = 0;
typedef type void;
};
#define SET_HASH(I, VALUE, CLASS) \
template<> struct hash_tag<(I)> \
{ \
static const int value = (VALUE); \
typedef type (CLASS); \
}
SET_HASH(1, 0xaf019b0c, Class1);
SET_HASH(2, 0xf864e55c, Class2);
SET_HASH(3, 0xfaea8ed5, Class3);
// Define a general recursive lookup struct.
template<int i> struct lookup
{
int result(int j)
{
if (j == hash_tag<i>::value)
return GetTag<hash_tag<i>::type>;
return lookup<i-1>::result(j);
}
};
// Make sure the recursion terminates.
template<> struct lookup<0>
{
int result(int) { return 0; }
};
Then, you use this as before.
Now, let's return to that first question -- what algorithm do you actually want to use to do the lookup? The advantage of this iterative O(N) lookup is that it's easy to program, and it doesn't require any initialization of any data structures at run-time -- you can just call it. However, as noted, it's O(N). An alternate choice is to use a std::map object; you can use a similar recursive definition to initialize it at runtime, and then use it. That might look something like this:
// Make a typedef to save some typing.
typedef std::map<unsigned int, size_t> Map_type;
typedef std::pair<unsigned int, size_t> Map_value;
// Define a recursion to add hashes to the map.
template<int i> struct add_hash
{
void add(Map_type& hashmap)
{
hashmap.insert(
Map_value(hash_tag<i>::value,
GetTag<hash_tag<i>::type>));
add_hash<i-1>::add(hashmap);
}
};
// Make sure the recursion terminates.
template<> struct lookup<0>
{
void add(Map_type&) {}
};
// Now, create a class to initialize the std::map and do lookup.
class Hash_lookup
{
Hash_lookup() { add_hash<last_hash_number>(map_); }
int result(unsigned int j) { return map_[j]; }
private:
Map_type map_;
}
Personally, I would probably combine this with your GetTagByHash<> idea, and give the Hash_loop a "runtime-computed result" function as I described, as well as a "compile-time-computed result" function that takes a template argument rather than a function argument. But, in general, that's the basic idea for doing runtime lookup -- you put the values you want to look up into a set of templated classes that you can recursively iterate over at compile time, and then you use that recursive iteration to either define a lookup function or initialize a runtime structure that you can use for doing the lookup.