从非模板基类执行静态转换到具有可变参数模板参数的模板化派生类(c ++)

时间:2013-06-27 10:49:17

标签: c++11 variadic-templates template-inheritance

我想存储一个函数指针的向量,每个函数指针都取不同的值。 “Store”类中的参数。所以,编写了一个模板化的类“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]); 

当我尝试这样做时,演员阵容不会正常发生。我无法指定模板参数(variadics),因为这个类(Store)不知道它们。

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

2 个答案:

答案 0 :(得分:0)

您应该在IFunc中创建一个纯虚函数Func<>,而Apply()定义IFunc,而不是尝试从Func<>转换为apply(f, args...);IFunc。当您迭代IFunc->Apply()指针的向量时,只需调用Func<>::Apply(),它将调度到{{1}}并执行实际应用。

答案 1 :(得分:0)

我不是一个C ++程序员,但我认为你可能会觉得这很有用。

我确信你知道模板在C ++中是一个编译时间,所以你的函数需要在构建时知道。

话虽如此,如果你确实知道你的函数并且你只是想映射它们来说一个字符串命令,然后动态地绑定类似流的参数,那么这段代码应该可以帮到你。它实际上可以使用dynamic_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();
}