背景
在微服务的开发过程中,分布式缓存数据库redis几乎是必然要使用到的,然而redis的储存数据全是string类型的数据,在读取数据和保存数据时,c++没法直接将操作对象进行存储和保存,必须进行转换。我们希望提供一个公共load与save接口完成对象数据到redis数据之间的无缝转换。本文是作者在工程实践的一些经验总结,主要实现目标为提供对象到redis之间的操作接口,使上层应用的调用变得稍微简单一点,数据存储到redis时采用hash表进行存储。
需求梳理
假设我们的数据类为Data,定义如下:
class Data
{
public:
Data(int inkey):key(inkey){
}
public:
void load();
void save();
public:
std::string key;
int field1;
int field2;
};
Data的对象通过load接口将对应的key从redis中读到我们的类成员中,通过save接口将自己的成员变量保存到redis中,有了目标之后我们接下来再进行分解,看一下load与save具体应该怎么实现。先看一下伪代码
Data::load()
{
loadfromRedis(key, {
"field1", "field2"}, field1, field2);
}
Data::save()
{
savetoRedis(key, "field1", field1, "field2", field2);
}
我们希望load调用loadfromRedis函数来实现从redis到对象数据的加载工作,loadfromRedis函数的第一个参数为redis数据库中存储的key,中间大括号括起来的是字段名称,剩下的参数是对象的数据成员;Data的save函数调用savetoRedis函数完成保存动作,第一个参数为key,后面参数依次为字段名称、值。有了大致的方向之后,我们梳理一下需求
- loadfromRedis(key, {“field1”, “field2”}, field1, field2):
- 字段名称需要使用初始化列表
- 字段值为多个成员变量,所以这里需要使用可变参模板,利用模板的类型自动推导将从redis读出来的字符串类型转换为对应的成员变量类型
- savetoRedis(key, “field1”, field1, “field2”, field2):
- 除第一个参数外,其他参数需使用可变参模块实现,利用模板的类型自动推导将所有成员变量自动转换为字符串形式进行存储
redis数据加载接口实现
有了上面的分析,我们可以开始编写模板的伪代码了
template<typename ... Args>
void loadfromRedis(const std::string &keys, std::initializer_list<std::string> fields, Args &&... args)
{
std::vector<std::string> values;
redis.hmget(keys, fields, values);
toArgs(values.begin(), values.end(), std::forward<Args>(args)...)
}
- step 1. 先定义一个vector类型的values类存储从redis读出来的value字符串
- step 2. 使用redis客户端接口hmget从指定key中读取出指定fields的value值,存入values中
- step 3. 将values的值依次转存到args中
这里需要注意的是可变参模板的参数类型,我这里使用的右值引用&&符作为参数的类型限定符,主要考虑到loadfromRedis这个函数本身并不直接对参数进行处理,而是将可变参数传递到toArgs函数里边进行处理,所以这里的可变参数仅仅只起到代理转发的作用,属于通用型的参数,即可以接受任意类型的参数。模板中的右值引用符&&与非模板函数的右值引用符&&用法有一点区别,模板中的右值引用符&&主要用于定义通用型的参数类型。另外虽然args即可为左值引用也可以为右值引用,但当args的值为右值引用时,args这个变量仍为左值,所以我们在使用args参数时,通过std::forward(args)将args的参数类型保持一致
另外还有一点需要注意,那就是省略参数符…的使用,可以这样使用
fun(args...)
</