Skip to content

Instantly share code, notes, and snippets.

@tringenbach
Last active February 9, 2021 08:03
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tringenbach/10013369 to your computer and use it in GitHub Desktop.
Save tringenbach/10013369 to your computer and use it in GitHub Desktop.
#!/bin/bash
clang++ -std=c++11 -Wall -Wextra -Weffc++ emplacer_test.cc -g3
/* This code is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#ifndef EMPLACER_H
#define EMPLACER_H
#include <new>
#include <type_traits>
#include <stdexcept>
template <typename ...Elements>
class type_collection;
template <>
class type_collection<> {
public:
static constexpr size_t max_size() { return 0; }
static constexpr size_t max_alignment() { return 0; }
template <typename Type>
static constexpr bool is_member() { return false; }
template <typename Type>
static constexpr bool are_all_subtypes() { return true; }
template <typename Type>
static void copy(const Type &, char *) { throw std::logic_error("type_collecion::copy reached zero argument specialization"); }
template <typename Type>
static void move(Type &&, char *) { throw std::logic_error("type_collecion::move reached zero argument specialization"); }
};
template <typename First, typename ...Rest>
class type_collection<First, Rest...> {
public:
typedef First first;
static constexpr size_t max_size() {
return sizeof(First) > type_collection<Rest...>::max_size() ? sizeof(First) : type_collection<Rest...>::max_size();
}
static constexpr size_t max_alignment() {
return alignof(First) > type_collection<Rest...>::max_alignment() ? alignof(First) : type_collection<Rest...>::max_alignment();
}
template <typename Type>
static constexpr bool is_member() {
return std::is_same<First, Type>::value ? true : type_collection<Rest...>::template is_member<Type>();
}
template <typename Type>
static constexpr bool are_all_subtypes() {
return std::is_base_of<Type, First>::value ? type_collection<Rest...>::template are_all_subtypes<Type>() : false;
}
template <typename Type>
static void copy(const Type &t, char *data) {
if (typeid(First) == typeid(t)) {
new (data) First(static_cast<const First &>(t));
} else {
type_collection<Rest...>::template copy(t, data);
}
}
template <typename Type>
static void move(Type &&t, char *data) {
if (typeid(First) == typeid(t)) {
new (data) First(static_cast<First &&>(std::move(t)));
} else {
type_collection<Rest...>::template move(std::move(t), data);
}
}
};
template <typename Type, typename TypeCollection>
class emplacer {
private:
char data[TypeCollection::max_size()] __attribute__((align(TypeCollection::max_alignment())));
bool live = false;
void throw_if_not_live() const {
if(!live)
throw std::logic_error("Attempt to use uninitialized object in emplacer");
}
template<typename Subtype> void make_copy(char *d) const {
new (d) Subtype(*reinterpret_cast<const Subtype *>(data));
}
public:
template <typename F = typename TypeCollection::first, class Enabled = typename std::enable_if<std::is_default_constructible<F>::value>::type>
emplacer() {
if (std::is_default_constructible<typename TypeCollection::first>::value) {
new(data) typename TypeCollection::first();
live = true;
}
}
template <typename F = typename TypeCollection::first, class = typename std::enable_if<!std::is_default_constructible<F>::value>::type, bool dummy = false>
emplacer() { }
emplacer(const emplacer& e) : live(e.live) {
if (live)
TypeCollection::copy(*e, data);
}
emplacer(emplacer&& e) : live(e.live) {
if (live)
TypeCollection::move(std::move(*e), data);
}
emplacer& operator=(const emplacer& other) {
live = other.live;
if (live && this != &other) {
(*this)->~Type();
TypeCollection::copy(*other, data);
}
return *this;
}
emplacer& operator=(emplacer&& other) {
live = other.live;
if (live && this != &other) {
(*this)->~Type();
TypeCollection::move(std::move(*other), data);
}
return *this;
}
template<typename Subtype, class = typename std::enable_if<std::is_base_of<Type,Subtype>::value || std::is_same<Type,Subtype>::value>::type>
emplacer(Subtype& copy) {
static_assert(TypeCollection::template are_all_subtypes<Type>(), "TypeCollection contains non-subclasses of Type");
static_assert(TypeCollection::template is_member<Subtype>(), "Subtype not part of TypeCollection");
new(data) Subtype(copy);
live = true;
}
template<typename Subtype, class = typename std::enable_if<std::is_base_of<Type,Subtype>::value || std::is_same<Type,Subtype>::value>::type>
emplacer(Subtype&& move) {
static_assert(TypeCollection::template are_all_subtypes<Type>(), "TypeCollection contains non-subclasses of Type");
static_assert(TypeCollection::template is_member<Subtype>(), "Subtype not part of TypeCollection");
new(data) Subtype(std::move(move));
live = true;
}
template <typename Subtype, typename ...Args>
emplacer& emplace(Args ...args) {
static_assert(TypeCollection::template are_all_subtypes<Type>(), "TypeCollection contains non-subclasses of Type");
static_assert(TypeCollection::template is_member<Subtype>(), "Subtype not part of TypeCollection");
if(live) {
(*this)->~Type();
live = false;
}
new(data) Subtype(args...);
live = true;
return *this;
}
~emplacer() {
if(live)
(*this)->~Type();
}
const Type &operator*() const { throw_if_not_live(); return *reinterpret_cast<const Type *>(data); }
Type &operator*() { throw_if_not_live(); return *reinterpret_cast<Type *>(data); }
const Type *operator->() const { throw_if_not_live(); return reinterpret_cast<const Type *>(data); }
Type *operator->() { throw_if_not_live(); return reinterpret_cast<Type *>(data); }
};
#endif
#include <iostream>
#include <vector>
#include "../emplacer/emplacer.hpp"
class Shape {
public:
virtual double area() const = 0;
};
class Square : public Shape {
double width;
public:
Square(double w) : width(w) { }
Square() = default;
virtual double area() const override { return width * width; }
};
class Rect : public Shape {
double width;
double height;
public:
Rect(double w, double h) : width(w), height(h) { }
Rect() = default;
virtual double area() const override { return width * height; }
};
class Circle: public Shape {
double radius;
public:
Circle(double r) : radius(r) { }
Circle() = delete;
virtual double area() const override { return 3.14159 * radius * radius; }
};
typedef emplacer<Shape, type_collection<Rect, Square, Circle>> ShapeUnion;
typedef emplacer<Shape, type_collection<Circle, Rect, Square>> ShapeUnion2;
struct ShapeAny : emplacer<Shape, type_collection<Rect, Square, Circle>>, Shape {
using emplacer::emplacer;
virtual double area() const override { return (*this)->area(); }
};
int main(int, char **) {
std::vector<ShapeUnion> shapes;
shapes.emplace(shapes.end())->emplace<Rect>(3, 4);
shapes.emplace(shapes.end())->emplace<Square>(3);
shapes.push_back(ShapeUnion().emplace<Rect>(5, 6));
shapes.push_back(ShapeUnion().emplace<Circle>(1));
ShapeUnion rect(Rect(7, 13));
for(auto shape: shapes) {
std::cout << shape->area() << std::endl;
}
std::vector<ShapeUnion> shapes2(10);
for (auto shape: shapes2) {
std::cout << shape->area() << std::endl;
}
ShapeAny x(Square(4));
std::cout << "x's area is " << x.area() << std::endl;
std::vector<ShapeUnion2> shapes3(10);
for (auto shape: shapes3) {
std::cout << shape->area() << std::endl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment