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.
精彩评论