How to Store Variant Data in C++

How to store variant data in C++

As of C++17, there’s std::variant.

If you can’t use that yet, you might want Boost.Variant. A similar, but distinct, type for modelling polymorphism is provided by std::any (and, pre-C++17, Boost.Any).

Just as an additional pointer, you can look for “type erasure”.

C++ Storing variant types in map

The obvious solution is to use just a single function that parses and consumes a string and build that directly with a little lambda:

std::map<std::string, std::function(void(std::string))> mymap = {
{ "F1", [](std::string in) { TypesafeIntegerSink(StringToInt(in)); } },
{ "F2", [](std::string in) { TypesafeXSink(StringToX(in)); } },
...

How to store different data types in one list? (C++)

C++ is a multi-paradigm language. It shines brightest and is most powerful where paradigms are mixed.

class Property
{
public:
Property(const std::string& name) //note: we don't lightly copy strings in C++
: m_name(name) {}
virtual ~Property() {}
private:
std::string m_name;
};

template< typename T >
class TypedProperty : public Property
{
public:
TypedProperty (const std::string& name, const T& data)
: Property(name), m_data(data);
private:
T m_data;
};

typedef std::vector< std::shared_ptr<Property> > property_list_type;

Edit: Why using std::shared_ptr<Property> instead of Property*?

Consider this code:

void f()
{
std::vector<Property*> my_property_list;
for(unsigned int u=0; u<10; ++u)
my_property_list.push_back(new Property(u));

use_property_list(my_property_list);

for(std::vector<Property*>::iterator it=my_property_list.begin();
it!=my_property_list.end(); ++it)
delete *it;
}

That for loop there attempts to cleanup, deleting all the properties in the vector, just before it goes out of scope and takes all the pointers with it.

Now, while this might seem fine for a novice, if you're an only mildly experienced C++ developer, that code should raise alarm bells as soon as you look at it.

The problem is that the call to use_property_list() might throw an exception. If so, the function f() will be left right away. In order to properly cleanup, the destructors for all automatic objects created in f() will be called. That is, my_property_list will be properly destroyed. std::vector's destructor will then nicely cleanup the data it holds. However, it holds pointers, and how should std::vector know whether these pointers are the last ones referencing their objects?

Since it doesn't know, it won't delete the objects, it will only destroy the pointers when it destroys its content, leaving you with objects on the heap that you don't have any pointers to anymore. This is what's called a "leak".

In order to avoid that, you would need to catch all exceptions, clean up the properties, and the rethrow the exception. But then, ten years from now, someone has to add a new feature to the 10MLoC application this has grown to, and, being in a hurry, adds code which leaves that function prematurely when some condition holds. The code is tested and it works and doesn't crash - only the server it's part of now leaks a few bytes an hour, making it crash due to being out of memory about once a week. Finding that makes for many hours of fine debugging.

Bottom line: Never manage resources manually, always wrap them in objects of a class designed to handle exactly one instance of such a resource. For dynamically allocated objects, those handles are called "smart pointer", and the most used one is shared_ptr.

How do you access to SAFE ARRAY in a VARIANT data type returned from a COM object in C++?

In order to use a SAFEARRAY in a VARIANT like this, you need to do a lot of validation and error-checking. Here's the rough pattern you'll need to follow. Please read the comments carefully, as I have made some assumptions about the COM API that you're using.

// verify that it's an array
if (V_ISARRAY(&namList))
{
// get safe array
LPSAFEARRAY pSafeArray = V_ARRAY(&namList);

// determine the type of item in the array
VARTYPE itemType;
if (SUCCEEDED(SafeArrayGetVartype(pSafeArray, &itemType)))
{
// verify it's the type you expect
// (The API you're using probably returns a safearray of VARIANTs,
// so I'll use VT_VARIANT here. You should double-check this.)
if (itemType == VT_VARIANT)
{
// verify that it's a one-dimensional array
// (The API you're using probably returns a one-dimensional array.)
if (SafeArrayGetDim(pSafeArray) == 1)
{
// determine the upper and lower bounds of the first dimension
LONG lBound;
LONG uBound;
if (SUCCEEDED(SafeArrayGetLBound(pSafeArray, 1, &lBound)) && SUCCEEDED(SafeArrayGetUBound(pSafeArray, 1, &uBound)))
{
// determine the number of items in the array
LONG itemCount = uBound - lBound + 1;

// begin accessing data
LPVOID pData;
if (SUCCEEDED(SafeArrayAccessData(pSafeArray, &pData)))
{
// here you can cast pData to an array (pointer) of the type you expect
// (The API you're using probably returns a safearray of VARIANTs,
// so I'll use VARIANT here. You should double-check this.)
VARIANT* pItems = (VARIANT*)pData;

// use the data here.

// end accessing data
SafeArrayUnaccessData(pSafeArray);
}
}
}
}
}
}


Related Topics



Leave a reply



Submit