HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Variant class using generic programming

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericvariantprogrammingusingclass

Problem

My goal is to learn a bit more about generic programming in C++. So, one of the trickiest things I've heard you can do is creating a Variant class. This took me a while and I needed to read and study a few implementations:

```
#ifndef VARIANT_HPP_INCLUDED
#define VARIANT_HPP_INCLUDED

#include
#include
#include

#include

namespace spaceengine
{
namespace utils
{
template
struct VariantHelper;

template
struct VariantHelper
{
inline static void Destroy(const std::type_index typeIndex, void* data)
{
if(typeIndex == std::type_index(typeid(F)))
reinterpret_cast(data)->~F();
else
VariantHelper::Destroy(typeIndex,data);
}

inline static void Move(const std::type_index oldTypeIndex, void oldV, void newV)
{
if(oldTypeIndex == std::type_index(typeid(F)))
new (newV) F(std::move(*reinterpret_cast(oldV)));
else
VariantHelper::Move(oldTypeIndex,oldV,newV);
}

inline static void Copy(const std::type_index oldTypeIndex, const void oldV, void newV)
{
if(oldTypeIndex == std::type_index(typeid(F)))
new (newV) F(*reinterpret_cast(oldV));
else
VariantHelper::Copy(oldTypeIndex,oldV,newV);
}
};

template<>
struct VariantHelper<>
{
inline static void Destroy(const std::type_index typeIndex, void* data){}
inline static void Move(const std::type_index oldTypeIndex, void oldV, void newV) {}
inline static void Copy(const std::type_index oldTypeIndex, const void oldV, void newV) {}
};

template
class Variant
{
public:
Variant() : m_typeIndex(InvalidType()){}

Variant(const Variant& old

Solution

Does my use of placement new mean that my Variant class is stack allocated, or have I misunderstood what placement new does?

It does mean that, and your implementation using the std::union to provide sufficient allocated space appears to be valid.

Almost at least, because your implementation of Variant::Set() is not checking types. So if a type is accidentally passed which wasn't covered within Ts, respectively requires more allocated space, it is going to corrupt the memory.


But I can't decipher boost::variant well enough to figure out what it does that mine doesn't do.

For instance, the boost implementation gives an never empty guarantee which your implementation does not. You are explicitly handling the case in which the Variant is in an invalid state.

In Variant::Set(), you are explicitly destroying old m_data, but you are not reverting m_typeIndex to a safe state prior to invoking the new constructor. If the new constructor would throw, it would leave m_typeIndex on the old value which is plain wrong.

Consider adding an Variant::Unset() method which handles save destruction instead and is guaranteed to set m_typeIndex explicitly to the invalid type.

This also leads to invoking the destructor on invalid states.

That recursion in the template of VariantHelper. Not going to lie - that's rather difficult to trace. A comment would have been in place, explaining what you are going to do.

Context

StackExchange Code Review Q#127372, answer score: 3

Revisions (0)

No revisions yet.