I recently have hit a wall, and can't seem to find an elegant solution to my problem:
I have different data structures of different number of elements, all of which are aligned to pointer-size and are not larger than a pointer, ex.:
struct DATA_0{
size_t Value;
size_t *PtrValue;
bool BoolValue;
};
or
struct DATA_1{
size_t Value0;
size_t Value1;
size_t *PtrValue;
void *PtrBuffer;
bool BoolValue;
const char *StringBuffer;
}
The input comes in a form like this:
struct INPUT{
size_t *DataBuffer;
size_t DataVector[16];
};
I need to map the data structures to different elements from either the DataBuffer or the DataVector, at compile time:
Some example of desired usage:
INPUT_DATA *input;
data_mapping<DATA_0> mapping_0(input);
//will fetch data from input->DataVector[0] in some cases, input->DataBuffer[0] in other
size_t tmpValue = mapping_0[Value];
data_mapping<DATA_1> mapping_1(input);
//will fetch data from input->DataVector[1] in some cases, input->DataBuffer[3] in other
size_t tmpPtrBuffer = mapping_1[PtrBuffer];
NOTE: the data does not need to be necessarily represented by structures, it can be even variadic template argument too, ex.: <size_t, Value, size_t, PtrValue, bool, BoolValue>
Here is an pseudo-code example of what i am trying to achieve:
template<class TMember, class TData>
TMember GetElement(INPUT_DATA *Input, TData Data)
{
#ifdef FIRST_CASE
if(indexof(TMember) == 0)
{
return Input->DataVector[0];
}
if(indexof(TMember) == 1)
{
return Input->DataVector[1];
}
return Input->DataBuffer[indexof(TMember) - 2];
#else
if(indexof(TMember) == 0)
{
return Input->DataVector[2];
}
if(indexof(TMember) == 1)
{
return Input->DataVector[3];
}
if(indexof(TMember) == 2)
{
return Input->DataVector[4];
}
if(indexof(TMember) == 3)
{
return Input->DataVector[5];
}
return Input->DataBuffer[indexof(TMember)];
#endif
}
size_t tmpValue = GetElement<Value>(input,DATA_0);
size_t tmpPtrBuffer = GetElement<PtrBuffer>(input,DATA_1);
I ended up using some (finitely) recursing macros to generate class types, that has named functions, that map to different data fields, depending on compile-time macros:
template<class TBase> class DataMapping : public TBase {
protected:
template<class T> inline const T &GetData(size_t Index) const{
#ifdef FIRST_CASE
return (T &)(this->GetDataBuffer()[Index]);
#elif SECOND_CASE
if (0 == Index) { return (T &)(this->DataVector()->data_0); }
if (1 == Index) { return (T &)(this->DataVector()->data_1); }
if (2 == Index) { return (T &)(this->DataVector()->data_2); }
return (T &)(this->GetDataBuffer()[Index]);
#elif THIRD_CASE
if (0 == Index) { return (T &)(this->DataVector()->data_0); }
if (1 == Index) { return (T &)(this->DataVector()->data_1); }
return (T &)(this->GetDataBuffer()[Index]);
#endif
}
};
#define GEN_GETTER(_Type, _Name, _Index) \
typedef _Type FIELD_##_Name##_TYPE; \
inline const FIELD_##_Name##_TYPE &Data_##_Name() const { \
return GetData<_Type>(_Index); \
} \
inline FIELD_##_Name##_TYPE & Data_##_Name() { \
return const_cast<_Type &>(const_cast<decltype(this)>(this)->GetData<_Type>(_Index)); \
}
#define EXPAND(_expr) _expr
#define GEN_GETTER_0(...)
#define GEN_GETTER_1(_Index, _Type, _Name) GEN_GETTER(_Type, _Name, _Index)
#define GEN_GETTER_2(_Index, _Type, _Name, ...) GEN_GETTER(_Type, _Name, _Index) EXPAND(GEN_GETTER_1(_Index + 1, __VA_ARGS__))
...
#define GEN_GETTER_31(_Index, _Type, _Name, ...) GEN_GETTER(_Type, _Name, _Index) EXPAND(GEN_GETTER_30(_Index + 1, __VA_ARGS__))
#define GEN_GETTER_32(_Index, _Type, _Name, ...) GEN_GETTER(_Type, _Name, _Index) EXPAND(GEN_GETTER_31(_Index + 1, __VA_ARGS__))
#define GEN_GETTER_N(_NData, ...) EXPAND(GEN_GETTER_##_NData(0, __VA_ARGS__))
#define DECLARE_DATA_MAP(_ClassName, _BaseClass, _NData, ...) \
struct _ClassName : public DataMapping<_BaseClass> { \
GEN_GETTER_N(_NData, __VA_ARGS__) \
};