1

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.

7
  • There is no straightforward "way to avoid specifying [...] type" at some point because C++ is strongly typed. Commented Dec 12, 2016 at 3:16
  • Its OK to specify types for every possible field. I just want to do so once. Not every time I request value. Look at macro solution. It allows me to do printf("%d\n", V1); - easy. Just want to check there are no better way... And I am not missing something simple and cool. Commented Dec 12, 2016 at 3:23
  • There is no difference between a macro and a template here. You specify the type every time you want to use it (either by choosing the appropriate macro or template paramter). Commented Dec 12, 2016 at 3:24
  • I am just not sure I will be able to remember types of all fields during development... I want to specify them once and use simple function (or macro) later. Seems like macro is what i need. Sorry! Just wanted to make sure I am not missing something important you know! Commented Dec 12, 2016 at 3:29
  • You need to know the type anyway because you need to use a different macro for every type. Commented Dec 12, 2016 at 14:33

2 Answers 2

1

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.

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

12 Comments

If not, boost provides those.
I checked boost::any briefly and was sure it also requires specifying of type when requesting value like here: any_cast<std::string>(&operand); - it IS an option of course. But maybe there are simpler ways...
@Sion0 How would you use a value without knowing what kind of value you were using? What would happen if you wrote operand.get() + 1 and operand turned out to contain a PinkFluffyHedgehog?
I want to use function like container.get("someValue") knowing that "someValue" is always int*
@Sion0: What would the return type of get be?
|
0

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.

2 Comments

What's the advantage of this over struct Cont {int value1; double value2;} Memory footprint you said, in the case that there are many fields? You do have a static object in memory for each field, but only one. Are there many such Cont objects? I wonder if you can constexpr the field type/code objects.
Yes, many Conts and many possible Fields... With this method I hope to save some memory and make my "structures" extendable by plugins. Some function in some plugin can take Cont as argument and not just modify existing fields (like with regular struct) but also add new ones. I still need plugin's header file to read new fields but it's ok...

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.