trouble with transform_attribute / attr_cast

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

trouble with transform_attribute / attr_cast

Tobias Loew
Hi,

I was playing around with qi's parse_date.cpp example and found out that replacing the rule

        typedef boost::fusion::vector<int, int, int> date_parts;
        qi::rule<Iterator, date_parts(), qi::space_type> date =
            qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;


by

// (1)
        qi::rule<Iterator, boost::gregorian::date(), qi::space_type> date =
            qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;

results in compilelable code that will parse successfully by will NOT call the customized transform_attribute-code and thus let's the date uninitialized

while

        typedef boost::fusion::vector<int, int, int> date_parts;

        qi::rule<Iterator, date_parts(), qi::space_type> date_raw =
            qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;
        qi::rule<Iterator, boost::gregorian::date(), qi::space_type> date = date_raw;

will do the job correctly.

Furthermore

// (2)
        qi::rule<Iterator, boost::gregorian::date(), qi::space_type> date =
            qi::attr_cast(qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_);

and

        qi::rule<Iterator, boost::gregorian::date(), qi::space_type> date =
            qi::attr_cast<boost::gregorian::date>(qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_);

result in compilation errors, while

// (3)
        qi::rule<Iterator, boost::gregorian::date(), qi::space_type> date =
            qi::attr_cast<boost::gregorian::date, date_parts>(qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_);

works again correctly. IMO it is quite dangerous that (1) compiles and yields wrong behavior. Case (3) works for me but (2) would be better and (1) (with correct semantics) would of course be best (if any of this is manageable).

Tobias

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general
Reply | Threaded
Open this post in threaded view
|

Re: trouble with transform_attribute / attr_cast

Tobias Loew
I just figured out something even worse: explicitly using attr_cast in a grammar (not just a single rule) won't spirit-runtime-compile or even results in runtime-errors.

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/date_time.hpp>
#include <boost/spirit/home/qi/auxiliary/attr_cast.hpp>

// define custom transformation
namespace boost { namespace spirit { namespace traits
{
    // This specialization of the customization point transform_attribute
    // allows to pass a boost::gregorian::date to a rule which is expecting
    // a fusion sequence consisting out of three integers as its attribute.
    template<>
    struct transform_attribute<
        boost::gregorian::date, fusion::vector<int, int, int>, qi::domain>
    {
        typedef fusion::vector<int, int, int> date_parts;

        // The embedded typedef 'type' exposes the attribute as it will be
        // passed to the right hand side of the rule.
        typedef date_parts type;

        // The function pre() is called for down-stream conversion of the
        // attribute supplied to the rule to the attribute expected by the
        // right hand side.
        // The supplied attribute might have been pre-initialized by parsers
        // (i.e. semantic actions) higher up the parser hierarchy (in the
        // grammar), in which case we would need to properly initialize the
        // returned value from the argument. In this example this is not
        // required, so we just create a new instance of a date_parts.
        static date_parts pre(boost::gregorian::date)
        {
            return date_parts();
        }

        // The function post() is called for up-stream conversion of the
        // results returned from parsing the right hand side of the rule.
        // We need to initialize the attribute supplied to the rule (referenced
        // by the first argument) with the values taken from the parsing
        // results (referenced by the second argument).
        static void post(boost::gregorian::date& d, date_parts const& v)
        {
            d = boost::gregorian::date(fusion::at_c<0>(v), fusion::at_c<1>(v)
                , fusion::at_c<2>(v));
        }

        // The function fail() is called whenever the parsing of the right hand
        // side of the rule fails. We don't need to do anything here.
        static void fail(boost::gregorian::date&) {}
    };
}}}

///////////////////////////////////////////////////////////////////////////////
namespace client
{

    namespace fusion = boost::fusion;
    namespace phoenix = boost::phoenix;
    namespace qi = boost::spirit::qi;
    namespace char_enc = boost::spirit::iso8859_1;

    static qi::uint_parser<uint8_t, 16, 2, 2> hx;

