Search code examples
c++boostboost-spiritboost-phoenixnull-pointer

c++ functional programming ( boost::phoenix && boost::spirit) testing for null-ptrs in pointer placeholders


So, I have the following spirit karma rule body:

base_rule = 
    eps(_r1 != 0) [ // _r1 is a pointer_typed placeholder
        eps
    ]
;

which leads to a rather long error message from g++ which (helpfully) ends with :

/opt/dev_64_swat/Boost/include/boost/spirit/home/phoenix/operator/comparison.hpp
:37:5: error: ISO C++ forbids comparison between pointer and integer 
[-fpermissive]

This is valid c++:

struct zebra{};

int main()
{
  zebra * x;
  if( x == 0);  
}

I thought to try boost::phoenix::static_cast_<_r1_type *>(0) as well as converting _r1_type to a integer (yes that is WRONG, it was just an experiment).

The question:

How can I use a spirit eps construct to perform a pointer test on a placeholder to prevent rule body evaluation when the point is zero ?

As with all "C++ functional programming library usage" questions I expect the answer to leave me feeling like a dimwit.

The Answer

Ildjam's point directly answers my question. There were two issues with my problem; there is a indirect problem above. And that is to do with conditionals in PEG. What I am trying to express should be written as such:

rule = ( eps(_r) << ( /* grammar for when pointer is not null */ ) ) 
    | eps // otherwise dont do anything.
;

I was using the semantic action body ( specified in a [] block), to express the conditional part of the grammar. Strangely though I have written conditional PEG grammars before, I just made a mistake, which led to the second class of problems.

So, eps(_r1) does the trick, The second type of compilation issue is irrelevant to the question.


Solution

  • Using implicit pointer-to-bool conversion, as suggested in my comment, works for me out of the box with Boost 1.46.1. The following is a minimal repro wherein parse succeeds if (and only if) p != 0 && input == "not null" or p == 0 && input == "null":

    #include <string>
    #include <ios>
    #include <ostream>
    #include <iostream>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    namespace spirit = boost::spirit;
    namespace qi = spirit::qi;
    
    struct test_grammar : qi::grammar<std::string::const_iterator, void(int*)>
    {
        test_grammar() : base_type(start_)
        {
            start_
                =   (   spirit::eps(spirit::_r1)
                        >> "not null"
                    |   spirit::eps(!spirit::_r1)
                        >> "null"
                    )
                    >> spirit::eoi
                ;
        }
    
    private:
        qi::rule<base_type::iterator_type, base_type::sig_type> start_;
    };
    test_grammar const test;
    
    int main()
    {
        int i = 42;
        int* p = &i;                          // alternatively, = 0;
        std::string const input = "not null"; // alternatively, = "null";
    
        std::string::const_iterator first = input.begin();
        std::cout
            << std::boolalpha
            << "parse succeeded: "
            << qi::parse(first, input.end(), test(p))
            << std::endl;
    }
    

    So, whatever problem you're having while trying to use implicit conversion in this manner, it must be specific to your code; i.e., you'll have to show more of your code to get any useful feedback.