在C++primer p618中介绍了递归实现的版本,主要的形式:
template<typname ARGS> void print(const T&t,const ARGS&... rest) { print(t); print(t,rest...); } template<typename T> void print(const T& t) { }
这样的写法又长又臭,有没有更简便的方法呢?
比如我想用openssh的sha进行哈希,其主要过程是
SHA256CTx ctx; CTXINIT(&ctx); CTXUPDATE(&ctx,(const void*)data,bytesize); ... CTXFINLIZE(&result,&ctx);
假如我有多个类型要一起进行哈希的数据,比如int,long,vector,string。如何优雅的写出模板函数呢:(假定已看过effective modern c++里的标签分派 p176)
auto result=computeHash(int,long,vector,string...);
可以利用逗号表达式与initializer_list。
C/C++语言中的逗号表达式:a=(b+c,d),其结果是a=d。依次求值,并取最后一个表达式的结果为最终值。
initializer_list:要求内容在编译期就已知,选这个我感觉是有点无奈,C++应该有更优雅的方式来实现非递归模式的。
下面上代码:
#include <bits/stdc++.h> #include <openssl/sha.h> using namespace std; using HashType = array<uint8_t, SHA256_DIGEST_LENGTH>; template <typename... ARGS> HashType computeHash(const ARGS &...args) { SHA256_CTX ctx; SHA256_Init(&ctx); std::initializer_list<int>{(internalCompute(&ctx, args, std::is_arithmetic<std::decay_t<ARGS>>()), 0)...}; HashType result; SHA256_Final(result.data(), &ctx); return result; } template <typename T> void internalCompute(SHA256_CTX *ctx, const T &data, std::true_type) { const void *underlying = static_cast<const void *>(&data); SHA256_Update(ctx, underlying, sizeof(T)); } template <typename T> void internalCompute(SHA256_CTX *ctx, const T &container, std::false_type) { using DataType = typename T::value_type; const void *underlying = static_cast<const void *>(container.data()); size_t size = container.size() * sizeof(DataType); SHA256_Update(ctx, underlying, size); } int main(int argc, char **argv) { std::string test1 = "123456"; uint8_t a = 1; uint64_t b = 2; auto hash = computeHash(test1, a, b); }
可以注意到
std::initializer_list<int>{(internalCompute(&ctx, args, std::is_arithmetic<std::decay_t<ARGS>>()), 0)...};
这个句子才是最为关键的,对每个模板参数依次compute,并依照是否是容器还是数值进行标签分派。因为扩展" ... "在括号外,所以也可以看成:
compute(&ctx,arg1,ARG1); compute(&ctx,arg2,ARG2); ...
完美!!!