    typedef boost::fusion::vector<int, int, int> date_parts;
    template <typename Iterator>
    struct data_grammar
        : qi::grammar<Iterator, boost::gregorian::date(), qi::space_type>
    {
        data_grammar()
            : data_grammar::base_type(date)
        {
#define USE_CASE 6

#if USE_CASE == 1
            // parsing succeeds but user-transform won't be invoked -> date uninitialized
            date  %=                     qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;

#elif USE_CASE == 2
            // parsing succeeds and user-transform get's invoked -> ok
            date  %=                     date_raw;
            date_raw  %=                 qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;

#elif USE_CASE == 3
            // runtime-compilation of rule date fails -> parsing fails
            date  %=                    qi::attr_cast(date_raw);
            date_raw  %=                 qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;

#elif USE_CASE == 4
            // runtime-compilation of rule date fails -> parsing fails (same as case 3)
            date  %=                    qi::attr_cast<boost::gregorian::date, date_parts>(date_raw);
            date_raw  %=                 qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;

#elif USE_CASE == 5
            // let's add a prefix...
            // runtime-compilation of rule date fails -> parsing fails
            date  %=                    qi::attr_cast<boost::gregorian::date, date_parts>(date_raw);
            date_raw  %=                qi::lit("date:") >> qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;

#elif USE_CASE == 6

            // ...and inline the rule
            // runtime-error when runtime-compiling the rhs
            date  %=                    qi::attr_cast<boost::gregorian::date, date_parts>(qi::lit("date:") >> qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_);
#endif
        }

        qi::rule<Iterator, boost::gregorian::date(), qi::space_type> date;
        qi::rule<Iterator, date_parts(), qi::space_type> date_raw;
    };


    template <typename Iterator>
    bool parse_date(Iterator& first, Iterator last, boost::gregorian::date& d)
    {
        typedef data_grammar<std::string::const_iterator> data_grammar;
        data_grammar date_gr; // Our grammar

        bool r = false;
        try
        {
            r = phrase_parse(first, last, date_gr, qi::space, d);
        }
        catch( std::exception& )
        {
            r = false;
        }
        return true;
    }
}

///////////////////////////////////////////////////////////////////////////////
int main()
{
    std::string str;

#if USE_CASE >= 5
    str="date:2016-12-11";
#else
    str="2016-12-11";
#endif
    boost::gregorian::date d;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    bool r = client::parse_date(iter, end, d);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "got: " << d << std::endl;
        std::cout << "\n-------------------------\n";
    }
    else
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "-------------------------\n";
    }

    return 0;
}
Reply | Threaded
Open this post in threaded view
|

Re: trouble with transform_attribute / attr_cast

sehe
In reply to this post by Tobias Loew
On 08/28/2015 12:42 PM, Löw, Tobias (STEAG Energy Services GmbH) wrote:
> Hi,
>
> I was playing around with qi's parse_date.cpp example and found out that replacing the rule
>
>         typedef boost::fusion::vector<int, int, int> date_parts;
>         qi::rule<Iterator, date_parts(), qi::space_type> date =
>             qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_;
>
>
I think you rediscovered the undefined behaviour case I found:
http://boost.2283326.n4.nabble.com/transform-attribute-segfault-tp4657399p4657403.html


I didn't check

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general
Reply | Threaded
Open this post in threaded view
|

Re: trouble with transform_attribute / attr_cast

Tobias Loew
Yes, I see the runtime error at exactly the same position.
I seems in http://boost.2283326.n4.nabble.com/transform-attribute-gt-segfault-td4657399.html#a4657403 you found a solution. Have you made a bugfix/pull-request?
Reply | Threaded
Open this post in threaded view
|

Re: trouble with transform_attribute / attr_cast

sehe
In reply to this post by Tobias Loew
(resend earlier failed mail)



On Mon, Aug 31, 2015, at 08:06 AM, Tobias Loew wrote:
> Yes, I see the runtime error at exactly the same position.
> I seems in
> http://boost.2283326.n4.nabble.com/transform-attribute-gt-segfault-td4657399.html#a4657403
> you found a solution. Have you made a bugfix/pull-request?
>
>
>From what I remember I found a workaround using deep_copy (or later
Spirit's copy function).

I have not made a pull request


--
Seth

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general