Git Product home page Git Product logo

stack-factory-puzzle's People

Contributors

amarmer avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

stack-factory-puzzle's Issues

The "I regret nothing" solution

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();
}

sean parent's approach

runtime polymorphism
the new struct X can store any type with void process() as method, also:

  • no inheritance is required (X0,X1,X2)
  • value semantics
  • no coupling

online code

#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.

Solution with std::variant, no default constructibility, and no redundant `std::get<XY>(x)` calls

    // 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.

Not quite elegant solution, but works in GCC

#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;
}

The "please dear god never do this again" solution

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;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.