您的位置:首页 > 编程语言 > C语言/C++

静态map成员的一种替代方法

2015-11-13 14:20 447 查看
问题场景

解决方法
尝试一

尝试二

尝试三

尝试四

其他思路

问题场景

有一个task接口类,存在一个对task操作的op_proxy静态函数,task可能有多种不同类型的操作,现在要实现这个op_proxy,原型为:

int op_proxy(const std::string &task_id, const std::string &op_type);


if else之类的太“俗”了,想通过一种更为优雅的方式来实现,有什么方法?

解决方法

一般来说可以这么做,在task接口类中新增一个静态的map程序变量op_list,来保存操作类型和操作函数之间的映射,类似这样:

#ifndef _TASK_INTERFACE_H_
#define _TASK_INTERFACE_H_

#include <string>
#include <map>

typedef int (*OpFun)(const std::string &);

class TaskInterface
{
public:
static int op_proxy(const std::string &task_id, const std::string &op_type);

public:
static std::map<std::string, OpFun> _op_list;
};

#endif //_TASK_INTERFACE_H_


那么,问题来了:这个静态的map成员如何初始化?

尝试一

初始化map的问题在于要给map附一个初始值,可是map的默认构造中不能像数组一样直接赋值:

int a[3] = {1, 2, 3};


假如map类型的对象_op_list是某一个类的一般成员变量(没有static修饰),那么_op_list可以在构造函数中赋值完成实例化

TaskInterface::TaskInterface()
{
_op_list["run"] = op_task_run;//op_task_run为run命令的操作函数
......
}


如果有static修饰的话,static变量初始化的语法是: <数据类型><类名>::<静态数据成员名>=<值> 即:

std::map<std::string, OpFun> TaskInterface::_op_list;


这时候就默认的构造了一个空的map,接下来想要像全局函数一样赋值:

TaskInterface::_op_list["run"] = op_task_run;


这是不可以的!

尝试二

现在考虑把赋值定义在某个函数中,这样的话只有在函数被调用的时候,map变量才会被初始化。可是问题是我们的期望是在执行main之前并且在类的构造函数调用之前,这个map变量就已经实例化并且已经赋值,因为可能在某些其他地方也需要使用这个map变量中的值。

所以,这样做也不可行。

尝试三

先定义一个全局数组,用数组来初始化这个map变量。可是如果已经有这样一个全局数组的话,那么这个map变量显得就多余了。另外怎么用它初始化这个map变量?可以使用超强的boost::map_list_of。

boost固然强大,但是这么一个小问题就是用boost的话,未免过“重”了。

尝试四

网上找的一种解决思路,用一个初始化类来初始化这个静态map成员:

定义一个初始化类

class init
{
inline void init(map<int, int>& a) { // 初始化 a };
};


在包含TaskInterface类中, 声明一个init类型的static变量

class TaskInterface
{
public:
static std::map<std::string, OpFun> _op_list;
static init _build_map;//为了初始化_op_list
};


调用_build_map的构造函数并将_op_list传入

std::map<std::string, OpFun> TaskInterface::_op_list;//先初始化为空的map
init TaskInterface::_build_map(_op_list);//再初始化


这个方法能够解决问题,但是却莫名其妙的多了一个init类,和我们要解决的业务逻辑毫无关系,它的存在仅仅是为了初始化类TaskInterface的static map对象。

还有没有别的解决方法呢?看起来优雅的那种。

其他思路

既然静态map成员初始化如此费尽,为什么一定要用static map呢?!

我的方法:用一个静态结构体数组来替代静态map。这个思路来源于许多开源代码中的选项操作的实现,比如nginx、ffmpeg中都有用到类似的方法来处理选项。

代码如下:

//op_task.h 文件,声明了所有task的操作函数,及静态的_op_list变量
#ifndef _OP_TASK_H_
#define _OP_TASK_H_

#include <string>

typedef int (*OpFun)(const std::string &);

struct OpInfo
{
std::string _op_type;
OpFun _op_fun;
};

class OpTask
{
public:
static int op_task_run(const std::string &task_id);
static int op_task_pause(const std::string &task_id);
static int op_task_retry(const std::string &task_id);
static int op_task_stop(const std::string &task_id);

public:
static OpInfo _op_info_list[];
};

#endif //_OP_TASK_H_


//op_task.cpp 文件,操作函数的实现及_op_list的初始化
#include <stdio.h>
#include "op_task.h"

OpInfo OpTask::_op_info_list[] = {
{"run",   OpTask::op_task_run},
{"pause", OpTask::op_task_pause},
{"retry", OpTask::op_task_retry},
{"stop",  OpTask::op_task_stop},
{"",      NULL}
};

int OpTask::op_task_run(const std::string &task_id)
{
printf("run task<%s> ...\n", task_id.c_str());
//do some running logic.
return 0;
}
.....


//task_interface.h 文件,TaskInterface接口类
#ifndef _TASK_INTERFACE_H_
#define _TASK_INTERFACE_H_

#include <string>

class TaskInterface
{
public:
static int op_proxy(const std::string &task_id, const std::string &op_type);
};

#endif //_TASK_INTERFACE_H_


//task_interface.cpp 文件,op_proxy的实现
#include <stdio.h>
#include "task_interface.h"
#include "op_task.h"

int TaskInterface::op_proxy(const std::string &task_id, const std::string &op_type)
{
int ret = 0;
OpInfo * info = &OpTask::_op_info_list[0];
while(info->_op_type != op_type && info->_op_type != "") info++;
if (info->_op_fun == NULL) {
printf("Unknown  op_type: %s\n", op_type.c_str());
ret = -1;
}
else {
ret = info->_op_fun(task_id);
}
return ret;
}


测试代码就不贴了。

总体来说,这个方法还算优雅,如有其他思路,欢迎交流。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C-C++