Информация об изменениях

Сообщение Re[3]: Не очень понимаю, как на модных плюсиках сделать таку от 21.09.2023 21:37

Изменено 21.09.2023 21:38 rg45

Re[3]: Не очень понимаю, как на модных плюсиках сделать такую ш
Здравствуйте, Marty, Вы писали:

R>>Единственная небольшая шероховатость — первый обработчик всегда вызывается последним (потому, что именно его результат возвращается). Но эта мелочь легко устраняется добавлением еще одного метода делегирования (аналогичного doDelegate):


Я выше упоминал о некрасивости, что первый обработчик вызыается последним. На самом деле у этой проблемы есть более элегантное решение, чем добаление еще одной перегрузки метода doDelegate. Исправить последовательность вызовов можно, использовав идиому RAII (см. класс AtExit и его использование в doDelegate). Следующий пример уже работает в точном соответствии с требованиями, при этом все делегирование выполняется одним общим методом (doDelegate):

http://coliru.stacked-crooked.com/a/d66ee722f822d4fc

#include <functional>
#include <iostream>
#include <memory>
#include <vector>

struct IDelegationTest
{

   virtual bool getter() = 0;
   virtual bool setter(bool b) = 0;
   virtual void jobImpl(bool b, int i) = 0;
   virtual void jobImpl(bool b, int i, const std::string& s) = 0;
   virtual int jobImpl2(bool b, int i) = 0;

};


struct DelegationTestImpl : public IDelegationTest
{
   int n = 0;

   DelegationTestImpl() = default;
   explicit DelegationTestImpl(int n) : n(n) {}

   virtual bool getter() override
   {
      std::cout << "DelegationTestImpl::getter(), n: " << n << "\n";
      return true;
   }

   virtual bool setter(bool b) override
   {
      std::cout << "DelegationTestImpl::setter(bool b), b: " << b << ", n: " << n << "\n";
      return true;
   }

   virtual void jobImpl(bool b, int i) override
   {
      std::cout << "DelegationTestImpl::jobImpl(bool b, int i), b: " << b << ", i: " << i << ", n: " << n << "\n";
   }

   virtual void jobImpl(bool b, int i, const std::string& s) override
   {
      std::cout << "DelegationTestImpl::jobImpl(bool b, int i), b: " << b << ", i: " << i << ", s: " << s << ", n: " << n << "\n";
   }

   virtual int jobImpl2(bool b, int i) override
   {
      std::cout << "DelegationTestImpl::jobImpl2(bool b, int i), b: " << b << ", i: " << i << ", n: " << n << "\n";
      return n;
   }

};

struct AtExit
{
    std::function<void()> guard;
    ~AtExit() { guard(); }
};

class Delegator : public IDelegationTest
{
   template <typename F>
   decltype(auto) doDelegate(F&& f)
   {
      const AtExit callTail {[&]
      {
         for (auto&& impl : m_tail)
         {
            f(*impl);
         }
      }};     
      return f(*m_head);
   }

public:

   template <typename T, typename...X>
   explicit Delegator(T&& head, X&&...tail)
      : m_head(std::forward<T>(head)), m_tail{ std::forward<X>(tail)...} {}

   bool getter() override { return doDelegate([&](IDelegationTest& t){ return t.getter(); }); }
   bool setter(bool b) override { return doDelegate([&](IDelegationTest& t) { return t.setter(b); }); }
   void jobImpl(bool b, int i) override { doDelegate([&](IDelegationTest& t) { t.jobImpl(b, i); }); }
   void jobImpl(bool b, int i, const std::string& s) override { doDelegate([&](IDelegationTest& t) { t.jobImpl(b, i, s); }); }
   int jobImpl2(bool b, int i) override { return doDelegate([&](IDelegationTest& t) { return t.jobImpl2(b, i); }); }

private:

   using ImplPtr = std::shared_ptr<IDelegationTest>;

   const ImplPtr m_head;
   const std::vector<ImplPtr> m_tail;
};

int main()
{
   Delegator delegator {
      std::make_shared<DelegationTestImpl>(1),
      std::make_shared<DelegationTestImpl>(2),
      std::make_shared<DelegationTestImpl>(3),
   };

   delegator.getter();
   delegator.setter(true);
   delegator.jobImpl(false, 3);
   delegator.jobImpl2(true, 5);
}
Re[3]: Не очень понимаю, как на модных плюсиках сделать таку
Здравствуйте, Marty, Вы писали:

