开发者

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 pr开发者_StackOverflowoblems.

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


This is a fundamental problem with the C++ type system in C++03. The value 0 is special and can be used in many places where it's type, int, cannot. This problem is easily demonstrated and causes plenty of problems where templates and pointers combine.

void f(int i) {
    void* ptr = ...;
    if (ptr == i) { // MALFORMED
    }
}

f(0); // But I'm trying to compare with 0, which is legit!

template<typename T, typename Y> T construct_from(const Y& y) {
    return T(y);
}
construct_from<void*>(0); // ERROR, cannot construct ptr from int.

The simplest solution is to write a quick version of nullptr, which can be found in C++0x.

struct nullptr_t {
    template<typename T> operator T*() const {
        return 0;
    }
};


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