我正在尝试制作一个功能路由器,以从std::map<uint64_t, std::function<void(T)>>
映射中调用正确的功能。问题是,它只能找到具有某种功能签名的某种功能。我希望它支持各种功能。
#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
这是正确的。
但是,如果我将void f3(std::string& i)
签名更改为void f3(const std::string& i)
,它将失败。据我了解,模板函数是使用const参数创建的,但是在某些时候它被剥离了,并且在函数映射中找不到正确的函数。
如果我将函数f3
的参数更改为const std::string&
,则会输出:
Signal router: no function implemented for parameter "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >"
因此删除了const。
如何通过模板设计支持各种参数(const ref,ref,value等)?
答案 0 :(得分:1)
当我们最初附加函数时,如果函数参数为const
,那么绑定可变版本也是安全的:
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);
}
}
然后,我们可以将f3
编写为const函数:
void f3(std::string const& i) {
std::cout << "Hello " << i << '\n';
}
现在,main
不管std::string
是否为常量都可以工作。
我们也可以使用模式匹配来重写它:
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);
}
答案 1 :(得分:0)
我将假设您不支持volatile。
以下是函数指针签名中的5种类型:
render()
在您的设计中,您不能传递纯int
int const&
int &
int const&&
int &&
。因此,我们只需要担心它作为函数指针参数。
int
可以被上述任何一种调用。
int
可以被上述任何一种调用。
int const&
可以由int const&&
调用。
int&&
和int&
不能被其他任何人调用。
现在,如果我们的类型是可移动的但不可复制,则规则会更改。
int&&
只能由T
调用。
T&&
仍然可以被任何人呼叫。
如果我们的类型是不可移动的,那么没有T const&
系统就无法通过代理包装调用T
。
在通话时,我们需要将其反转。如果用emplace
T&
和T const&
调用。如果可以复制T&
,请同时检查T
。
如果使用T
进行调用,则仅检查T const&
和T const&
,前提是可以复制T
。
如果用T
调用,我们需要检查T&&
,如果可以移动T&&
,请检查T const&&
和T const&
。
如果用T
调用,我们只会检查T
和T const&&
是否可以复制T const&&
。
所以这给了我们一个进攻计划。
T
发射是:
T
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);
}
}
支持需要另外一遍。
这使用了一些C ++ 17; template<class T>
void emit(hash_t hash, T&& param) {
try {
signal_router<T&&>[hash](param);
}
周围有很多方法。我会使用标签分配。