Search code examples
c++boost-spiritboost-spirit-karma

What debugging technique for a karma generator with runtime error


Not easy to sate what my programming question is as I don't really see where the problem is. Indeed, I have a runtime error lost somewhere in the boost spirit karma Library. I guess I miss a debugging technique here.

I have seen that the macro BOOST_SPIRIT_DEBUG_NODE(S) greatly helps for parsers, although I couldn't find any reference to it in the manual. For generators, it seems this is not working and I (honestly) don't have the courage (should I ?) to dig into the code of this library to understand where the issue is.

I have tried to generate the three types of my union-like structure alone in a grammar without any issue. So I assume the error comes from the cast of the U structure into a boost variant, again (see Casting attribute to boost::variant) but I have no proof.

For those who could solve it with a simple code inspection, here is the minimal example of my issue:

#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/karma.hpp>
#include <boost/variant/variant.hpp>

namespace ka = boost::spirit::karma;

typedef std::back_insert_iterator<std::string> iterator;
typedef enum {A, B, C } E;
typedef enum {FOO, BAR, POINTER } K;

struct U /* Union like */
{
    K kind;
    double foo;
    E bar;
    unsigned int * p;
};

class EName : public ka::symbols<E, std::string>
{
public:
    EName()
    {
        add (A,"A") (B,"B") (C,"C");
    }
};

typedef boost::variant<E, double, unsigned int *> UVariant;

namespace boost { namespace spirit { namespace traits {
    template<>
    struct transform_attribute<const U,UVariant,ka::domain>
    {
        typedef UVariant type;
        static type pre(const U & u) {
            switch (u.kind)
            {
            case FOO:
                return type(u.foo);
            case BAR:
                return type(u.bar);
            case POINTER:
                return type(u.p);
            }
            return type(A);
        }
    };
}}}

class grm: public ka::grammar<iterator, U()>
{
public:
    grm():grm::base_type(start)
    {
        start = ka::attr_cast<UVariant >(bar | foo | pointer);
        bar = b;
        foo = ka::double_;
        pointer = ka::hex;
    }
private:
    ka::rule<iterator,U()> start;
    ka::rule<iterator,double()> foo;
    ka::rule<iterator,E()> bar;
    ka::rule<iterator,unsigned int *()> pointer;
    EName b;
};

int main(int argc, char * argv[])
{
    grm g;
    U u;
    //unsigned int a;
    u.kind = BAR;
    //u.foo = 1.0;
    u.bar = B;
    //u.p = &a;

    std::string generated;
    std::back_insert_iterator<std::string> sink(generated);
    ka::generate(sink,g,u);
    std::cout << generated;

    return 0;
}

UPDATE: Compiler: Visual C++ Express version 11 and 12. The call stack stops at :

            // If you are seeing a compilation error here stating that the
            // third parameter can't be converted to a karma::reference
            // then you are probably trying to use a rule or a grammar with
            // an incompatible delimiter type.
            if (f(sink, context, delim)) // <--- call stack stops here (last boost spirit reference)

Also I have found out that the definition of the _SCL_SECURE_NO_WARNINGS macro shadowed the following compiler warning:

warning C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'

This warning referring to several boost-spirit files:

  • spirit\home\karma\detail\output_iterator.hpp(242)
    spirit\home\karma\detail\output_iterator.hpp(577) 
    spirit\home\karma\detail\output_iterator.hpp(574) 
    spirit\home\karma\detail\alternative_function.hpp(170)
    spirit\home\karma\detail\alternative_function.hpp(162) 
    spirit\home\karma\operator\alternative.hpp(122)
    spirit\home\karma\auxiliary\attr_cast.hpp(85)
    spirit\home\karma\nonterminal\detail\generator_binder.hpp(43)
    spirit\home\karma\nonterminal\detail\generator_binder.hpp(52)
    spirit\home\karma\nonterminal\rule.hpp(193)
    spirit\home\karma\nonterminal\rule.hpp(230)

Solution

  • I can't reproduce the error.

    I can't "solve it" from a little code inspection either. I can however do two things:

    • I can confirm that debug macros don't appear to be implemented for Karma

    • I can go out on a limb and say that perhaps bar|foo|pointer needs to be deep-copied:

      start = ka::attr_cast<UVariant >(boost::proto::deep_copy(bar | foo | pointer));
      

    I tried with UB-sanitizer and Address-sanitizer enabled, but they both didn't report any problems.


    UPDATE In fact it looks like I can "solve" it from a little code inspection (and lucky brainwave) after all:

    In fact, running under valgrind DOES show a problem and indeed, it goes away when adding the deep_copy.

    I'll add some references as to how I hypothesized these things: