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

Сообщение Re[4]: Не очень понимаю, как на модных плюсиках сделать таку от 22.09.2023 11:07

Изменено 22.09.2023 11:17 rg45

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

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


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


А теперь, легким движением руки, переходим от динамического полиморфизма к статическому (угу, с концептами):

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

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

template <typename T>
concept IDelegationTest = requires(T t, bool b, int i, const std::string& s)
{

   { t.getter() } -> std::same_as<bool>;
   { t.setter(b) } -> std::same_as<bool>;
   { t.jobImpl(b, i) } -> std::same_as<void>;
   { t.jobImpl(b, i, s) } -> std::same_as<void>;
   { t.jobImpl2(b, i) } -> std::same_as<int>;

};

struct DelegationTestImpl
{
   int n = 0;

   DelegationTestImpl() = default;
   explicit DelegationTestImpl(int n) : n(n)
   {
      static_assert(IDelegationTest<DelegationTestImpl>);
   }

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

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

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

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

   int jobImpl2(bool b, int i)
   {
      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(); }
};

template <IDelegationTest Head, IDelegationTest...Tail>
class Delegator
{
public:

   explicit Delegator(Head&& head, Tail&&...tail)
      : m_head(std::forward<Head>(head))
      , m_tail{ std::forward<Tail>(tail)...}
      {
         static_assert(IDelegationTest<Delegator>);
      }

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

private:

   template <typename F>
   decltype(auto) doDelegate(F&& f)
   {
      const AtExit callTail {[&]
      {
         std::apply([&](IDelegationTest auto&&...t){ (f(t), ...); }, m_tail);
      }};
      return f(m_head);
   }

   Head m_head;
   std::tuple<Tail...> m_tail;
};

int main()
{
   Delegator delegator {
      DelegationTestImpl(1),
      DelegationTestImpl(2),
      DelegationTestImpl(3),
   };

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

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


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


А теперь, легким движением руки, переходим от динамического полиморфизма к статическому (угу, с концептами):

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

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

template <typename T>
concept IDelegationTest = requires(T t, bool b, int i, const std::string& s)
{

   { t.getter() } -> std::same_as<bool>;
   { t.setter(b) } -> std::same_as<bool>;
   { t.jobImpl(b, i) } -> std::same_as<void>;
   { t.jobImpl(b, i, s) } -> std::same_as<void>;
   { t.jobImpl2(b, i) } -> std::same_as<int>;

};

struct DelegationTestImpl
{
   int n = 0;

   DelegationTestImpl() = default;
   explicit DelegationTestImpl(int n) : n(n)
   {
      static_assert(IDelegationTest<DelegationTestImpl>);
   }

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

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

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

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

   int jobImpl2(bool b, int i)
   {
      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(); }
};

template <IDelegationTest Head, IDelegationTest...Tail>
class Delegator
{
public:

   explicit Delegator(Head&& head, Tail&&...tail)
      : m_head(std::forward<Head>(head))
      , m_tail{ std::forward<Tail>(tail)...}
      {
         static_assert(IDelegationTest<Delegator>);
      }

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

private:

   template <typename F>
   decltype(auto) doDelegate(F&& f)
   {
      const AtExit callTail {[&]
      {
         std::apply([&](IDelegationTest auto&&...t){ (f(t), ...); }, m_tail);
      }};
      return f(m_head);
   }

   Head m_head;
   std::tuple<Tail...> m_tail;
};

int main()
{
   Delegator delegator {
      DelegationTestImpl(1),
      DelegationTestImpl(2),
      Delegator {
         DelegationTestImpl(3),
         DelegationTestImpl(4)
      }
   };

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