如何将lambda,function object或std :: function作为模板参数传递并控制它们的参数

时间:2013-05-10 14:36:03

标签: c++ templates c++11

我想将一些协议实现为一个类模板,其中raw_write和raw_read函数作为模板参数。两个原始函数都有严格定义的接口:

int raw_write(uint8_t *src, size_t len);
int raw_read(uint8_t *dst, size_t maxlen);

当有人尝试时,是否可以通过编译错误来控制此接口 传递例如:

int raw_write2(uint16_t *src, size_t len);

我应该将模板参数作为指定类型的对象传递,还是作为在模板实现中实例化的类型名称?

2 个答案:

答案 0 :(得分:7)

认为这是使用std::function存储可调用对象的所需(或至少 )解决方案(不需要使用已定义的template } class):

#include <iostream>
#include <functional>

struct protocol_callbacks
{
    using func_t = std::function<int(uint8_t*, size_t)>;

    protocol_callbacks(func_t a_reader, func_t a_writer) :
        reader(a_reader),
        writer(a_writer) {}
    func_t reader;
    func_t writer;
};

int writer(uint8_t*, size_t) { return 0; }
int reader(uint8_t*, size_t) { return 0; }

int bad_writer(uint16_t*, size_t) { return 0; }

int main ()
{
    protocol_callbacks pc1(reader, writer);
    protocol_callbacks pc2([](uint8_t*, size_t) { return 0; },
                           [](uint8_t*, size_t) { return 0; });
    //protocol_callbacks pc3(bad_writer, reader);
}

使用bad_writer导致编译失败(没有bad_writer http://ideone.com/hG7tqcbad_writer http://ideone.com/roMJgM)。

答案 1 :(得分:1)

您可以使用SFINAE和一些特质类来完成此任务。

我怀疑最好的方法是期望一个与呼叫兼容的仿函数 - 这也更容易。

#include <utility>
#include <type_traits>
template<typename T, bool=true>
struct raw_write_compatible: std::false_type {};
template<typename T, bool=true>
struct raw_read_compatible: std::false_type {};
template<typename T>
struct raw_write_compatible<
  T,
  std::is_convertable<
    decltype(
      std::declval<T&>()(
        std::declval<uint8_t *>(),
        std::declval<size_t>()
      )
    ),
    int
  >::value
>: std::true_type {};
template<typename T>
struct raw_read_compatible<
  T,
  std::is_convertable<
    decltype(
      std::declval<T&>()(
        std::declval<uint8_t *>(),
        std::declval<size_t>()
      )
    ),
    int
  >::value
>: std::true_type {};

这一点的意思是raw_read_compatible< T >::valuetrue iff T的实例可以使用签名(uint8_t*, size_t)进行评估,并且返回类型可以转换为int const uint8_t

(顺便说一下,你的“写”函数签名可能应该指向template<typename Reader, typename Writer> typename std::enable_if< raw_read_compatible<Reader>::value && raw_write_compatible<Writer>::value, bool // return value of do_some_io_stuff >::type do_some_io_stuff( Reader const& reader, Writer const& writer ) { return true; } ,因为它不会修改那个参数。)

您可以这样使用:

do_some_io_stuff

do_some_io_stuff将匹配iff读/写器可以按照您想要的方式调用。

当您尝试传入不兼容的lambda或函数指针或函数时,std::function< int(uint8_t*, size_t) >无法匹配,而不是匹配然后无法编译。从理论上讲,这可以让你覆盖一切。

上述解决方案需要一个具有良好C ++ 11支持的编译器:例如,MSVC2012不能与上述一起工作(它的单词中缺少“表达式SFINAE”)。

更简单的解决方案是只需要virtual,但这有两个成本:首先,它在每次调用时都有一个运行时成本(大致相当于std::function方法调用 - 所以与io相比并不那么高 - 真正的成本是阻止功能调用边界的优化。其次你可能会遇到一些编译失败,而不是在我的经验中没有匹配签名错误(我不确定C ++ 11标准是否已经指定std::function的“lambda”构造函数应该只匹配时传递了一个兼容的类型,但我想我已经看到了那个测试失败的实现。)

{{1}}解决方案的优点是它更简单,它允许将实现放在一个单独的文件中,并且意图更容易理解。