Question

I have condensed my problem to a (maybe not smallest) sample application below. The sample is a generic JSON parser. However, it exhibits two issues. 1. When none of the other options pass, it always outputs true or false when bool_ is an outputter in a variant list. If it isn't the last one, anything after it is effectively unused. I can't figure out why. 2. When the input is a string, the string handler is never fired from the variant. When used outside the variant, it fires.

The example code has string output simplified to just karma::string and still exhibits the error. When I take what is learned here and return to the real application, the string output will be a C style escaped string, so something that only works with karma::string will not help.

I have already read (and reread) Output of a boost::variant type using boost::spirit::karma and boost::spirit::karma output of string in quotation marks and either can't apply it properly to my case (ie I don't understand the answer after all) or else it doesn't work in the more complex example. And I am also familiar with the mini_xml example code.

Any suggestions for what I am doing wrong? And why what I am doing is wrong and the fix is right?

All help is really appreciated.

#include <boost/variant/recursive_variant.hpp>
#include <string>
#include <vector>

namespace lloyd
{
namespace json
{

struct null
{
    bool operator==(const null& cmp) {return true; }
};

struct element;

typedef boost::make_recursive_variant<null, bool, long, double, std::string, std::vector<element>, std::vector<boost::recursive_variant_> >::type value;

struct element
{
    std::string name;
    json::value value;
    inline element(const element& src): name(src.name), value(src.value) {}
    inline element(const std::string& name, const json::value& value): name(name), value(value) {}
    inline element() {}
};

typedef std::vector<element> object;

}
}

#include <boost/fusion/adapted.hpp>

BOOST_FUSION_ADAPT_STRUCT(
    lloyd::json::element,
    (std::string, name)
    (lloyd::json::value, value)
)

#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/karma_auto.hpp>

#include <boost/spirit/include/phoenix.hpp>

namespace lloyd { namespace json { namespace karma {

template <typename OutputIterator>
struct json_object_out_generator
    : boost::spirit::karma::grammar<OutputIterator, json::object(bool, unsigned, unsigned) >
{
    //  JSON Output Grammars
    boost::spirit::karma::rule<OutputIterator, std::vector<json::value>(bool, unsigned, unsigned) > array_rule;
    boost::spirit::karma::rule<OutputIterator, json::null(bool, unsigned, unsigned) > null_rule;
    boost::spirit::karma::rule<OutputIterator, json::value(bool, unsigned, unsigned) > value_rule;
    boost::spirit::karma::rule<OutputIterator, json::element(bool, unsigned, unsigned) > elem_rule;
    boost::spirit::karma::rule<OutputIterator, json::object(bool, unsigned, unsigned) > obj_rule;

    json_object_out_generator() : json_object_out_generator::base_type(obj_rule)
    {
        using boost::spirit::lit;
        using boost::spirit::_r1;
        using boost::spirit::_r2;
        using boost::spirit::_r3;

        namespace karma=boost::spirit::karma;

        null_rule %= karma::eps << boost::spirit::karma::lit("null");
        array_rule %= lit("[") << -(value_rule(_r1, _r2, _r3) % lit(",") ) << "]";
        value_rule %= ( null_rule(_r1, _r2, _r3) | karma::string | karma::long_ | karma::double_ | obj_rule(_r1, _r2, _r3) | array_rule(_r1, _r2, _r3) | karma::bool_);
        elem_rule %= boost::spirit::karma::string << ":" << -value_rule(_r1, _r2+1, _r3);
        obj_rule %= boost::spirit::lit("{")
            << -boost::spirit::buffer[( elem_rule(_r1, _r2+1, _r3)  % ',' ) ]
            << "}";

    }
};

}}}

#include <vector>
#include <sstream>
#include <iomanip>

#include <boost/assign/list_of.hpp>

#include <boost/assign/std/vector.hpp>
using namespace boost::assign;

int main(int argc, const char* argv[])
{
    using lloyd::json::value;
    using lloyd::json::element;
    using lloyd::json::null;
    lloyd::json::object obj;
    lloyd::json::object sobj;
    std::vector<value> t5;
    t5 += null(), true, false, value("Testing"), sobj;

    obj += element("T1", null()), element("T2", true), element("T3", false);
    obj += element("T4", "Testing 4"), element("T5", t5), element("T6", sobj);
    obj += element("NT0", (long)50), element("NT1", 50.5), element("NT2", 50.0);

    std::stringstream s;
    typedef boost::spirit::karma::ostream_iterator<char> out_itr;
    out_itr so(s);

    lloyd::json::karma::json_object_out_generator<out_itr> json_object_out;                 //  Our grammar definition
    boost::spirit::karma::generate(so, json_object_out(true, 0, 1), obj);
    std::cout << "Result:\n";
    std::cout << s.str() << std::endl;
    return 0;
}

Edited to change the title to cover the actual problem displayed. Edited to fix the upload miss on the code sample.

Was it helpful?

Solution

As someone's comments state, the missing are headers which I missed removing the references (they exist here) to, but I had removed the usage of.

However, the actual problem above is with C++ basic object conversion rules. In case soemone else runs into this:

c provides a direct conversion from a pointer type to a boolean. c++ adds a std::string class. That class provides a constructor from const char*.

Direct conversions are simpler than class constructors, thus that conversion is preferred when either can be used. Because it is simpler, it is also not considered ambiguous over which conversion to use. Thus, though char* to string is what was intended, the compiler did pointer to boolean, causing the output to be a boolean.

The information was given by VeXocide on the freenode ##spirit chat channel.

So, to force the desired conversion, if std::string("STRING HERE") was used instead of "STRING HERE", it would have worked. boost::spirit::karma had no bearing on the actual problem, as it was a gigo issue.

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