Question

Say we have the following source code:

#include <iostream>
#include <string>
#include <iterator>
#include <boost/spirit/include/karma.hpp>

namespace karma = boost::spirit::karma;

template <typename OutputIterator> struct grammar : karma::grammar<OutputIterator, std::nullptr_t()> {
  grammar() : grammar::base_type(query) {
    query =  "yeah";
  }

  karma::rule<OutputIterator, std::nullptr_t()> query;
};


int main(void) {
  typedef std::back_insert_iterator<std::string> iterator_type;
  std::string generated;
  iterator_type output_it(generated);
  //keys_and_values<sink_type> g;
  grammar<iterator_type> g;
  bool result = karma::generate(output_it, g, nullptr);
  std::cout << result << ":" << generated << std::endl;
  return 0;
}

This fails to compile because karma lacks some traits for std::nullptr_t (those are boost::spirit::traits::extract_c_string and boost::spirit::traits::char traits). More specifically, it fails because karma is not able to find a generator for an attribute of type std::nullptr_t.

I see several ways to cope with that:

  1. Replace std::nullptr_t by karma::unused_type in the grammar definition : It works on this example but may introduce ambiguity in a more complex grammar.
  2. Defining the traits specialization : In my opinion, this is dirty and not generic. Plus, it exposes my specialization of a standard type for everyone, leading to potential conflicts.
  3. Specializing an attribute transform : Same problem of specializing a standard type just for me.
  4. Write a custom generator : The best candidate so far, but it makes a serious load of highly templated code lines compared to the task complexity.
  5. Put a intermediate rule with a karma::unused_type attribute. A quick fix that works but have no sense.

Question : How can I tell the karma::rule to generate a simple literal and not care about having or not a generator for its attribute ?

Was it helpful?

Solution

You seem to have stumbled on the inverse of the infamous single-element fusion sequence conundrum[1] :(

I noticed, because the error emanates from the code trying to verify that the input string matches the attribute (lit.hpp):

// fail if attribute isn't matched by immediate literal
typedef typename attribute<Context>::type attribute_type;

typedef typename spirit::result_of::extract_from<attribute_type, Attribute>::type
    extracted_string_type;

using spirit::traits::get_c_string;
if (!detail::string_compare(
        get_c_string(
            traits::extract_from<attribute_type>(attr, context))
      , get_c_string(str_), char_encoding(), Tag()))
{
    return false;
}

However, that makes no sense at all, since the docs state:

lit, like string, also emits a string of characters. The main difference is that lit does not consumes [sic] an attribute. A plain string like "hello" or a std::basic_string is equivalent to a lit

So I just... on a whim thought to coerce things a little, by using the same workaround that works for single-element fusion sequences on the Qi side:

query = karma::eps << "yeah";

And, voilà: it works: Live On Coliru


[1] See

Etc. This is a sad flaw that will probably need to be worked around for SpiritV2.

OTHER TIPS

Possible answer : After posting this, I found a solution which satisfies me for the moment. That is : introduce a intermediate rule.

template <typename OutputIterator> struct grammar : karma::grammar<OutputIterator, std::nullptr_t()> {
  grammar() : grammar::base_type(query) {
    query =  null_rule;
    null_rule = "null";
  }

  karma::rule<OutputIterator, std::nullptr_t()> query;
  karma::rule<OutputIterator, karma::unused_type()> null_rule;
};

I'm still interested in any comment, reproach or other solution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top