0

我想用 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);

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

4

2 回答 2

7

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

#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)。

于 2013-05-10T14:54:55.970 回答
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 >::value如果true的实例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如果可以以您想要的方式调用读取器/写入器,它将匹配。

这样做的好处是当您尝试传入不兼容的 lambda 或函数指针或仿函数时do_some_io_stuff无法匹配,而不是匹配然后无法编译。从理论上讲,这可以让您覆盖事物。

上述解决方案需要具有良好 C++11 支持的编译器:例如,MSVC2012 不适用于上述解决方案(它缺少“表达式 SFINAE”)。

一个更简单的解决方案是只采用 a std::function< int(uint8_t*, size_t) >,但这有两个成本:首先,它在每次调用时都有运行时间成本(大致相当于一个virtual方法调用——因此与 io 相比并不那么高)——真正的成本在函数调用边界上进行阻塞优化。其次,根据我的经验,您可能会遇到一些编译失败而不是无法匹配签名错误(我不确定 C++11 标准是否已指定std::function' 的“lambda”构造函数仅在传递兼容类型时才应匹配,但我想我已经看到了未通过该测试的实现)。

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

于 2013-05-10T14:57:01.207 回答