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.

Sion0
  • 77
  • 7
  • There is no straightforward "*way to avoid specifying [...] type*" at some point because C++ is strongly typed. – Pixelchemist Dec 12 '16 at 03: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. – Sion0 Dec 12 '16 at 03: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). – Pixelchemist Dec 12 '16 at 03: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! – Sion0 Dec 12 '16 at 03:29
  • You need to know the type anyway because you need to use a different macro for every type. – Pixelchemist Dec 12 '16 at 14:33
  • Suppose you don't want or need to remember the type. `printf ("%??????", c.find("v1"))` <--- what do you put here ?????? – n. m. could be an AI Dec 12 '16 at 22:23
  • Fine, I can remember that "v1" is int. But can't remember its size: 1 byte, 2 bytes or 4 bytes. – Sion0 Dec 12 '16 at 22:38

2 Answers2

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.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • 3
    If not, boost provides those. – Michael Albers Dec 12 '16 at 02:31
  • I checked boost::any briefly and was sure it also requires specifying of type when requesting value like here: any_cast(&operand); - it IS an option of course. But maybe there are simpler ways... – Sion0 Dec 12 '16 at 02:50
  • @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`? – user253751 Dec 12 '16 at 03:01
  • I want to use function like container.get("someValue") knowing that "someValue" is always int* – Sion0 Dec 12 '16 at 03:18
  • @Sion0: What would the return type of `get` be? – Pixelchemist Dec 12 '16 at 03:25
  • If you know that "someValue" is always "int *", then you call the template function with an explicit `` exactly like you're doing now. `std::variant` and `std::any` only allow you to store different kinds of objects. In order to access them, you have to know what type they are. In C++, you cannot have the same function called `get()` (or by any other name), return different kinds of types at different times. C++ does not work this way. You can have a facade called `getIntptr()`, for example, as a wrapper for `get()`, and call `getIntptr()` when you know what to expect. – Sam Varshavchik Dec 12 '16 at 03:29
  • You are not listening to me :( I've already posted two examples doing what I want: types of all possible fields specified once + getting them later without specifying type. – Sion0 Dec 12 '16 at 21:18
  • Like with simple struct. Large struct containing all possible fields does what I want. And I don't need to remember exact types to use values. I can call st.v += 1; without remembering size of integer and its signedness. – Sion0 Dec 12 '16 at 21:27
  • No, you're not listening to me: as I explained, "in C++, you cannot have the same function called get() (or by any other name), return different kinds of types at different times". [C++ is a statically typed language](http://stackoverflow.com/questions/1517582/what-is-the-difference-between-statically-typed-and-dynamically-typed-languages), and not a dynamically-typed language. If you want just a variable, and don't care what type it is, you have to use a dynamically-typed language, something other than C++. C++ simply does not work this way. – Sam Varshavchik Dec 12 '16 at 22:00
  • Why not? Function get() can return different types if overloaded. I don't care if compiler creates different functions for each version of template. And macro (not function) is also good for me. I already showed two working options. Stop telling me that this is not possible. – Sion0 Dec 12 '16 at 22:22
  • In C++, you can only overload on the function signatures. If, when you expect a string out of `get()` you pass it one kind of a parameter, and when you expect an `int` you pass a different kind of parameter, you can overload, and write different versions of `get()`. But that's exactly the same as writing a template function. But instead of writing a simple template function once, and using it, you have to both: write different versions of `get()`, for each type you wish to support, ***and*** provide different parameters to `get()`, for each type of a return value you want from it. – Sam Varshavchik Dec 12 '16 at 22:25
  • Can you check example I called "mad and unusable" in my post above? It uses template to overload get(). I am looking for proper way to accomplish same results: getting fields without remembering exact type. And if I change type of some value from uint32 to uint16 during development, I want to change it in ONE file. Not in every place where I call get() – Sion0 Dec 12 '16 at 22:46
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.

Sion0
  • 77
  • 7
  • 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. – Kenny Ostrom Dec 16 '16 at 18:30
  • 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... – Sion0 Dec 16 '16 at 19:57