user4807452
user4807452

Reputation:

variadic template argument for std::function

Recently, I've been working on a little project alongside my c++ game-dev engine : it's a programming language, written in C++, in one header, named kickC. Here is what I have done so far : (See question below)

#ifndef KICK_C_INCLUDED_H
#define KICK_C_INCLUDED_H
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <exception>
#include <functional>
#include <unordered_map>
#include <vector>
#define LOG(x) std::cout << x << std::endl;

namespace strutil
{
    inline unsigned CountWords(const std::string& value){
        std::string temp = value;
        std::replace_if(temp.begin(), temp.end(), std::ptr_fun<int, int>(std::isspace), ' ');
        temp.erase(0, temp.find_first_not_of(" "));
        if(temp.empty())
            return 0;
        return std::count(temp.begin(), std::unique(temp.begin(), temp.end()), ' ') + !std::isspace(*value.rbegin());
    }
}

class KickCException : std::exception
{
public:
    explicit KickCException(const char* msg, bool fatal = false)
        :   msg_(msg){}
    explicit KickCException(const std::string& msg)
        : msg_(msg){}
    virtual ~KickCException() throw(){}
    virtual const char* what() const throw(){
        return std::string("[error :] [")
            .append(msg_)
            .append("]")
            .c_str();
    }
protected:
    std::string msg_;

};
class KickCFileException : KickCException
{
public:
    explicit KickCFileException(const char* msg)
        : KickCException(msg){}
    explicit KickCFileException(const std::string& msg)
        : KickCException(msg){}
    virtual ~KickCFileException() throw(){}
    const char* what() const throw() override{
        return std::string("[file error :] [")
            .append(msg_)
            .append("]")
            .c_str();
    }
};
class KickCEmptyStringException : KickCException
{
public:
    explicit KickCEmptyStringException(const char* msg)
        : KickCException(msg){}
    explicit KickCEmptyStringException(const std::string& msg)
        : KickCException(msg){}
    virtual ~KickCEmptyStringException() throw(){}
    const char* what() const throw() override{
        return std::string("[empty string error :] [")
            .append(msg_)
            .append("]")
            .c_str();
    }
};


class KickCAPIBehaviourImplementation
{
public:
    KickCAPIBehaviourImplementation(){}
    ~KickCAPIBehaviourImplementation(){}

    void AddDefined(const std::string& str, std::function<void(void)> func){
        m_values[str] = func;
    }
    void ParseAndApplyLine(const std::string& line){
        std::istringstream iss(line);

        for(unsigned i = 0; i < strutil::CountWords(line); ++i){
            static std::string word = "";
            iss >> word;
            for(auto it_map = m_values.begin(); it_map != m_values.end(); ++it_map){
                if(it_map->first == word)
                {
                    (it_map->second)(/*HERE ! GIVE SOME ARGUMENTS ! */);
                }
            }
        }
    }
private:
    std::unordered_map<std::string, std::function<void(void)>> ///so far, args is void... m_values;
};

#endif //KICK_C_INCLUDED_H

///src

int main(int argc, const char** args){
    std::ifstream file("script.kick");
    KickCAPIBehaviourImplementation kickCApiBehaviour;
    try{
        if(!file.is_open())
            throw KickCFileException("unvalid fileName taken at input");
        kickCApiBehaviour.AddDefined("print", [&](void){std::cout << "print found !" << std::endl;});
        while(!file.eof()){
            std::string line;
            std::getline(file, line);
            kickCApiBehaviour.ParseAndApplyLine(line);
        }
    }catch(KickCException& e){
        LOG(e.what());
    }
    file.close();
  std::cin.get();
}

So here is the Question : I would like to pass std::function (see class KickCAPIBehaviourImplementation ) a variable argument of types : I need to use variatic templates, of course, but the question how can I implement it so i end up calling my functions like this ?

kickCApiBehaviour.AddDefined("print", [&](int arg1, char * arg2, int arg3){std::cout << arg1 << arg2 << arg3 << std::endl;});

Upvotes: 1

Views: 199

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275200

Move the parser into the std::function.

Where you add the function, include a signature:

// helper type:
template<class T>struct tag{using type=T;};

kickCApiBehaviour.AddDefined(
  "print", // the name
  tag<void(int,char*,int)>{}, // the signature
  [&](int arg1, char * arg2, int arg3){
    std::cout << arg1 << arg2 << arg3 << std::endl;
  } // the operation
);

store a std::function< error_code(ParserState*) >. Inside AddDefined, store a lambda that includes a call to the code that parses arguments and calls the passed in lambda:

template<class R, class...Args, class F>
void AddDefined(std::string name, tag<R(Args...)>, F f) {
  std::function< error_code(ParserState*) > r =
    [f](ParserState* self)->error_code {
      // here, parse each of Args... out of `self`
      // then call `f`.  Store its return value,
      // back in `self`.  If there is a parse error (argument mismatch, etc),
      // return an error code, otherwise return no_error
    };
  m_values[name] = r;
};

then m_values contains the operation "take a parser state, and parse the arguments, and call the function in question on them".

Upvotes: 1

Related Questions