Last active
June 18, 2017 20:03
-
-
Save shlomil/79ddde6f55a0b129f01c870739b3f4e7 to your computer and use it in GitHub Desktop.
C++: Creating polymorphic objects on the stack
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 <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! |
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 ___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