9

私は QI と Phoenix を使用しており、セマンティック アクション内の関数呼び出しの引数として使用される 4 つの bool を返す小さな文法を書きたいと考えています。

それらを必要とするいくつかの関数があり、これまでのところ、このアプローチを使用しています。

( qi::_bool >>  qi::_bool >>  qi::_bool >>  qi::_bool)
[px::bind(&Bool4Function, spirit::_val, spirit::_1, spirit::_2, spirit::_3, spirit::_4)]

それ自体は問題ありませんが、名前空間の部分を「使用」していても、あちこちで使用するのは見苦しく、混乱を招きます。

そのため、この表現をスタンドアロンの文法に抽出したいと考えました。

だから私はこれを試しました(クレジットはテストベッドのildjarnに行きます):

///// grammar implementation /////
#include <boost/fusion/include/vector10.hpp>
#include <boost/spirit/include/qi_bool.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_string.hpp>

struct FourBools : boost::spirit::qi::grammar<
    char const*,
    boost::fusion::vector4<bool, bool, bool, bool>()
>
{
    typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type;

    FourBools() : base_type(start_)
    {
        using boost::spirit::bool_;

        start_
            =   "4bools:"
            >> bool_ >> ','
            >> bool_ >> ','
            >> bool_ >> ','
            >> bool_ >> ';'
            ;
    }

private:
    boost::spirit::qi::rule<
        base_type::iterator_type,
        base_type::sig_type
    > start_;
};
FourBools const fourBools;


///// demonstration of use /////
#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>



void noDice(bool a, bool b, bool c, bool d) 
{

}

void worksFine(boost::fusion::vector4<bool, bool, bool, bool> a)
{

}
int main()
{
    namespace phx = boost::phoenix;
    namespace spirit = boost::spirit;

    std::string const input("4bools:true,true,true,false;");


    char const* first = input.c_str();
    char const* const last = first + input.size();
    bool const success = spirit::qi::parse(
        first, last,
        fourBools[phx::bind(&noDice, spirit::_1)]
    );


    if (!success)
        std::cout << "parse() failed\n";
    else if (first != last)
        std::cout << "didn't consume all input\n";
    std::cout.flush();
}

fourBools[phx::bind(&noDice, spirit::_1)]に置き換えない限り、コンパイルされませんfourBools[phx::bind(&worksFine, spirit::_1)]

つまり、私の問題は、引数の数が署名レベルで異なるため、呼び出される関数の署名に一致するように引数をアンパックすることです (4 つのブール値の 1 つのタプルと 4 つのブール値自体)。

タプルを個別の必要がある既存の関数の個々の引数に変換するラッパーを記述する代わりに、フェニックスプレースホルダーを直接使用してアンパックすることは可能ですか? もしそうなら、その構文は何ですか?結局のところ、プレースホルダー( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)によって「アンパック」されると、インライン バージョンは正常に動作します。spirit::_1 - spirit::_4,

そのため、このバージョンもタプルを返すかのように見え、タプルを返す文法とは異なり、上記のアプローチでは何らかの形でアンパック可能です。

どうすればこれに対処できますか?

4

3 に答える 3

12

完全で首尾一貫した再現を投稿しないと、問題を診断することはほとんど不可能です。それは構文エラーである可能性があり、欠落している可能性があり#includeます..?

これは実際のデモンストレーションです。うまくいけば、コードの何が問題なのかを理解するための参照として使用できます。

///// grammar implementation /////
#include <boost/fusion/include/vector10.hpp>
#include <boost/spirit/include/qi_bool.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_string.hpp>

struct FourBools : boost::spirit::qi::grammar<
    char const*,
    boost::fusion::vector4<bool, bool, bool, bool>()
>
{
    typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type;

    FourBools() : base_type(start_)
    {
        using boost::spirit::bool_;

        start_
            =   "4bools:"
                >> bool_ >> ','
                >> bool_ >> ','
                >> bool_ >> ','
                >> bool_ >> ';'
            ;
    }

private:
    boost::spirit::qi::rule<
        base_type::iterator_type,
        base_type::sig_type
    > start_;
};
FourBools const fourBools;


///// demonstration of use /////
#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>

typedef FourBools::attribute_type attr_t;

struct verify_same
{
    explicit verify_same(attr_t const& expected) : expected_(expected) { }

    void verify(attr_t const& actual) const
    {
        using boost::fusion::at_c;

        std::cout << std::boolalpha
            << "same as expected: " << (actual == expected_)
            << "\nactual values: "
            << at_c<0>(actual) << ' '
            << at_c<1>(actual) << ' '
            << at_c<2>(actual) << ' '
            << at_c<3>(actual) << '\n';
    }

private:
    attr_t expected_;
};

