0

In my C++ / QT application I've got a struct with hundreds of variables organized like below:

struct MyStruct
{
    int a1;
    double a2;
    struct InnerStruct
    {
        int b1;
        bool b2;
        struct InnerInnerStruct
        {
            char c1;
              .
              .
              .
        };
        InnerInnerStruct b3;
          .
          .
          .

    };
    InnerStruct a3;
      .
      .
      .
};

I want to have a map of the addresses of each struct member variable, which has integer as the key type so I'll be able to access it via its key:

MyStruct ms;
theMap[0] = &(ms.a1);
theMap[1] = &(ms.a2);
theMap[2] = &(ms.a3.b1);
theMap[3] = &(ms.a3.b2);
theMap[4] = &(ms.a3.b3.c1);

theMap[3] = true; // MyStruct::InnerStruct::b2 is 'true' now

how should I define theMap object? what kind of esotericism do I need to have this feature? plain templates? boost stuff? some offset trick? QVariant somehow?

Note: Unfortunately Qt's property system is not an option.


Edit: About the strange nature of my request: I receive data frames from somewhere, say, a device. first 4-bytes of the data specifies the type of the frame. so whenever I receive this data, I must put it into Device struct's corresponding member variable. Since there're hundreds of different data types and since I need to use this kind of matching more than one place I wanted to get rid of the drudge work -or at least do it once- ("if/else" and "switch" statements).

sithereal
  • 1,656
  • 9
  • 16
  • 3
    Why? There may be a way to do this using type erasure, but this mainly smacks of needing a design overhaul. – tmpearce Jun 25 '12 at 00:43
  • When you receive a frame, it's up to *you* to decide what to do with it. Putting it into a big twisted mess of a data structure is one bad approach. Don't suggest it was thrown onto you from up high as it was your choice, it's bad, and you probably don't want to be using such a mess of a structure to hold your data. Having this messy data structure may be a sorta-kinda way only in very specific circumstances, namely if you're mapping a process image of, say, a PLC. – Kuba hasn't forgotten Monica Jun 25 '12 at 13:37

1 Answers1

0

Update: See my other answer to your question. It'd be much cleaner to put each data frame into its own element in a tree data model. The children of such element would be the data members in the frame. This matches with your situation of various frames having varying numbers of elements. If each frame had same number of elements, then a table model would be better suited. Qt's model-view system uses QVariants for all data, and provides means of indexing into arbitrarily complex models, so why reinvent the wheel.

Internally you can still use the structures to hold the data, but at least you don't need to have a fixed global (outer) structure. When creating a prototype model, each toplevel element (the topmost row, for example), can be assigned its data type, internally creating the underlying data structure, etc.

Now going back to your original approach.

QVariant-like approach will work, and that's about the only way to actually make it work if the integer key is a runtime property. If you want the integer key to be only useful at compile time, then of course integer-speciliazed templated functions/functors/class members would do the trick.

Now that the value is assigned to a variant, the "variant" has to forward the assignment to the target member.

theMap can be any container type you desire. The functionality is in the "variant". Let's call it Handle, since it essentially is a handle to a variable.

You can code in support for conversions, for example it might be useful to let the double handle allow assignment of integers.

Let me repeat, though: it's some truly wicked design, and I see no need for anything so broken. It's an antipattern. It screams: someone messed up. Don't do it.

Below is the most trivial of implementations.

#include <QMap>
#include <QVariant>

class Handle
{
    QVariant::Type type;
    void * address;
public:
    Handle() : type(QVariant::Invalid), address(0) {}
    explicit Handle(int* p) : type(QVariant::Int), address(p) {}
    explicit Handle(bool * p) : type(QVariant::Bool), address(p) {}
    explicit Handle(double* p) : type(QVariant::Double), address(p) {}
    Handle & operator=(int* p) { return *this = Handle(p); }
    Handle & operator=(int v) {
        if (type == QVariant::Int) { *(int*)address = v; }
        else if (type == QVariant::Double) { *(double*)address = v; }
        else Q_ASSERT(type == QVariant::Invalid);
        return *this;
    }
    int toInt() const { Q_ASSERT(type == QVariant::Int); return *(int*)address; }
    Handle & operator=(bool* b) { return *this = Handle(b); }
    Handle & operator=(bool b) {
        Q_ASSERT(type == QVariant::Bool); *(bool*)address = b;
        return *this;
    }
    bool toBool() const { Q_ASSERT(type == QVariant::Bool); return *(bool*)address; }
    Handle & operator=(double* p) { return *this = Handle(p); }
    Handle & operator=(double d) {
        Q_ASSERT(type == QVariant::Double); *(double*)address = d;
        return *this;
    }
    int toDouble() const { Q_ASSERT(type == QVariant::Double); return *(double*)address; }
};

struct MyStruct
{
    int a1;
    double a2;
    struct InnerStruct
    {
        int b1;
        bool b2;
    } b3;
};

int main()
{
    MyStruct s;
    QMap<int, Handle> map;
    map[0] = &s.a1;
    map[1] = &s.a2;
    map[2] = &s.b3.b1;
    map[3] = &s.b3.b2;
    map[0] = 10;
    map[1] = 1.0;
    map[2] = 20;
    map[3] = true;
    return 0;
}
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313