I'm trying to make a function router that calls correct function from std::map<uint64_t, std::function<void(T)>>
map. The problem is, it can only find certain kinds of functions with certain kind of function signatures. I want it to support all kinds of functions.
#ifndef ENGINE_H
#define ENGINE_H
#include <iostream>
#include <map>
class Engine
{
public:
typedef std::uint64_t hash_t;
/* Register function to signal router. */
template<class T>
void attach(hash_t hash, void(*f)(T)) {
/* Cast function ptr to std::function. */
auto func = static_cast<std::function<void (T)>>(f);
signal_router<T>[hash] = func;
}
/* Call registerd function from signal router. */
template<class T>
void emit(hash_t hash, T&& param) {
try {
signal_router<T>[hash](param);
} catch (std::bad_function_call&) {
int status = -4;
std::cerr << "Signal router: no function implemented for parameter \""
<< abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status) << "\" " << '\n';
}
}
private:
template<typename T>
static std::map<hash_t, std::function<void (T)>> signal_router;
};
/* We must declare static instance outside of its class, altough it's private. */
template<typename T>
typename::std::map<uint64_t, std::function<void (T)>> Engine::signal_router;
#endif /* ENGINE_H */
#include <iostream>
#include <string>
#include <functional>
#include "engine.hpp"
void f1(int i) {
std::cout << "Hello " << i << '\n';
}
void f2(long i) {
std::cout << "Hello " << i << '\n';
}
void f3(std::string& i) {
std::cout << "Hello " << i << '\n';
}
int main()
{
Engine eng;
eng.attach(0, f1);
eng.emit(0, 1);
eng.attach(1, f2);
eng.emit(1, 10l);
eng.attach(2, f3);
std::string s = " world";
eng.emit(2, s);
return 0;
}
Hello 1
Hello 10
Hello world
Which is correct.
But if I change void f3(std::string& i)
signature to void f3(const std::string& i)
it fails. As I understand, the template function is created with const parameter but it stripped out at some point and doesn't find correct the function from function map.
If I change the function f3
parameter to const std::string&
it outputs:
Signal router: no function implemented for parameter "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >"
So the const is stripped out.
How can I support all kinds of parameters (const ref, ref, values, etc...) through my template design?
When we originally attach the function, if the function parameter is const
, then it's safe to also bind a mutable version:
template<class T>
void attach(hash_t hash, void(*f)(T)) {
/* Bind to signal rounter */
signal_router<T>[hash] = std::function<void(T)>(f);
/* Bind mutable version to signal router */
using PlainT = std::remove_reference_t<T>;
if(std::is_reference<T>::value && std::is_const<PlainT>::value) {
// Bind mutable version
using MutableT = std::remove_const_t<PlainT>&;
signal_router<MutableT>[hash] = std::function<void(MutableT)>(f);
}
}
Then, we can write f3
as a const function:
void f3(std::string const& i) {
std::cout << "Hello " << i << '\n';
}
And now, main
works whether or not std::string
is const.
We can re-write this using pattern matching too:
template<class T>
void attach(hash_t hash, void(*f)(T)) {
// if it's pass by value, add multiple binds for references
signal_router<T>[hash] = std::function<void(T)>(f);
signal_router<T&>[hash] = std::function<void(T&)>(f);
signal_router<T const&>[hash] = std::function<void(T const&)>(f);
signal_router<T&&>[hash] = std::function<void(T&&)>(f);
}
template<class T>
void attach(hash_t hash, void(*f)(T&)) {
signal_router<T&>[hash] = std::function<void(T&)>(f);
}
template<class T>
void attach(hash_t hash, void(*f)(const T&)) {
signal_router<T const&>[hash] = std::function<void(T const&)>(f);
signal_router<T&>[hash] = std::function<void(T&)>(f);
}
template<class T>
void attach(hash_t hash, void(*f)(T&&)) {
signal_router<T&&>[hash] = std::function<void(T&&)>(f);
}
I'm going to assume you don't support volatile.
Here are the 5 types that can be in the signature of your function pointers:
int
int const&
int &
int const&&
int &&
In your design, you cannot pass a pure int
in. So we only have to worry about it as a function pointer argument.
int
can be called by any of the above.
int const&
can be called by any of the above.
int const&&
can be called by int&&
.
int&
and int&&
cannot be called by anything else.
Now, if our type is movable but not-copyable, the rules change.
T
can only be called by T&&
.
T const&
can still be called by anyone.
If our type is non-movable, then T
cannot be called via a proxy wrapper without an emplace
system.
At the point of call, we need to invert this. If called with T&
T const&
and T&
. If T
can be copied, also check T
.
If called with T const&
we only check T const&
and T
iff T
can be copied.
If called with T&&
we need to check T&&
and T const&&
and T const&
and T
iff T
can be moved.
If called with T const&&
we only check T const&&
and T
if T
can be copied.
So this gives us a plan of attack.
template<class T>
void populate(has_t hash, std::function<void(T)> f) {
signal_router<T>[hash] = std::move(f);
}
template<class T>
void attach(hash_t hash, void(*f)(T&)) {
populate<T&>(hash, f);
}
template<class T>
void attach(hash_t hash, void(*f)(const T&)) {
populate<T const&>(hash, f);
populate<T&>(hash, f);
populate<T&&>(hash, f);
populate<T const&&>(hash, f);
}
template<class T>
void attach(hash_t hash, void(*f)(T&&)) {
populate<T&&>(hash, f);
}
template<class T>
void attach(hash_t hash, void(*f)(T const&&)) {
populate<T&&>(hash, f);
populate<T const&&>(hash, f);
}
template<class T>
void attach(hash_t hash, void(*f)(T)) {
if constexpr( std::is_copy_constructible<T>{} ) {
populate<T const&>(hash, f);
populate<T&>(hash, f);
populate<T const&&>(hash, f);
}
if constexpr( std::is_move_constructible<T>{} ) {
populate<T&&>(hash, f);
}
}
and emit is:
template<class T>
void emit(hash_t hash, T&& param) {
try {
signal_router<T&&>[hash](param);
}
volatile
support would require another pass.
This used some C++17; there are ways around the if constexpr
. I'd use tag dispatching.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.