Last active
January 5, 2022 04:57
-
-
Save IlyaSkriblovsky/1c60a4de593bc5a4d8c241d711a25f00 to your computer and use it in GitHub Desktop.
Typing to implement dynamic Variant type in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include "Variant.h" | |
using namespace std; | |
class Shape | |
{ | |
public: | |
virtual void hello() const = 0; | |
~Shape() | |
{ | |
printf("~Shape()\n"); | |
} | |
}; | |
class Circle: public Shape | |
{ | |
public: | |
int r; | |
Circle(int r): r(r) | |
{ | |
printf("Circle(%d)\n", r); | |
} | |
Circle(const Circle& other): r(other.r) | |
{ | |
printf("Circle(copy %d)\n", r); | |
} | |
virtual ~Circle() | |
{ | |
printf("~Circle()\n"); | |
} | |
void hello() const | |
{ | |
printf("I'm circle r=%d\n", r); | |
} | |
}; | |
class Rectangle: public Shape | |
{ | |
public: | |
int x, y; | |
Rectangle(int x, int y): x(x), y(y) | |
{ | |
printf("Rectangle(%d, %d)\n", x, y); | |
} | |
Rectangle(const Rectangle& other): x(other.x), y(other.y) | |
{ | |
printf("Rectangle(copy %d, %d)\n", x, y); | |
} | |
~Rectangle() | |
{ | |
printf("~Rectangle()\n"); | |
} | |
void hello() const | |
{ | |
printf("I'm rectangle x=%d y=%d\n", x, y); | |
} | |
}; | |
int main() { | |
Circle circle(42); | |
Variant v1(circle); | |
Variant v2; | |
v2 = Rectangle(5, 7); | |
v2 = v1; | |
v1.get<Circle>().hello(); // "I'm circle r=42" | |
v2.get<Rectangle>().hello(); // "Type mismatch: Variant holds Circle, but get<Rectnagle> is called" | |
Variant v3; | |
v3.get<Rectangle>().hello(); // "Variant is empty, but get<Rectangle> is called" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __VARIANT_H | |
#define __VARIANT_H | |
#include <cstdio> | |
#include <typeinfo> | |
#include <map> | |
typedef void (*deleter)(void*); | |
typedef void* (*copier)(const void*); | |
template <typename T> | |
void delete_type(void *ptr) | |
{ | |
delete reinterpret_cast<T*>(ptr); | |
} | |
template <typename T> | |
void* copy_type(const void *src) | |
{ | |
return new T(*reinterpret_cast<const T*>(src)); | |
} | |
// Holder for std::type_info since it is not copy-assignable | |
class TypeIDHolder | |
{ | |
public: | |
TypeIDHolder(): type(&typeid(void)) { } | |
TypeIDHolder(const std::type_info& type): type(&type) { } | |
const std::type_info& get_type_info() const { return *type; } | |
bool operator < (const TypeIDHolder& other) const { return type->before(*other.type); } | |
bool operator == (const TypeIDHolder& other) const { return *type == *other.type; } | |
bool operator != (const TypeIDHolder& other) const { return *type != *other.type; } | |
private: | |
const std::type_info* type; | |
}; | |
class Variant | |
{ | |
public: | |
Variant(): ptr(0) { } | |
template <typename T> | |
Variant(const T& value) | |
{ | |
set(value); | |
} | |
~Variant() | |
{ | |
clear(); | |
} | |
template <typename T> | |
void set(const T& value) | |
{ | |
ptr = new T(value); | |
type = TypeIDHolder(typeid(T)); | |
deleters[typeid(T)] = &delete_type<T>; | |
copiers[typeid(T)] = ©_type<T>; | |
} | |
template <typename T> | |
const T& get() const | |
{ | |
if (! ptr) | |
{ | |
fprintf(stderr, "Variant is empty, but get<%s> is called\n", typeid(T).name()); | |
abort(); | |
} | |
if (type != typeid(T)) | |
{ | |
fprintf(stderr, "Type mismatch: Variant holds %s, but get<%s> is called\n", | |
type.get_type_info().name(), typeid(T).name()); | |
abort(); | |
} | |
return *reinterpret_cast<const T*>(ptr); | |
} | |
void clear() | |
{ | |
if (ptr) | |
{ | |
deleters[type](ptr); | |
ptr = 0; | |
} | |
} | |
template <typename T> | |
void operator = (const T& value) | |
{ | |
clear(); | |
set(value); | |
} | |
void operator = (const Variant& other) | |
{ | |
clear(); | |
ptr = copiers[other.type](other.ptr); | |
type = other.type; | |
} | |
private: | |
void* ptr; | |
TypeIDHolder type; | |
static std::map<TypeIDHolder, deleter> deleters; | |
static std::map<TypeIDHolder, copier> copiers; | |
}; | |
std::map<TypeIDHolder, deleter> Variant::deleters; | |
std::map<TypeIDHolder, copier> Variant::copiers; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment