Reputation:
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
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