I have a toy example that I'd like to modify architecturally to remove type dependency of Processor
on EmitterT
:
#include <iostream>
#include <utility>
using namespace std;
struct Emitter {
void e(int) { cout << "emitting int\n";}
void e(double) { cout << "emitting double\n";}
void e(char*) { cout << "emitting char*\n";}
void e(const char*) { cout << "emitting const char*\n";}
};
template <typename EmitterT>
struct Processor {
Processor(EmitterT e) : e_{e} {}
template <typename T>
void process(T&& value) {
cout << "some processing... ";
e_(std::forward<T>(value));
}
EmitterT e_;
};
template<typename Emitter_>
Processor<Emitter_> makeProcessor(Emitter_ e) { return Processor<Emitter_>(e);}
int main() {
Emitter em;
auto p = makeProcessor([&em](auto v){em.e(v);});
p.process(1);
p.process("lol");
return 0;
}
I'd like to decouple part responsible for utilizing results of processing from the processing itself. The Emitter
class structure is given to me, so I have to support overloaded functions.
I'd like to pass a lambda function to a processor that will use it. Kind of like a callback mechanism, however it must be a generic lambda, to support overloads.
The example I wrote works, but it depends on Emitter
type as a template parameter. I don't like Processor
type to change based on Emitter
. It's also contagious, I have a real Processor
hierarchy and Emitter
spread like const
or worse.
After reading https://stackoverflow.com/a/17233649/1133179 I've tried playing with below struct as a member:
struct EmitterC {
template<typename T>
void operator()(T value) { }
};
But I cannot figure out a way to defer implementation of Emitter
after Processor
when using it as a normal parameter. It worked out with a forward declaration and a reference EmitterC&
but it supports one only Emitter definition. The only way I could come up with was to drop lambda, and make virtual overloads in EmitterC
for every type I expect in Emitter
and use it as a base class.
So, Is there a way to pass the (generic) lambda as a parameter, so that Processor
type doesn't depend on Emitter
?
I am restricted to c++14, but I am interested in more modern standards as well if the have better support.
If you are willing to pay a high runtime cost in exchange for minimal constraints, you can use std::function
with std::any
(for C++14, use boost::any
):
#include <iostream>
#include <utility>
#include <any>
#include <functional>
struct Processor {
Processor(std::function<void(std::any)> e) : e_{e} {}
template <typename T>
void process(T&& value) {
std::cout << "some processing... ";
e_(std::forward<T>(value));
}
std::function<void(std::any)> e_;
};
struct Emitter {
void e(int) { std::cout << "emitting int\n";}
void e(double) { std::cout << "emitting double\n";}
void e(char*) { std::cout << "emitting char*\n";}
void e(const char*) { std::cout << "emitting const char*\n";}
};
int main() {
Emitter em;
auto p = Processor(
[&em](std::any any){
// This if-else chain isn't that cheap, but it's about the best
// we can do. Alternatives include:
// - Hashmap from `std::type_index` (possibly using a perfect hash)
// to a function pointer that implements this.
// - Custom `any` implementation which allows "visitation":
//
// any.visit<int, double, char*, char const*>([&em] (auto it) {
// em.e(it);
// });
if (auto* i = std::any_cast<int>(&any)) {
em.e(*i);
} else if (auto* d = std::any_cast<double>(&any)) {
em.e(*d);
} else if (auto* cstr = std::any_cast<char*>(&any)) {
em.e(*cstr);
} else {
em.e(std::any_cast<char const*>(any));
}
}
);
p.process(1);
p.process("lol");
return 0;
}
std::any
and std::function
are both owning type erased wrappers. You may have heap allocations for this, or you might fit inside their small object optimization. You will have virtual function calls (or equivalent).
Compiler Explorer link
IMHO: Inheritance is here for that.
#include <iostream>
#include <utility>
using namespace std;
struct BaseEmitter {
virtual void e(int) =0;
virtual void e(double)=0;
virtual void e(char*)=0;
virtual void e(const char*)=0;
};
struct Emitter :public BaseEmitter {
virtual void e(int) { cout << "emitting int\n";}
virtual void e(double) { cout << "emitting double\n";}
virtual void e(char*) { cout << "emitting char*\n";}
virtual void e(const char*) { cout << "emitting const char*\n";}
};
struct Processor {
BaseEmitter& e_;
Processor(BaseEmitter& e) : e_(e) {}
template <typename T>
void process(T&& value) {
cout << "some processing... ";
e_(std::forward<T>(value));
}
};
int main() {
Emitter em;
auto p = Processor(em);
p.process(1);
p.process("lol");
return 0;
}
You can do a mix in order to capture the lambda, just by inheritance in the interface:
struct bypass
{
virtual void operator()() = 0;
};
template<typename callable> struct capture: public bypass
{
callable& _ref;
capture(callable &ref)
: _ref(ref)
{;};
virtual void operator()()
{
_ref();
}
};
struct test
{
bypass *_c;
template<class T> test(T& callback)
: _c(nullptr)
{
_c = new capture<decltype(callback)>(callback);
};
virtual ~test()
{
delete _c;
};
void doit()
{
(*_c)();
}
};
int main(int argc, char* argv[])
{
auto lambda = [](){std::cout << "hello\n";};
test z=test(lambda);
z.doit();
return 0;
}
Is it possible to pass generic lambda as non-template argument
It is not possible to declare a non-template function that accepts a lambda as an argument. The type of a lambda is anonymous: It has no name. It is not possible to write a function declaration that accepts an argument of an anonymous type.
The type of the lambda can be deduced, which is why lambdas can be passed into function templates whose argument types are deduced.
While this answers the question, it does not offer a solution. I don't think a solution is going to be simple.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With