有没有办法在我的通用功能路由器设计中支持"常量引用"作为功能签名参数?
Is there a way to support 'const reference' as a function signature parameter in my generic function router design?
我正在尝试制作一个从映射中调用正确函数的功能路由器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
,那么绑定一个可变版本也是安全的:
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';
}
而现在,无论std::string
是否恒定,main
都有效。
我们也可以使用模式匹配来重写它:
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);
}
我假设你不支持易失性。
以下是可以在函数指针签名中的 5 种类型:
int
int const&
int &
int const&&
int &&
在你的设计中,你不能传递一个纯粹的int
。 因此,我们只需要将其视为函数指针参数即可。
int
可以通过上述任何一种调用。
int const&
可以通过上述任何一种调用。
int const&&
可以由int&&
调用。
int&
和int&&
不能被其他任何东西调用。
现在,如果我们的类型是可移动但不可复制的,则规则会更改。
T
只能由T&&
调用。
T const&
仍然可以被任何人调用。
如果我们的类型是不可移动的,那么在没有emplace
系统的情况下,就无法通过代理包装器调用T
。
在调用时,我们需要反转这一点。 如果用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);
}
}
并且发出是:
template<class T>
void emit(hash_t hash, T&& param) {
try {
signal_router<T&&>[hash](param);
}
volatile
支持将需要另一次传递。
这使用了大约 C++17; 有办法绕过if constexpr
。 我会使用标签调度。
- Qt中来自不同功能的按钮引用
- 引用 using 声明引入的功能的句子是什么意思?
- 警告#13212:引用需要堆栈对齐功能的EBX
- 我的模板功能具有通用引用不起作用
- C 使用多个lambdas/绑定以引用相同的功能
- FFMPEG关于多个功能的未定义引用
- 有没有办法在我的通用功能路由器设计中支持"常量引用"作为功能签名参数?
- 对我的功能的未定义引用
- 为什么功能程序的规律性允许按值传递和按常量引用传递?
- 未定义的引用指针的变量模板在clang中功能,而不是GCC
- IFDEF块内部对功能的未定义引用
- 对完整模板专业类成员功能的未定义引用,但不是部分专业化
- 对函数的未定义引用,该功能是另一个类的成员
- 呼叫功能的值,该功能通过引用C 中的指针
- 可以接受未知签名的可变功能引用的类
- 必须调用对非静态功能的引用
- C 通过引用DLL中的功能传递std ::字符串
- 为什么BOOST.RANGE RANGE_BEGIN/END FREE功能对const和非const引用都重载
- 模板功能:混合逐个复制传递和按引用传递
- 使用mingw对魔鬼功能的未定义引用