1

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:

  • the mapping rule is being controlled by #defines/compile time switches - for a given compilation configuration, the mapping rule is the same for all structures.
  • a given data structure member can be mapped to either a DataBuffer or a DataVector
  • a data structure member's index (no need for offset, since the data is aligned) defines the element it is mapped to, ex.: in some cases the element at index 0 (DATA_0::Value or DATA_1::Value0 for example) maps to DataVector[3], and in other cases to DataBuffer[0]
  • for those elements, that have no index based mapping rules (usually the first 2-4 elements from a structure have such rule), they all should map to sequential elements, onward from a given element from DataBuffer

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);

2 Answers 2

1

Value and PtrBuffer are not valid names on their own. &DATA_0::PtrBuffer and &DATA_1::PtrBuffer are, but they have different types, so you'll have a whole load of overloads of operator[] for your desired syntax.

I think the easiest thing for you to do here is just write out some functions

DATA_0 to_data_0(INPUT input) {
    DATA_0 result;
    result.Value = input.DataVector[0];
    // etc.
    return result;
}

DATA_1 to_data_1(INPUT input) {
    DATA_1 result;
    result.Value0 = input.DataVector[2];
    // etc.
    return result;
}

If you have different configurations, then you will need different versions of these functions.

Sign up to request clarification or add additional context in comments.

Comments

0

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__) \
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.