简体   繁体   中英

Templatized Storing Multiple Different Types In std::vector

thank you all for your time I really appreciate it.

There exists a need to store multiple variables of different types in a std::vector using a templatized fashion. To use the following functions the programmer must be aware of which order they stored the variables and how many variables were stored.

class _NetVar {};

A sub class is created to hold the actual variable:

template <class VARTYPE> class NetVar : public _NetVar
{
private:
    VARTYPE Var;
    NetVar(VARTYPE Value)
    {
        Var = Value;
    }
};

A vector is created of the base helper class

std::vector<_NetVar> DataVec;

Data is put into the vector like so:

template <class DATATYPE> void AddData(DATATYPE AddData)
{
    DataVec.push_back(NetVar<DATATYPE>(AddData));
}

Data is pulled out of the vector here, an internal variable holds the current position of the vector and is incremented each time a variable is requested:

template <class DATATYPE> DATATYPE GetData()
{
    NetVar<DATATYPE> Temp = PacketData[VecPos];

    return Temp.Var;
    ++VecPos;
}

The previous function is where the trouble arises, it is possible to recognize the sub class as it's base class but is it possible to recognize the base class as one of it's sub classes?

Here is how the code is being used:

AddData<int>(32);
AddData<bool>(true);
AddData<std::string>("Test");

auto Var1 = GetData<int>();
auto Var2 = GetData<bool>();
auto Var3 = GetData<std::string>();

When GetData is called an exception is thrown:

'initializing' : cannot convert from '_NetVar' to 'NetVar<DATATYPE>'

If anyone can help me with this it would be greatly appreciated, thank you again for your time.

Note: External libraries such as Boost need to be avoided.

The vector should be:

std::vector<_NetVar *> DataVec;

or a high level pointer

std::vector<std::shared_ptr<_NetVar> > DataVec;

so that you can store instances of the child classes instead of slicing them to the base class.

On GetData you will need to upcast the pointer retrieved from the vector.


Edit: Adding a full blown working code

Example working on ideone , had to fiddle with the permissions a bit.

and the example goes with some comments added.

#include <iostream>
#include <vector>
#include <memory>
class _NetVar {};

template <class VARTYPE> 
class NetVar : public _NetVar
{
private:
    VARTYPE Var;
public:
    NetVar(VARTYPE Value)
    {
        Var = Value;
    }
};

Note that I changed NetVar<> constructor and Var attribute to be public... AddData and GetData needed to access it.

Not sure if in your example you had some virtual methods on _NetVar (in which case the static_pointer_cast below could be a dynamic_pointer_cast )

Related to that, you might want to veriy that the destructors for NetVar (and not only the destructors for _NetVar) are being called (checked on ideone, they work in my example because I'm using std::make_shared<NetVar<XX> >(...) )

std::vector<std::shared_ptr<_NetVar> > DataVec;
int VecPos;

Added this global variables for the functions below to work.

template <class DATATYPE> void AddData(DATATYPE AddData)
{
    DataVec.push_back(std::make_shared<NetVar<DATATYPE> >(AddData));
}

So here we create a shared_ptr with new object NetVar<DATATYPE> and push it into the vector.

template <class DATATYPE> DATATYPE GetData()
{
    std::shared_ptr<_NetVar> content = DataVec[VecPos];
    std::shared_ptr<NetVar<DATATYPE> > Temp = std::static_pointer_cast<NetVar<DATATYPE> >(content);
    ++VecPos;

    return Temp->Var;
}

Here, the content of the vector is std::shared_ptr<_NetVar> so that's what we get. That shared_ptr needs to be upcasted to the right kind of shared_ptr

Now there's a concern that you must know the right type to upcast to, otherwise is undefined behaviour. If you had virtual methods you could use dynamic_pointer_cast and then perform a check for null... but that has some performance penalties

int main() {

    AddData<int>(32);
    AddData<bool>(true);
    AddData<std::string>("Test");

    auto Var1 = GetData<int>();
    auto Var2 = GetData<bool>();
    auto Var3 = GetData<std::string>();

    std::cout << Var1 << std::endl;
    std::cout << Var2 << std::endl;
    std::cout << Var3 << std::endl;
    return 0;
}

Finally testing and printing the resuls.

One solution is to get rid of _NetVar and use Boost.Any , as in:

std::vector<boost::any> DataVec;

template<class T>
void AddData(T data)
{ DataVec.emplace_back(std::move(data)); }

template<class T>
T GetData()
{ return boost::any_cast<T>(DataVec[VecPos++]); }

AddData(32);
AddData(true);
AddData(std::string("test"));

auto Var1 = GetData<int>();
auto Var2 = GetData<bool>();
auto Var3 = GetData<std::string>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM