1

I wanted to store a vector of function pointers, each taking different no. of arguments in a class "Store". So, wrote a templated class "Func" that would store the function as a std::function and its arguments in a tuple.

I derived this "Func" class from a non-template base class "IFunc", so that i can store a vector of pointers to this base class in the class "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

    };

The IFunc class:

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

The Store class:

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);
     }
};

I want to iterate through the vector and call each function. To do that i need to do a static cast like this:

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

When i try to do this, the cast doesn't happen properly. I cannot specify the template paramenters(variadics) since this class(Store) is not aware of them.

I am totally stuck at this place. Something is wrong with my design i guess. Can someone please suggest me a better way to do this. Thank you.

Brad Allred
  • 7,323
  • 1
  • 30
  • 49

2 Answers2

0

Rather than trying to do a cast from IFunc to Func<>, you should make a pure virtual function, Apply() in IFunc, which Func<> defines as apply(f, args...);. As you iterate over the vector of IFunc pointers, simply call IFunc->Apply(), which will dispatch to the Func<>::Apply() and do the actual apply.

mpark
  • 7,574
  • 2
  • 16
  • 18
0

I'm not much of a C++ programmer, but I think you may find this useful.

I'm sure you know that templates are a compile time thing in C++ so your functions need to be known at build time.

With that said, if you do know your functions and you just want to map them to say a string command and then dynamically bind arguments from something like a stream then this code should help you. It is actually able to use a dynamic_cast to retrieve the command from the map.

this snippet is from a school project I did a while back that had a similar goal:

#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();
}
Brad Allred
  • 7,323
  • 1
  • 30
  • 49