1

Recently, I've tried to exercise Mediator pattern, but my program throws two exceptions. One tells me that there's some problem with empty value, and another one says that there's some problem with "this". What am I doing wrong here? I've encountered some issues with calling method "Notify" with "this" pointer because of "shared_ptr" arguments, so I used enable_shared_from_this.

    #include <iostream>
    #include <memory>

    class IComponent;

    class IMediator
    {
    public:
        virtual void Notify(std::shared_ptr<IComponent> component, std::string task) = 0;
    };

    class IComponent
    {
    protected:
        std::shared_ptr<IMediator> m_mediator;
    public:
        IComponent(std::shared_ptr<IMediator> mediator = nullptr) : m_mediator{ mediator } {}
        void setMediator(std::shared_ptr<IMediator> mediator)
        {
            m_mediator = mediator;
        }
    };

    class ConcreteComponent1 : public IComponent, std::enable_shared_from_this<ConcreteComponent1>
    {
    public:
        void DoFirst()
        {
            m_mediator->Notify(shared_from_this(), "A"); //Exception thrown here
        }
        void DoSecond()
        {
            m_mediator->Notify(shared_from_this(), "B");
        }

    };

    class ConcreteComponent2 : public IComponent, std::enable_shared_from_this<ConcreteComponent2>
    {
    public:
        void DoThird()
        {
            m_mediator->Notify(shared_from_this(), "C");
        }
        void DoFourth()
        {
            m_mediator->Notify(shared_from_this(), "D");
        }
    };

    class ConcreteMediator : public IMediator, std::enable_shared_from_this<ConcreteMediator>
    {
    private:
        std::shared_ptr<ConcreteComponent1> m_comp1;
        std::shared_ptr<ConcreteComponent2> m_comp2;
    public:
        ConcreteMediator(std::shared_ptr<ConcreteComponent1> comp1, std::shared_ptr<ConcreteComponent2> comp2) : m_comp1{ comp1 }, m_comp2{ comp2 } 
        {
            m_comp1->setMediator(shared_from_this());
            m_comp2->setMediator(shared_from_this());
        }
        virtual void Notify(std::shared_ptr<IComponent> component, std::string task)
        {
            if (task == "A")
            {
                std::cout << "Mediator reacts on A. \n";
                m_comp1->DoFirst();
            }
            if (task == "B")
            {
                std::cout << "Mediator reacts on B. \n";
                m_comp1->DoSecond();
            }
            if (task == "C")
            {
                std::cout << "Mediator reacts on C. \n";
                m_comp2->DoThird();
            }
            if (task == "D")
            {
                std::cout << "Mediator reacts on D. \n";
                m_comp2->DoFourth();
            }
        }
    };

    int main()
    {
        std::shared_ptr<ConcreteComponent1> comp1 = std::make_shared<ConcreteComponent1>();
        std::shared_ptr<ConcreteComponent2> comp2 = std::make_shared<ConcreteComponent2>();
        std::shared_ptr<ConcreteMediator> med = std::make_shared<ConcreteMediator>(comp1, comp2);
        comp1->DoFirst();
    }
Thorvas
  • 45
  • 10
  • I could be wrong, but does `public IComponent, std::enable_shared_from_this` imply that both interfaces are `public`? `std::enable_shared_from_this` must be publicly inherited. – rhughes Jul 13 '20 at 17:40
  • Also, please share the exact error messages. – rhughes Jul 13 '20 at 17:41
  • 2
    It is not possible to use `shared_from_this` while the constructor hasn't finished. – François Andrieux Jul 13 '20 at 17:41
  • What Francois said, see the notes here https://en.cppreference.com/w/cpp/memory/enable_shared_from_this/shared_from_this – john Jul 13 '20 at 17:42
  • @rhughes • good call, the second base class is private. – Eljay Jul 13 '20 at 19:23
  • Error messages are usually telling me something like "std::bad_weak_ptr in memory location (some hexadecimal numbers)". I've tried to inherit from enable_shared_from_this publicly, but I receive more exceptions. – Thorvas Jul 13 '20 at 19:37
  • @FrançoisAndrieux I've been practising design patterns from websites like sourcemaking or refactoring guru, these websites told me to organize code like this - in this example - for the purpose of proper Mediator pattern implementation – Thorvas Jul 13 '20 at 19:39
  • Otherwise, how should I convert "this" pointer into std::shared_ptr? – Thorvas Jul 13 '20 at 19:43
  • @Thorvas You can't during a constructor because there isn't a `shared_ptr` yet to get. – François Andrieux Jul 13 '20 at 19:49
  • @Thorvas If the components own the mediators, then the mediators don't need `shared_ptr` and you don't need `shared_from_this` anyway. – François Andrieux Jul 13 '20 at 19:51
  • @FrançoisAndrieux But then how am I supposed to execute components' methods from mediator? I make pointers to components to use their methods later – Thorvas Jul 13 '20 at 20:44
  • @Thorvas You should use a raw pointer for pointers that are not owning. – François Andrieux Jul 13 '20 at 21:34
  • @FrançoisAndrieux So if I don't assign any value to a pointer (for example with operator `new`) then I should use raw pointers? – Thorvas Jul 13 '20 at 22:30
  • An owning pointer is a pointer that is responsible for `delete`ing an object or that would be eventually assigned to an owning pointer. It is a pointer that is responsible for the lifetime of the object. Raw pointers should never be used for owning pointers, use smart pointers like `std::shared_ptr`. Here, it looks like `Mediator`s are owned by `Component`s , don't need to `delete` their owning `Component` and shouldn't outlive their `Component`. So you shouldn't need an `std::shared_ptr`. – François Andrieux Jul 14 '20 at 00:16
  • @Thorvas If you assign the result of `new` to a pointer, it is probably an owning pointer and should be a smart pointer. But it is possible to have an owning pointer without `new`, for example if you assign to it the value from another owning pointer or if it owns an object that was produced by a factory function. – François Andrieux Jul 14 '20 at 00:17

0 Answers0