Здравствуйте, 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);
}