Consider the following code where the Writer_I
acts as an interface. Other classes which fulfil the contract of writing element types in correct form can derive
Your design constraint is that Calculator
needs to not be a template, and has to be initialized with a writer.
That means its interface with the writer has to be dynamic. It can be through a virtual interface class, by storing function pointers, or by being passed pointers later, or similar.
As you don't want the polymorphic interface of writer to be fixed, that rules out a virtual interface.
Now, we can do this manually.
void header() {
for (int i = 0; i < 10; i++) {
write_i(i);
}
}
void footer() {
write_i(-100.0f);
}
those are the calls we need to type erase. We need to type erase down to their signatures, and remember how to do it later.
template
struct tag_t { using type=T; };
template
constexpr tag_t tag = {};
template
struct any_type_erase;
template
struct any_type_erase {
std::function operation;
any_type_erase() = default;
any_type_erase(any_type_erase const&) = default;
any_type_erase(any_type_erase &&) = default;
any_type_erase& operator=(any_type_erase const&) = default;
any_type_erase& operator=(any_type_erase &&) = default;
template
any_type_erase(tag_t, F&& f) {
operation = [f=std::forward(f)](Any& object, Args&&...args)->R {
return f(*std::any_cast(&object), std::forward(args)...);
};
}
R operator()(Any& any, Args...args)const {
return operation(any, std::forward(args)...);
}
};
any_type_erase
is a bit of a helper to do the boxing of the operation. For a const
operation, pass in std::any const
as the 2nd argument.
Add these members:
std::any writer;
any_type_erase print_header;
any_type_erase print_footer;
template
static auto invoke_writer() {
return [](auto& writer, auto&&..args) {
writer.write(decltype(args)(args)...);
};
}
template
Calculator(Writer_I& writer) :
writer(writer),
print_header( tag>, invoke_writer() ),
print_footer( tag>, invoke_writer() )
{}
void header() {
for (int i = 0; i < 10; i++) {
print_header( writer, i );
}
}
void footer() {
print_footer( writer, -100.0f );
}