我的程序从诸如“w test.txt 5”的文本文件中读取“命令”,用于写入test.txt数字5或“r test.txt”用于从test.txt读取。而不是有一个可怕的开关循环来维护我有一个名为aFunction的函数成员
string name;
void (*thefunction)(char *argsIn[], char *out);
所以我有一个字符串名称和一个函数指针。课外我有一个
vector<aFunction> funcVec
包含所有功能。当我从文本文件中读取命令时,代码通过funcVec查找正确的函数来调用
所以当funcVec.name =命令读入
时(*funcVec[i].theFunction(other values from the text file, output);
例如我可能有功能read(char *argsIn[], char *out)
其中argsIn是一个包含test.txt的数组,5和char out可能是1或0,具体取决于操作是否成功。
但是我并不是很喜欢这个,因为现在所有函数都必须有签名(char * argsIn [],char * out),并且由函数决定列表中每个参数的含义。< / p>
有人能想到更好的实施吗?当然,支持脚本编写的软件必须应对这种事情吗?
答案 0 :(得分:3)
注意:您最好使用std::string
和std::vector
Command
模式通常是这样做的方式,这允许在对象中“打包”输入/输出参数,并提供“空白”执行方法void operator()()
,以确保公共接口。 / p>
编辑:演示命令(通用)。
定义一些命令:
struct Command: boost::noncopyable
{
virtual void do() = 0;
virtual void undo() = 0;
virtual ~Command() {}
};
class SaveFile: public Command
{
public:
explicit SaveFile(FileHandle f, Changes c): _file(file), _changes(c) {}
virtual void do() { _file.apply(_changes); }
virtual void undo() { _file.revert(_changes); }
private:
FileHandle _file;
Changes _changes;
};
class OpenFile: public Command
{
public:
explicit OpenFile(std::string filename): _filename(filename) {}
FileHandle get() const { return _file; }
virtual void do() { _file.load(_filename); }
virtual void undo() { /*nothing to be done*/ }
private:
std::string _filename;
FileHandle _file;
};
使用两组动作的示例:要执行的动作和已执行的动作。
typedef std::stack<std::unique_ptr<Command>> CommandStack;
void transaction(CommandStack& todo)
{
CommandStack undo;
try
{
while(!todo.empty())
{
todo.top()->do();
undo.push(std::move(todo.top()));
todo.pop();
}
}
catch(std::exception const&)
{
while(!undo.empty())
{
undo.top()->do();
undo.pop();
}
}
} // transaction
答案 1 :(得分:1)
阅读这些命令需要做的事情有三个:
抽象#2非常难,因为C ++几乎不支持处理仅在运行时已知的不同类型,但这并非不可能。
我曾经看过一篇文章,其中有人使用模板元编程来查找已注册函数的参数,然后生成将字符串列表分解为匹配参数的代码。函数保存在函数指针的关键字符串映射中(使用类型擦除来存储具有不同签名的函数)。
这是一个关于如何使用类型擦除来存储地图的不同函数指针的草图:
struct func_base {
virtual void operator()(std::istream&) const = 0;
};
template< typename F >
class function : public func_base {
public:
function(F f) : func_(f) {}
void operator()(std::string& arguments) const;
private:
func_base func_;
};
typedef std::map< std::string, std::shared_ptr<func_base> > func_map;
template< typename F >
void addFunc(func_map& map, const std::string& keyword, F f)
{
assert(map.find(keyword) == map.end());
map[keyword] = std::shared_ptr<func_base>(new function<T>(f));
}
这会让function<F>::operator()()
将参数切换成单个字符串,将它们转换为适当的类型,然后用它们调用函数。
将字符串切换成参数列表应该不是问题,所以我将跳过它。困难的部分是使用给定该列表的正确参数调用函数。请注意,函数的类型在编译时function<F>::operator()()
中是已知的,因此您可以使用整个模板元编程技术。
ISTR该文章通过根据函数的参数列表创建元组来实现这一点,并且具有在给定这样的元组的情况下调用任何函数的方法。这里你可以用递归调用创建这样的元组:
template< typename Tuple >
Tuple convert_args(const std::string& line)
{
Tuple result;
// I don't know tuples well enough yet, so here's just an
// algorithm rather than code:
// 1. read first argument from line and put it into tuple's first element
// 2. call yourself for a tuple that consists of the remaining elements of Tuple
// 3. fill the remaining tuple elements from the result of #2
return result
}
然后使用特征来调用这些函数:
template<typename F>
struct func_traits;
template<typename R, typename A1>// function taking one arg
struct func_traits<R(*)()> {
typedef std::tuple<A1> arg_list;
static R call(R(*f)(), const arg_list& args)
{
return f(std::get<0>(arg_list)); // how do you access an element in a tuple
}
};
template<typename R, typename A1, typename A2>// function taking two args
struct func_traits<R(*)()> {
typedef std::tuple<A1,A2> arg_list;
static R call(R(*f)(), const arg_list& args)
{
return f(std::get<0>(arg_list), std::get<1>(arg_list));
}
};
// repeat for as many args as you'll need
答案 2 :(得分:0)
一个非常简单的实现是使用std::map
而不是您使用的std:vector
:
typedef void (*FunctionType)(char *argsIn[], char *out);
std::map<std::string, FunctionType> functionMap;
//populate the functionMap
//key = commandName, value = function to be called;
functionMap["write"] = WriteFunc;
functionMap["read"]= ReadFunc;
//later use this map as
functionMap[commandName](arguments, output);