1

我想存储一个函数指针向量,每个指针取不同的编号。类“存储”中的参数。因此,编写了一个模板类“Func”,它将函数存储为 std::function 并将其参数存储在一个元组中。

我从非模板基类“IFunc”派生了这个“Func”类,这样我就可以在“Store”类中存储指向这个基类的指针向量。

   template<typename... Args>
   class Func : public IFunc
   {

    public:
       std::function<void (Args...)> f;
       std::tuple<Args...> args;

       template <typename F,typename... Ar>
       Func(F&& func,Ar&&... arg): f(std::forward<F>(func)),args(std::make_tuple(std::forward<Ar>(arg)...))
       {   

       }

       virtual ~NonMemfun()
       {
       }

      //other methods to unpack the tuple and call the function

    };

IFunc 类:

    class IFunc
    {
     public:
     Ifunc(){}
     virtual ~Ifunc(){}     
    };

商店类:

class Store
{
  std::vector<Ifunc*> funcs;

  public:      
     template<typename... Args,typename... Args2>
     void registerfunc(std::string name,int runs,void(*f)(Args...),Args2&&... arg) 
     {  
       Func<Args2...>* sample = new Func<Args2...>(f,arg...);
       Ifunc* fp = sample;
       funcs.push_back(fp);
     }
};

我想遍历向量并调用每个函数。为此,我需要像这样进行静态转换:

Func<>* der = static_cast<Func<>*>(funcs[0]); 

当我尝试这样做时,演员表不会正确发生。我无法指定模板参数(可变参数),因为此类(存储)不知道它们。

我完全被困在这个地方。我猜我的设计有问题。有人可以建议我一个更好的方法来做到这一点。谢谢你。

4

2 回答 2

0

我不是 C++ 程序员,但我认为您可能会发现这很有用。

我确定您知道模板是 C++ 中的编译时事物,因此您的函数需要在构建时知道。

话虽如此,如果您确实知道您的函数,并且只想将它们映射为字符串命令,然后动态绑定来自流之类的参数,那么这段代码应该可以帮助您。它实际上可以使用 adynamic_cast从地图中检索命令。

这个片段来自我不久前做的一个有类似目标的学校项目:

#include <map>
#include <string>
#include <sstream>
#include <tuple>

using namespace std;

class Shell {
    class Command {
    public:
        virtual ~Command() {};
        virtual void executeWithArgStream(Shell*, istream& s)=0;
    };

    template <typename... ArgTypes>
    class ShellCommand : public Command {
    private:
        // FIXME: its probably more apropriate for FuncType to return an int for exit code...
        typedef function<void(Shell*, ArgTypes...)> FuncType;
        FuncType _f;
        tuple<ArgTypes...> args;

        template<int... Is>
        struct seq { };

        template<int N, int... Is>
        struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

        template<int... Is>
        struct gen_seq<0, Is...> : seq<Is...> { typedef seq<Is...> type; };

        template<size_t I = 0, class ...P>
        typename std::enable_if<I == sizeof...(P)>::type
        // template for functions with no arguments
        parseArgs(istream& is, std::tuple<P...> &) {}

        template<size_t I = 0, class ...P>
        typename std::enable_if<I < sizeof...(P)>::type
        parseArgs(istream& is, std::tuple<P...> & parts) {
            // this is the magic bit that takes a tuple of pointers (representing the command arguments)
            // created at compile time and creates new instances of each argument type and populates it from
            // the given input stream :D
            auto& part = std::get<I>(args);
            // hmmm should we delete or recycle...
            delete part;
            part = new typeof(*part);
            is >> *part;
            parseArgs<I + 1>(is, parts);
        }

        template<int ...S>
        void callFunc(Shell* shell, seq<S...>) {
            _f(shell, get<S>(args) ...);
        }
    public:
        static constexpr size_t numArgs = sizeof...(ArgTypes);

        ShellCommand(FuncType f) : _f(f) {};

        void operator()(Shell* shell, ArgTypes... args) {
            _f(shell, args...);
        };

        void executeWithArgStream(Shell* shell, istream& s)
        {
            parseArgs(s, args);
            callFunc(shell, typename gen_seq<sizeof...(ArgTypes)>::type());
        };
    };

private:
    typedef shared_ptr<Command> CommandPtr;
    typedef map<string, CommandPtr> FMap;

    FMap _cmdMap;
    ostream& _out;
    istream& _in;

public:
    Shell(istream& is = cin, ostream& os = cout)
    : _out(os), _in(is)
    {
        // populate
        _cmdMap.insert(pair<string, CommandPtr>("chdir", make_shared<ShellCommand<string*>>(&Shell::chdir)));
        _cmdMap.insert(pair<string, CommandPtr>("list", make_shared<ShellCommand<>>(&Shell::list)));
        _cmdMap.insert(pair<string, CommandPtr>("count", make_shared<ShellCommand<>>(&Shell::count)));
    };

    int run();
    // FIXME: its probably more apropriate for execute to return an int for exit code...
    template <typename... ArgTypes>
    void execute(string& command, ArgTypes... args);
    void executeWithArgStream(string& command, istream& istr);


    // shell commands:
    // any command parameters must be done as a pointer!
    // the magic that parses string arguments into real types depends on it!
    void list() {
        list command
    };

    void chdir(string* dir) {
        // chdir command
    };

    void count() {
        // count command
    };
};

template <typename... ArgTypes>
void Shell::execute(string& command, ArgTypes... args)
{
    typedef ShellCommand<ArgTypes...> CommandType;

    CommandType* c = dynamic_cast<CommandType*>(_cmdMap[command].get());

    // TODO: neeed to diferentiate between invalid commands and some kind of dynamic_cast failure
    if (c) {
        (*c)(this, args...);
    } else {
        // dynamic cast failure
        throw runtime_error("Broken Implementation for:" + command);
    }
}

void Shell::executeWithArgStream(string& command, istream& istr)
{
    Command* c = _cmdMap[command].get();
    if (c) {
        c->executeWithArgStream(this, istr);
    } else {
        throw runtime_error("Invalid Shell Command: " + command);
    }
}

int Shell::run()
{
    do {
        string cmd, argString;
        _out << _currentDir->name() << "> ";
        _in.clear();
        _in >> cmd;
        if (cmd == "q") {
            return 0;
        }

        if (_in.peek() == ' ')
            _in.ignore(1, ' ');
        getline(cin, argString);
        if (_cmdMap[cmd]) {
            try {
                if (argString.length()) {
                    istringstream s(argString);
                    executeWithArgStream(cmd, s);
                } else {
                    execute(cmd);
                }
            } catch (runtime_error& e) {
                _out << e.what() << endl;
            }
        } else {
            _out << "unrecognized command: " << cmd << endl;
        }
    } while (true);
}

int main(int argc, const char * argv[])
{
    // start the interactive "shell"
    Shell shell();
    return shell.run();
}
于 2013-12-04T02:06:12.723 回答
0

与其尝试从IFuncto进行强制转换Func<>,不如创建一个纯虚函数Apply()in IFunc,它Func<>定义为apply(f, args...);。当您遍历IFunc指针向量时,只需调用IFunc->Apply(),它将分派到Func<>::Apply()并执行实际应用。

于 2013-12-04T01:44:22.233 回答