要调用的函数列表。必须有相同的签名。更好的实施?

时间:2011-02-04 15:47:24

标签: c++ delegates function-pointers

我的程序从诸如“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>

有人能想到更好的实施吗?当然,支持脚本编写的软件必须应对这种事情吗?

3 个答案:

答案 0 :(得分:3)

注意:您最好使用std::stringstd::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)

阅读这些命令需要做的事情有三个:

  1. 找出要调用的函数。
  2. 将参数字符串列表转换为右类型的参数。
  3. 调用函数传递这些参数以执行任何需要完成的操作。
  4. 抽象#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);