Search code examples
c++boostboost-spiritboost-spirit-karmaboost-optional

Boost Spirit Karma multiple optionals


I'm seeing an error that I'm not seeing the resolution to. First, the relevant code:

namespace C {

    struct RangeEntry {
        size_t byte;
        boost::optional<size_t> bit;
    };

    struct Range {
        RangeEntry firstPart;
        boost::optional<RangeEntry> secondPart;
        boost::optional<size_t> shift;
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    C::RangeEntry,
    (size_t, byte)
    (boost::optional<size_t>, bit)
)

BOOST_FUSION_ADAPT_STRUCT(
    C::Range,
    (C::RangeEntry , firstPart)
    (boost::optional<C::RangeEntry> , secondPart)
    (boost::optional<size_t> , shift)
)

... Declare the rules ...

karma::rule<Iterator, C::Range()> range;
karma::rule<Iterator, C::RangeEntry()> range_part;

... Define rules ...

range_part %= no_delimit[ulong_ << -(lit(":") << ulong_)];
range %= no_delimit[range_part << -(lit("-") << range_part)] << -(lit("<<") << ulong_);

At the range %= part, I get the compile error

/usr/include/boost/spirit/home/karma/numeric/detail/numeric_utils.hpp:504:30: error:
invalid operands to binary expression 
('C::RangeEntry' and 'C::RangeEntry')
    return floor(num / spirit::traits::pow10<T>(exp));
                 ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I'm guessing it's trying to match a RangeEntry to the ulong_ rule, but I can't figure out why? What am I missing?


Solution

  • The no_delimit directive is regrouping your exposed fusion sequences. Note that the following does compile:

        range %= range_part << -(lit("-") << range_part) << -(lit("<<") << ulong_);
    

    or even

        range %= no_delimit[range_part << -(lit("-") << range_part) << -(lit("<<") << ulong_)];
    

    AFAICT the rules are defined without delimiter, so the no_delimit should be redundant here anyway.


    I have "fantasized" a RangeEntry type just to make it a selfcontained sample:

    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/include/karma.hpp>
    
    namespace karma = boost::spirit::karma;
    
    namespace C {
        typedef std::pair<unsigned long, boost::optional<unsigned long> > RangeEntry;
    
        struct Range {
            RangeEntry firstPart;
            boost::optional<RangeEntry> secondPart;
            boost::optional<size_t> shift;
        };
    }
    
    BOOST_FUSION_ADAPT_STRUCT(
        C::Range,
        (C::RangeEntry , firstPart)
        (boost::optional<C::RangeEntry> , secondPart)
        (boost::optional<size_t> , shift)
        );
    
    //... Declare the rules ...
    
    int main()
    {
        typedef char* Iterator;
        karma::rule<Iterator, C::Range()> range;
        karma::rule<Iterator, C::RangeEntry()> range_part;
    
        //... Define rules ...
    
        using namespace karma;
        range_part %= no_delimit[ulong_ << -(lit(":") << ulong_)];
        range %= no_delimit[range_part << -(lit("-") << range_part) << -(lit("<<") << ulong_)];
    }