我想将一些协议实现为一个类模板,其中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);
我应该将模板参数作为指定类型的对象传递,还是作为在模板实现中实例化的类型名称?
答案 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/hG7tqc和bad_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 >::value
是true
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}}解决方案的优点是它更简单,它允许将实现放在一个单独的文件中,并且意图更容易理解。