Reputation: 77
I am trying to create a special container for pointers to different data types. Let's assume we have struct:
struct data
{
int *v1;
double *v2;
};
Number of pointers and their types are fixed and must be known at compile time. But I need a bit different thing. I have a LARGE list of possible fields (type + name). Each field can be present or absent. I could use LARGE struct with all possible fields but I want to save memory. If field is absent, I don't want it to take memory in container. Still all present fields should be accessible by name or unique code without need to cast them or remember exact type.
Real question ends here. Further is my own ideas that you don't have to read and that may confuse you.
Fine, here is what I have now:
class Cont
{
public:
Cont() {}
void map(std::string name, void *p)
{
m[name] = p;
}
template <typename T>
T *find(std::string name)
{
return reinterpret_cast<T *>(m[name]);
}
private:
std::map<std::string, void *> m;
};
void handler()
{
int v1 = 100;
double v2 = 0.1;
Cont c;
c.map("v1", &v1);
c.map("v2", &v2);
printf("%d\n", *c.find<int>("v1"));
printf("%f\n", *c.find<double>("v2"));
}
It works and does what I need. But I am looking for:
1) More elegant and right way (possible);
2) The way to avoid specifying pointer type when calling find(). We don't know at compile time whether value named "v1" will be present. But we know that "v1" is always of "int" type. Currently only solution I can see is using macros like:
#define V1 *c.find<int>("v1")
and then
printf("%d\n", V1);
String "v1" here can be replaced with unique code for faster search.
So, primary goal is to specify types for all possible fields once and only once. Any other ideas? Thanks!
Note: I understand that my example will crash in many cases. I removed important checks for simplicity.
Upvotes: 1
Views: 1083
Reputation: 77
It seems that I've reached the goal! Method requires additional testing but looks good for now.
template <typename T>
class Field
{
public:
Field(uint32_t _code) : code(_code) {}
uint32_t code;
};
class Cont
{
public:
template <typename T>
void set(Field<T> field, void *p)
{
m[field.code] = p;
}
template <typename T>
T *get(Field<T> field)
{
return reinterpret_cast<T *>(m[field.code]);
}
private:
std::map<uint32_t, void *> m;
};
// All possible fields
const Field <int> value1 (0x00000001);
const Field <double> value2 (0x00000002);
int main(int argc, char *argv[])
{
Cont c;
int temp1 = 400;
double temp2 = 0.4;
c.set(value1, &temp1);
c.set(value2, &temp2);
printf("%d\n", *c.get(value1));
printf("%f\n", *c.get(value2));
return 0;
}
Using class named "Field" allows to save both field type and unique code. And then pass it to get() and set() methods.
Upvotes: 0
Reputation: 118435
If your compiler supports C++17 you can use either std::variant or std::any for the values of your map; and your find()
will use either get()
(in variant's case), or std::any_cast to retrieve the value.
Upvotes: 1