R>>Единственная небольшая шероховатость — первый обработчик всегда вызывается последним (потому, что именно его результат возвращается). Но эта мелочь легко устраняется добавлением еще одного метода делегирования (аналогичного doDelegate):


Я выше упоминал о некрасивости, что первый обработчик вызыается последним. На самом деле у этой проблемы есть более элегантное решение, чем добаление еще одной перегрузки метода doDelegate. Исправить последовательность вызовов можно, использовав идиому RAII (см. класс AtExit и его использование в doDelegate). Следующий пример уже работает в точном соответствии с требованиями и в правильной последовательности, при этом все делегирование выполняется одним общим методом (doDelegate):

http://coliru.stacked-crooked.com/a/d66ee722f822d4fc

#include <functional>
#include <iostream>
#include <memory>
#include <vector>

struct IDelegationTest
{

   virtual bool getter() = 0;
   virtual bool setter(bool b) = 0;
   virtual void jobImpl(bool b, int i) = 0;
   virtual void jobImpl(bool b, int i, const std::string& s) = 0;
   virtual int jobImpl2(bool b, int i) = 0;

};


struct DelegationTestImpl : public IDelegationTest
{
   int n = 0;

   DelegationTestImpl() = default;
   explicit DelegationTestImpl(int n) : n(n) {}

   virtual bool getter() override
   {
      std::cout << "DelegationTestImpl::getter(), n: " << n << "\n";
      return true;
   }

   virtual bool setter(bool b) override
   {
      std::cout << "DelegationTestImpl::setter(bool b), b: " << b << ", n: " << n << "\n";
      return true;
   }

   virtual void jobImpl(bool b, int i) override
   {
      std::cout << "DelegationTestImpl::jobImpl(bool b, int i), b: " << b << ", i: " << i << ", n: " << n << "\n";
   }

   virtual void jobImpl(bool b, int i, const std::string& s) override
   {
      std::cout << "DelegationTestImpl::jobImpl(bool b, int i), b: " << b << ", i: " << i << ", s: " << s << ", n: " << n << "\n";
   }

   virtual int jobImpl2(bool b, int i) override
   {
      std::cout << "DelegationTestImpl::jobImpl2(bool b, int i), b: " << b << ", i: " << i << ", n: " << n << "\n";
      return n;
   }

};

struct AtExit
{
    std::function<void()> guard;
    ~AtExit() { guard(); }
};

class Delegator : public IDelegationTest
{
   template <typename F>
   decltype(auto) doDelegate(F&& f)
   {
      const AtExit callTail {[&]
      {
         for (auto&& impl : m_tail)
         {
            f(*impl);
         }
      }};     
      return f(*m_head);
   }

public:

   template <typename T, typename...X>
   explicit Delegator(T&& head, X&&...tail)
      : m_head(std::forward<T>(head)), m_tail{ std::forward<X>(tail)...} {}

   bool getter() override { return doDelegate([&](IDelegationTest& t){ return t.getter(); }); }
   bool setter(bool b) override { return doDelegate([&](IDelegationTest& t) { return t.setter(b); }); }
   void jobImpl(bool b, int i) override { doDelegate([&](IDelegationTest& t) { t.jobImpl(b, i); }); }
   void jobImpl(bool b, int i, const std::string& s) override { doDelegate([&](IDelegationTest& t) { t.jobImpl(b, i, s); }); }
   int jobImpl2(bool b, int i) override { return doDelegate([&](IDelegationTest& t) { return t.jobImpl2(b, i); }); }

private:

   using ImplPtr = std::shared_ptr<IDelegationTest>;

   const ImplPtr m_head;
   const std::vector<ImplPtr> m_tail;
};

int main()
{
   Delegator delegator {
      std::make_shared<DelegationTestImpl>(1),
      std::make_shared<DelegationTestImpl>(2),
      std::make_shared<DelegationTestImpl>(3),
   };

   delegator.getter();
   delegator.setter(true);
   delegator.jobImpl(false, 3);
   delegator.jobImpl2(true, 5);
}