c++ - 有没有办法在我的通用功能路由器设计中支持 'const reference' 作为功能签名参数?

标签 c++ c++11 templates

我正在尝试制作一个从 std::map<uint64_t, std::function<void(T)>> 调用正确函数的函数路由器 map 。问题是,它只能找到具有特定类型函数签名的特定类型的函数。我希望它支持各种功能。

库本身:

#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、values 等)?

最佳答案

当我们最初附加函数时,如果函数参数是const,那么也绑定(bind)一个可变版本是安全的:

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写成一个常量函数:

void f3(std::string const& i) {
  std::cout << "Hello " << i << '\n';
}

现在,无论 std::string 是否为 const,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); 
}

关于c++ - 有没有办法在我的通用功能路由器设计中支持 'const reference' 作为功能签名参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55580060/

相关文章:

c++ - Qt 缺少库吗?

c++ - 预分配内存列表

c++ - clang 3.1 看不到 unique_ptr?

c++ - 我可以指定模板参数应该是某个基类的子类吗?

c++ - Mupdf加密解密问题

c++ - 如何在 C++20 范围内将投影与变换结合起来

c++ - 稍后在构造函数中重新启动异常指令 delete this

c++11 - C++ STL;迭代包含STL容器的类?

c++ - 如何在 C++ 中使用模板函数

c++ - 在 C++ 计算器中使用模板