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