amarmer / stack-factory-puzzle Goto Github PK
View Code? Open in Web Editor NEWStack Factory Puzzle
Stack Factory Puzzle
Credit: @AlexOteiza
for (auto i = 0; i < 3; i++)
{
X* pX = nullptr;
// Begin
if (i == 0)
{
X0 var;
var.Process();
}
else if (i == 1)
{
X1 var;
var.Process();
}
else
{
X2 var;
var.Process();
}
continue; // I regret nothing
// End
pX->Process();
}
runtime polymorphism
the new struct X
can store any type with void process()
as method, also:
#include <iostream>
#include <string>
#include <memory>
struct X final {
template <typename V>
X& operator=(V v) {
pimpl = std::make_unique<model_t<V>>(std::move(v));
return *this;
}
void process() { pimpl->process(); }
private:
struct concept_t {
virtual ~concept_t() = default;
virtual void process() = 0;
};
template <typename V>
struct model_t final : concept_t {
V value;
explicit model_t(V v) : value{std::move(v)} {}
void process() override { value.process(); }
};
std::unique_ptr<concept_t> pimpl;
};
struct X0 final {
X0() { std::cout << "X0::X0 "; }
~X0() { std::cout << "\tX0::~X0\n"; }
void process() { std::cout << "X0::Process: " << c_; }
char c_ = 'A';
};
struct X1 final {
X1() { std::cout << "X1::X1 "; }
~X1() { std::cout << "\tX1::~X1\n"; }
void process() { std::cout << "X1::Process: " << s_; }
std::string s_ = "ABC";
};
struct X2 final {
X2() { std::cout << "X2::X2 "; }
~X2() { std::cout << "\tX2::~X2\n"; }
void process() { std::cout << "X2::Process: " << i_; }
int i_ = 123;
};
int main() {
for (auto i = 0; i < 3; i++) {
X x;
if (i == 0)
x = X0{};
else if (i == 1)
x = X1{};
else
x = X2{};
x.process();
}
return 0;
}
static_assert(std::is_move_constructible<X>::value, "");
static_assert(std::is_move_assignable<X>::value, "");
static_assert(std::is_move_constructible<X0>::value, "");
static_assert(std::is_move_assignable<X0>::value, "");
also a simple functional or CRTP
approach lead to much easier (more performant) implementation.
// Using a factory functions avoids default constructibility of the variant
auto factory = [](auto i)
{
using VX = std::variant<X0,X1,X2>;
if (i == 0)
return VX(std::in_place_type_t<X0>());
else if (i == 1)
return VX(std::in_place_type_t<X1>());
else
return VX(std::in_place_type_t<X2>());
};
// Cast reference to variant<T0,...> to common
// polymorphic base class T of T0, ... . T should
// be a template parameter, but since we're at block
// level T=X is hardwired.
auto commonBaseCast = [](auto& x) -> decltype(auto) {
return std::visit([](auto& x) -> decltype(auto){
return dynamic_cast<X&>(x);
}, x);
};
auto x = factory(i);
pX = &commonBaseCast(x);
The commonBaseCast
could also be replace by single line
pX = std::visit([](auto& x){ return dynamic_cast<X*>(&x);}, x);
but using a named function for the cast nicely documents the intend.
#include <iostream>
#include <string>
#include <memory>
struct X
{
friend struct X0;
friend struct X1;
friend struct X2;
virtual ~X() {}
virtual void Process() = 0;
private:
X() {}
};
struct X0 final : public X
{
X0() { std::cout << "X0::X0 "; }
~X0() { std::cout << "\tX0::~X0\n"; }
void Process() override { std::cout << "X0::Process: " << c_; }
char c_ = 'A';
};
struct X1 final : public X
{
X1() { std::cout << "X1::X1 "; }
~X1() { std::cout << "\tX1::~X1\n"; }
void Process() override { std::cout << "X1::Process: " << s_; }
std::string s_ = "ABC";
};
struct X2 final : public X
{
X2() { std::cout << "X2::X2 "; }
~X2() { std::cout << "\tX2::~X2\n"; }
void Process() override { std::cout << "X2::Process: " << i_; }
int i_ = 123;
};
int main()
{
for (auto i = 0; i < 3; i++)
{
X* pX = nullptr;
// Begin
struct Test
{
struct tag0 {};
struct tag1 {};
struct tag2 {};
X* x;
union
{
X0 x0;
X1 x1;
X2 x2;
};
Test(tag0) : x0{} { x = &x0; }
Test(tag1) : x1{} { x = &x1; }
Test(tag2) : x2{} { x = &x2; }
~Test()
{
x->~X();
}
static Test get(int x)
{
switch(x)
{
case 0:
return Test{tag0{}};
case 1:
return Test{tag1{}};
case 2:
return Test{tag2{}};
};
}
};
Test test = Test::get(i);
pX = test.x;
// End
pX->Process();
}
return 0;
}
AKA Abusing statics and abort.
int main()
{
for( auto i = 0; i < 3; i++ )
{
X* pX = nullptr;
// Begin
struct Aborter
{
~Aborter()
{
abort();
}
};
if( i == 0 )
{
static X0 x;
pX = &x;
}
else if( i == 1 )
{
static X1 x;
pX = &x;
}
else
{
static X2 x;
pX = &x;
static Aborter c;
}
struct DestructorCaller
{
~DestructorCaller()
{
p->~X();
}
X* p;
};
DestructorCaller d{ pX };
// End
pX->Process();
}
return 0;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.