Last active
February 9, 2021 08:03
-
-
Save tringenbach/10013369 to your computer and use it in GitHub Desktop.
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
#!/bin/bash | |
clang++ -std=c++11 -Wall -Wextra -Weffc++ emplacer_test.cc -g3 |
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
/* 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 |
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 <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