Skip to content

Instantly share code, notes, and snippets.

@shlomil
Last active June 18, 2017 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shlomil/79ddde6f55a0b129f01c870739b3f4e7 to your computer and use it in GitHub Desktop.
Save shlomil/79ddde6f55a0b129f01c870739b3f4e7 to your computer and use it in GitHub Desktop.
C++: Creating polymorphic objects on the stack
#include <cstring>
#include <iostream>
#include "StackVariant.h"
class Animal{
public:
virtual void makeSound() = 0;
virtual std::string name() = 0;
virtual ~Animal() = default;
};
class Dog : public Animal{
char dogname[6];
int feedingTime[3];
public:
void makeSound() final { std::cout << "woff" << std::endl; };
std::string name() final { return dogname; };
Dog(){ std::strcpy(dogname,"moshe"); };
void woff() {std::cout << "woff!" << std::endl;}
~Dog() {std::cout << "woff bye!" << std::endl;}
};
class Cat : public Animal{
std::string catname;
public:
Cat() : catname("gonzo") {};
Cat(const std::string& _name, int nt0) : catname(_name) { napTime[0] = nt0; };
void makeSound() final { std::cout << "miuw" << std::endl; };
int napTime[70];
std::string name() final { return catname; };
};
using StackAnimal = StackVariant<Animal, Dog, Cat>;
int main() {
StackAnimal a1;
StackAnimal a2;
StackAnimal a3;
a1.init<Cat>(); // or 'new (&a1) Cat();'
a2.init<Dog>(); // or 'new (&a2) Dog();'
a3.init<Cat>("gonzo2",5); // or 'new (&a3) Cat("gonzo2",5)'
std::cout << sizeof(Cat) << std::endl;
std::cout << sizeof(Dog) << std::endl;
std::cout << sizeof(StackAnimal) << std::endl;
a1->makeSound();
a2->makeSound();
std::cout << a1->name() << std::endl;
std::cout << a2->name() << std::endl;
std::cout << a3->name() << std::endl;
if(a1.canBe<Dog>()) a1.as<Dog>()->woff();
if(a2.canBe<Dog>()) a2.as<Dog>()->woff();
return 0;
}
// Output:
// 320
// 32
// 320
// miuw
// woff
// gonzo
// moshe
// gonzo2
// woff!
// woff bye!
#ifndef ___STACKVARIANT_H
#define ___STACKVARIANT_H
// compile: g++ file.cpp -std=c++11
// Code by Shlomi Loubaton
// free for any use
#include <type_traits>
#include <cstddef>
template<typename...>
struct ___typelist{};
template<typename T, typename ... Rest>
constexpr size_t ___union_size(___typelist<T,Rest...>)
{
return (sizeof(T) > ___union_size(___typelist<Rest...>()))
? (sizeof(T))
: (___union_size(___typelist<Rest...>()));
};
constexpr size_t ___union_size(___typelist<>) { return 0; }
template<typename...classes>
constexpr size_t union_size(){
return ___union_size(___typelist<classes...>());
}
template<typename T, typename ... Rest>
constexpr size_t ___union_align(___typelist<T, Rest...>)
{
return (alignof(T) > ___union_align(___typelist<Rest...>()))
? (alignof(T))
: (___union_align(___typelist<Rest...>()));
};
constexpr size_t ___union_align(___typelist<>) { return alignof(char); }
template<typename...classes>
constexpr size_t union_align() {
return ___union_align(___typelist<classes...>());
}
template<class Tbaseclass, typename...classes>
class StackVariant {
alignas(union_align<classes...>()) char storage[union_size<classes...>()];
bool initialized = false;
public:
inline Tbaseclass* operator->() { return ((Tbaseclass*)storage); }
template<class C, typename...TCtor_params>
StackVariant& init(TCtor_params&&...fargs)
{
initialized = true;
new (storage) C(std::forward<TCtor_params>(fargs)...); // "placement new"
return *this;
};
//call_dtor defined by SFINAE (Substitution Failure Is Not An Error)
template<class X = Tbaseclass>
typename std::enable_if<std::has_virtual_destructor<X>::value, void>::type
deinit() {
((X*)storage)->~X();
initialized = false;
}
template<class X = Tbaseclass>
typename std::enable_if<!std::has_virtual_destructor<X>::value, void>::type
deinit() { initialized = false; };
~StackVariant() { if(initialized) deinit();}
bool isInitialized() const { return initialized; };
template<class X>
bool canBe() {
return initialized && (dynamic_cast<X*>((Tbaseclass*)storage) != nullptr);
};
template<class X>
X* as() {
return dynamic_cast<X*>((Tbaseclass*)storage);
};
};
#endif //___STACKVARIANT_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment