#include <string>
#include <vector>
#include <map>
#include <assert.h>
#include <iostream>

using namespace std;
class Variant{
public:
    enum Type{
        INT,
        FLOAT,
        STRING
    };
    Variant(int i){
        m_type = INT;
        m_int = i;
    }
    Variant(float f){
        m_type = FLOAT;
        m_float = f;
    }
    Variant(const string &s){
        m_type = STRING;
        m_string = s;
    }
    
    Type m_type;
    int m_int;
    float m_float;
    string m_string;
};

class ArgList{
public:
    template<class T>
    ArgList(const T &val){
        m_args.emplace_back(val);
    }
    template<class HEAD, class... TAIL>
    ArgList(const HEAD &head,const TAIL&... tail){
        m_args.emplace_back(head);
        ArgList t(tail...);
        m_args.insert(m_args.end(),t.m_args.begin(),t.m_args.end());
    }

    vector<Variant> m_args;
};

class ICommand{
public:  
    virtual void execute(const ArgList& args) = 0;
    template<class... ARGS>
    void executeList(const ARGS &... args){
        execute(ArgList(args...));
    }
};

class Add : public ICommand{
public:
    virtual void execute(const ArgList& args){
        assert(args.m_args.size() == 2);
        assert(args.m_args[0].m_type == Variant::INT);
        assert(args.m_args[1].m_type == Variant::INT);
        cout << "Add result:" << args.m_args[0].m_int + args.m_args[1].m_int << endl;
    }
};
class Negate : public ICommand{
public:
    virtual void execute(const ArgList& args){
        assert(args.m_args.size() == 1);
        assert(args.m_args[0].m_type == Variant::INT);
        cout << "Negate result:" << -args.m_args[0].m_int<< endl;
    }
};

class Print : public ICommand{
    virtual void execute(const ArgList& args){
        for(auto &var : args.m_args){
            switch(var.m_type){
                case Variant::INT:
                    cout << "int " << var.m_int << ",";
                    break;
                case Variant::FLOAT:
                    cout << "float " << var.m_float << ",";
                    break;
                case Variant::STRING:
                    cout << "string " << var.m_string << ",";
                    break;   
            }
        }
        cout << endl;
    }
};
int main(){
    map<string,ICommand*> commands;
    commands["Add"] = new Add();
    commands["Negate"] = new Negate();
    commands["Print"] = new Print();
    
    commands["Add"]->executeList(1,2);
    commands["Negate"]->executeList(4);
    commands["Print"]->executeList(1,2.0f,"helloworld!");
}