有没有办法检索有关哪些参数被boost :: bind绑定的信息,还是需要手动存储?
即:
in .h
class MyClass
{
void foo(int a);
void foo2(double b);
void bar();
void execute();
int _myint;
double _mydouble;
}
in .cpp
MyClass::bar()
{
vector<boost::function<void(void)> myVector;
myVector.push_back(boost::bind(&MyClass::foo, this, MyClass::_myint);
myVector.push_back(boost::bind(&MyClass::foo2, this, MyClass::_mydouble);
}
MyClass::execute(char* param)
{
boost::function<void(void)> f = myVector[0];
//MAGIC goes here
//somehow know that an int parameter was bound
_myint = atoi(param);
//--------------------------------------
f();
}
答案 0 :(得分:6)
因为看起来你只是在寻找触发函数以响应解析文本的方法,所以我提出了这个基于Boost Spirit解析器的例子:
我希望能够调用各种类的预先存在的函数:
/exec <functionName> <param1> <param2>
想象一下,您的应用程序具有以下现有类,这些类表示用户应该能够使用文本命令调用的服务:
struct Echo
{
void WriteLine(const std::string& s) { std::cout << "WriteLine('" << s << "');" << std::endl; }
void WriteStr (const std::string& s) { std::cout << "Write(string: '" << s << "');" << std::endl; }
void WriteInt (int i) { std::cout << "Write(int: " << i << ");" << std::endl; }
void WriteDbl (double d) { std::cout << "Write(double: " << d << ");" << std::endl; }
void NewLine () { std::cout << "NewLine();" << std::endl; }
} echoService;
struct Admin
{
void Shutdown(const std::string& reason, int retval)
{
std::cout << "Shutdown(reason: '" << reason << "', retval: " << retval << ")" << std::endl;
// exit(retval);
}
} adminService;
那么我们如何将基于行的输入与这个'界面'联系起来呢? 有两个工作
您一直在强调为评估提供基础设施。但是,您遇到了不知道要传递什么参数的问题。当然,解析时你知道。您真的希望避免在通用容器中存储参数。 (当然,你可以抛出提升选项,提升(递归)变体,同时提升任何一个,但让我们面对它:这仍然是繁琐繁忙的工作)。
对于具有语义操作的解析器来说,这是一个绝佳的机会。 Lex / yacc,Coco / R C ++,ANTLR等等都支持它们。 Boost Spirit Qi 也是如此。
不用多说,这就是上述服务的完整,简约的线条语法:
parser = "/execute" > (
(lit("WriteLine") > stringlit)
| (lit("Write") >> +(double_ | int_ | stringlit))
| lit("NewLine")
| (lit("Shutdown") > (stringlit > -int_))
// stringlit is just a quoted string:
stringlit = lexeme[ '"' >> *~char_('"') >> '"' ];
注意:我决定向您展示如何接受/execute Write
调用的各种类型的任意数量的参数
添加语义操作以将其与我们的echoService
和adminService
对象联系起来,我们得到此REPL引擎来解析和评估单行:
void execute(const std::string& command)
{
typedef std::string::const_iterator It;
It f(command.begin()), l(command.end());
if (!phrase_parse(f,l, "/execute" > (
(lit("WriteLine")
> stringlit [ bind(&Echo::WriteLine, ref(echoService), _1) ])
| (lit("Write") >> +(
double_ [ bind(&Echo::WriteDbl, ref(echoService), _1) ]
| int_ [ bind(&Echo::WriteInt, ref(echoService), _1) ]
| stringlit [ bind(&Echo::WriteStr, ref(echoService), _1) ]
))
| (lit("NewLine") [ bind(&Echo::NewLine, ref(echoService)) ])
| (lit("Shutdown") > (stringlit > (int_ | attr(0)))
[ bind(&Admin::Shutdown, ref(adminService), _1, _2) ])
), space))
{
// handle error, see full code below
}
}
现在我们要做的就是编写一个主循环:
int main()
{
std::string command;
while (std::getline(std::cin, command))
execute(command);
}
这很简单,不是吗?
我在github上发布了这个程序的完整工作示例 1 :https://gist.github.com/1314900
它有
你所需要的只是提升。我用g ++ 4.6.1(没有特殊选项)和Boost 1.47测试了这个。对于以下测试输入( input.txt ):
/execute WriteLine "bogus"
/execute Write "here comes the answer: "
/execute Write 42
/execute Write 31415e-4
/execute Write "that is the inverse of" 24 "and answers nothing"
/execute Shutdown "Bye" 9
/execute Shutdown "Test default value for retval"
演示程序的输出是
WriteLine('bogus');
Write(string: 'here comes the answer: ');
Write(double: 42);
Write(double: 3.1415);
Write(string: 'that is the inverse of');
Write(double: 24);
Write(string: 'and answers nothing');
Shutdown(reason: 'Bye', retval: 9)
Shutdown(reason: 'Test default value for retval', retval: 0)
exit(...)
来电,因此我可以演示如何在提供默认值(retval
)attr(0)
参数可选
31415e-4
如何正确打印为3.1415
/execute Write
的多个参数如何在单独调用echoService.Write(...)
时转换,具体取决于输入参数的实际类型<子> 1 为了后代的利益,github应该停止主持我的要点:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <fstream>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
///////////////////////////////////
// 'domain classes' (scriptables)
struct Echo
{
void WriteLine(const std::string& s) { std::cout << "WriteLine('" << s << "');" << std::endl; }
void WriteStr (const std::string& s) { std::cout << "Write(string: '" << s << "');" << std::endl; }
void WriteInt (int i) { std::cout << "Write(int: " << i << ");" << std::endl; }
void WriteDbl (double d) { std::cout << "Write(double: " << d << ");" << std::endl; }
void NewLine () { std::cout << "NewLine();" << std::endl; }
} echoService;
struct Admin
{
void Shutdown(const std::string& reason, int retval)
{
std::cout << "Shutdown(reason: '" << reason << "', retval: " << retval << ")" << std::endl;
// exit(retval);
}
} adminService;
void execute(const std::string& command)
{
typedef std::string::const_iterator It;
It f(command.begin()), l(command.end());
using namespace qi;
using phx::bind;
using phx::ref;
rule<It, std::string(), space_type> stringlit = lexeme[ '"' >> *~char_('"') >> '"' ];
try
{
if (!phrase_parse(f,l, "/execute" > (
(lit("WriteLine")
> stringlit [ bind(&Echo::WriteLine, ref(echoService), _1) ])
| (lit("Write") >> +(
double_ [ bind(&Echo::WriteDbl, ref(echoService), _1) ] // the order matters
| int_ [ bind(&Echo::WriteInt, ref(echoService), _1) ]
| stringlit [ bind(&Echo::WriteStr, ref(echoService), _1) ]
))
| (lit("NewLine") [ bind(&Echo::NewLine, ref(echoService)) ])
| (lit("Shutdown") > (stringlit > (int_ | attr(0)))
[ bind(&Admin::Shutdown, ref(adminService), _1, _2) ])
), space))
{
if (f!=l) // allow whitespace only lines
std::cerr << "** (error interpreting command: " << command << ")" << std::endl;
}
}
catch (const expectation_failure<It>& e)
{
std::cerr << "** (unexpected input '" << std::string(e.first, std::min(e.first+10, e.last)) << "') " << std::endl;
}
if (f!=l)
std::cerr << "** (warning: skipping unhandled input '" << std::string(f,l) << "')" << std::endl;
}
int main()
{
std::ifstream ifs("input.txt");
std::string command;
while (std::getline(ifs/*std::cin*/, command))
execute(command);
}
答案 1 :(得分:2)
据我所知,你不能做你想做的事,因为std::function
抛弃了(差不多)传递的函数指针,函数对象或lambda的所有类型信息。它使用了一种称为type erasure的技术,并且在表面上完全忘记了传递给它的内容,只要它可以使用提供的参数进行调用。因此,绑定后,你似乎运气不好。
但,您可以自行提供该信息:
#include <functional>
#include <sstream>
#include <string>
#include <vector>
struct call_info{
std::function<void(void)> func;
std::string arg_type;
};
class MyClass{
std::vector<call_info> _myVec;
int _myInt;
double _myDouble;
public:
void foo1(int i);
void foo2(double d);
void bar();
void execute(char* param);
};
void MyClass::bar(){
call_info info;
info.func = std::bind(&MyClass::foo1, this, &MyClass::_myInt);
info.arg_type = "int";
_myVector.push_back(info);
info.func = std::bind(&MyClass::foo2, this, &MyClass::_myDouble);
info.arg_type = "double";
_myVector.push_back(info);
}
void MyClass::execute(char* param){
call_info& info = _myVector[0];
std::stringstream conv(param);
if(info.arg_type == "int")
conv >> _myInt;
else if(info.arg_type == "double")
conv >> _myDouble;
info.func();
}
不像自动提供它那么干净,但据我所知,没有更好的方法(除了完全改变你的实现,如同提议)。
答案 2 :(得分:0)
boost::function
有一些你应该可以使用的公共typedef。
template<typename Signature> // Function type R (T1, T2, ..., TN)
class function : public functionN<R, T1, T2, ..., TN> {
public:
// types
typedef R result_type;
typedef T1 argument_type; // If N == 1
typedef T1 first_argument_type; // If N == 2
typedef T2 second_argument_type; // If N == 2
typedef T1 arg1_type;
typedef T2 arg2_type;
// ...
// static constants
static const int arity = N;
答案 3 :(得分:0)
您的基本问题似乎是您的界面不够丰富。完成向向量中添加内容后(因此只能以多态方式开始使用函数对象),您不需要再次查看其具体类型。有几种方法可以解决这个问题:
使用另一个存储解析函数的回调:
vector<boost::function<void(char*)> myParseVector;
myParseVector.push_back(boost::bind(&MyClass::parseInt, this, MyClass::_myint);
myParseVector.push_back(boost::bind(&MyClass::parseDouble, this, MyClass::_mydouble);
..或者,当然,通过自定义结构或一对将这两个回调放入同一个向量中。
或者...使用支持parse
和execute
的自定义界面自行使用类型擦除!