int main()
{
    namespace phx = boost::phoenix;
    namespace spirit = boost::spirit;

    std::string const input("4bools:true,true,true,false;");
    verify_same const vs(attr_t(true, true, true, false));

    char const* first = input.c_str();
    char const* const last = first + input.size();
    bool const success = spirit::qi::parse(
        first, last,
        fourBools[phx::bind(&verify_same::verify, phx::cref(vs), spirit::_1)]
    );
    if (!success)
        std::cout << "parse() failed\n";
    else if (first != last)
        std::cout << "didn't consume all input\n";
    std::cout.flush();
}

余談ですが、純粋に同種の型を持つタプルを使用するのは奇妙だと思います。個人的には、文法の合成属性を に変更しますboost::array<bool, 4>


編集(OPの編集に応じて):良いニュースと悪いニュース、さらに良いニュースがあります.

良いニュースがあります: Boost.Fusionには、最小限のコードでやりたいことを正確に行う機能があります: boost::fusion::fused<>. これは、複数の引数を取る呼び出し可能な型 (フリー関数ポインターとメンバー関数ポインターを含む) を取り、Fusion シーケンスを取るファンクターでその呼び出し可能な型をラップします。このファンクターが呼び出されると、Fusion シーケンスを取得してアンパックし、タプルの個々の要素をラップされた呼び出し可能な型に個別の引数として転送します。

したがって、私がすでに投稿した文法と次のことを考えると:

#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/fusion/include/make_fused.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>

typedef FourBools::attribute_type attr_t;

void free_func_taking_tuple(attr_t const& tup)
{
    using boost::fusion::at_c;

    std::cout << std::boolalpha
        << "inside free_func_taking_tuple() :: "
        << at_c<0>(tup) << ' '
        << at_c<1>(tup) << ' '
        << at_c<2>(tup) << ' '
        << at_c<3>(tup) << '\n';
}

void free_func_taking_bools(
    bool const a, bool const b,
    bool const c, bool const d
)
{
    std::cout << std::boolalpha
        << "inside free_func_taking_bools() :: "
        << a << ' '
        << b << ' '
        << c << ' '
        << d << '\n';
}

boost::spirit::qi::parse()次のように呼び出すことができます:

namespace phx = boost::phoenix;
namespace spirit = boost::spirit;
using boost::fusion::make_fused;

// calls free_func_taking_tuple, nothing new here
spirit::qi::parse(
    first, last,
    fourBools[phx::bind(free_func_taking_tuple, spirit::_1)]
);

// calls free_func_taking_bools, using boost::fusion::fused<> to unpack the tuple
// into separate arguments
spirit::qi::parse(
    first, last,
    fourBools[phx::bind(make_fused(&free_func_taking_bools), spirit::_1)]
);

悪いニュースがあります。Boost.Fusion の呼び出し可能型ラッパーは TR1/C++11result_ofプロトコルに依存していますが、Boost.Phoenix v2 は Boost.Lambdaresult_ofプロトコルを実装しています。これらには互換性がありません。そのため、タプル要素を自分で解凍する必要があります。

namespace phx = boost::phoenix;
namespace spirit = boost::spirit;

spirit::qi::parse(
    first, last,
    fourBools[phx::bind(
        free_func_taking_bools,
        phx::at_c<0>(spirit::_1),
        phx::at_c<1>(spirit::_1),
        phx::at_c<2>(spirit::_1),
        phx::at_c<3>(spirit::_1)
    )]
);

うん!しかし、さらに良いニュースがあります。Boost.Phoenix v3 が Boost 1.47 でリリースされる予定で、TR1/C++11result_ofプロトコルを実装しています。boost::fusion::fused<>したがって、Boost 1.47 以降では、面倒なボイラープレートを使用して節約することができます。

于 2011-05-27T22:46:02.680 に答える
3

一般的な注意事項として、Spirit Web サイトの属性処理に関する記事を読むことをお勧めします。これらは、ライブラリと共に配布されているオンライン ドキュメントへの優れた補足です。

于 2011-05-28T00:02:56.463 に答える
0

qi::_bool >> qi::_bool >> qi::_bool >> qi::_boolリファレンスで説明されているisstd::vector<bool>またはその他の stl コンテナーの属性: http://www.boost.org/doc/libs/1_46_0/libs/spirit/doc/html/spirit/qi/quick_reference/compound_attribute_rules.html .

表の最初の行がそのケースです:)

于 2011-05-27T20:58:41.797 に答える