使用boost :: bind和boost :: function:检索绑定变量类型

Sma*_*ash 4 c++ boost types

有没有办法检索有什么参数由boost :: bind限制的信息,还是需要手动存储?

即:

在.h

class MyClass
{
    void foo(int a);
    void foo2(double b);
    void bar();
    void execute();
    int _myint;
    double _mydouble;
}
Run Code Online (Sandbox Code Playgroud)

在.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();
}
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 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;
Run Code Online (Sandbox Code Playgroud)

方法和解释(TL; DR?跳过这个)

那么我们如何将基于行的输入与这个"接口"联系起来呢?有两个工作

  • 解析输入
  • 评估输入

您一直在强调为评估提供基础设施.但是,您遇到了不知道要传递什么参数的问题.当然,你知道解析的时候.您真的希望避免将参数存储在通用容器中.(当然,你可以抛出boost选项,提升(递归)变体,同时提升任何一个,但让我们面对它:这仍然是繁琐繁忙的工作).

对于具有语义动作的解析器来说,这是一个绝佳的机会.Lex/yacc,Coco/R C++,ANTLR等等都支持它们.因此,没有升压精神齐.

不用多说,这就是上述服务的完整,简约的线条语法:

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_('"') >> '"' ];
Run Code Online (Sandbox Code Playgroud)

注意:我决定向您展示如何在/execute Write调用中接受各种类型的任意数量的参数

添加语义动作以将其与我们echoServiceadminService对象联系起来,我们得到这个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
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们要做的就是编写一个主循环:

int main()
{
    std::string command;
    while (std::getline(std::cin, command))
        execute(command);
}
Run Code Online (Sandbox Code Playgroud)

这很简单,不是吗?


完整的工作示例程序

我在github 1上发布了这个程序的完整工作示例:https://gist.github.com/1314900

它有

  • 添加了完整的错误处理/报告
  • 使用输入文件而不是std :: cin(易于测试)
  • 将帮助您开始这个(命名空间,包括)

你所需要的只是提升.我用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"
Run Code Online (Sandbox Code Playgroud)

演示程序的输出是

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)
Run Code Online (Sandbox Code Playgroud)
  • 注意:我注释掉了exit(...)调用,因此我可以演示如何retval在提供默认值时使参数可选(attr(0))
  • 注意:double如何31415e-4正确打印为3.1415
  • 注意:/execute Writeget 的多个参数如何在单独的调用中进行转换,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);
}
Run Code Online (Sandbox Code Playgroud)