Optionals in variants

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Optionals in variants

Yuri Khan
Hello All,

I am having trouble with alternatives.

For the sake of exposition, suppose I want to parse a value which can
be of one of two types, e.g. int or double. The value is tagged by its
type prefix, e.g. “int 42” or “double 3.14”. This works:

```
#include <iostream>
#include <string>
#include <boost/variant.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

typedef std::string::const_iterator Iterator;
typedef boost::variant<int, double> Number;

struct NumberGrammar : qi::grammar<Iterator, Number()>
{
    NumberGrammar() : NumberGrammar::base_type(number)
    {
        tagged_int %= qi::lit("int ") >> qi::int_;
        tagged_double %= qi::lit("double ") >> qi::double_;
        number %= tagged_int | tagged_double;
    }
    qi::rule<Iterator, int()> tagged_int;
    qi::rule<Iterator, double()> tagged_double;
    qi::rule<Iterator, Number()> number;
};

void test(const std::string& s, Number expected)
{
    Iterator it = s.begin();
    Number result;
    bool success = qi::parse(it, s.end(), NumberGrammar(), result);
    if (!success)
    {
        std::cout << "no match\n";
        return;
    }
    if (it != s.end())
        std::cout << "unconsumed input: "
            << std::string(it, s.end()) << "\n";
    if (expected != result)
        std::cout << "test failed: expected " << expected << ", "
            << "got " << result << "\n";
    else
        std::cout << "test passed: " << result << "\n";
}

int main()
{
    test("int 42", 42);
    test("double 3.14", 3.14);
}
```

Now let’s say we also want an indeterminate value in one of the
branches, so “int none” is also a valid input.

My first reaction is to change the int case of the variant to an
optional<int>. The Spirit documentation seems to support me:

> a: Unused, b: B --> (a | b): optional<B>
> a: A, b: B --> (a | b): variant<A, B>

(I notice the “a: A, b: B, c: Unused --> (a | b | c):
optional<variant<A, B> >” clause. This might be a problem for a more
advanced case, but I convince myself that it won’t kick in in my case
for associativity reasons.)

```
[…]
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
[…]
typedef boost::variant<boost::optional<int>, double> Number;
[…]
        tagged_integer %= qi::lit("int ") >>
            (qi::lit("none") | qi::int_);
[…]
    qi::rule<Iterator, boost::optional<int>()> tagged_integer;
[…]
    test("int none", boost::optional<int>());
    test("int 42", boost::optional<int>(42));
[…]
```

And now it does not work:

> a.out: /usr/include/boost/optional/optional.hpp:630:
> boost::optional<T>::reference_const_type boost::optional<T>::get() const
> [with T = int; boost::optional<T>::reference_const_type = const int&]:
> Assertion `this->is_initialized()' failed.
> Aborted (core dumped)

What happens is that the tagged_integer rule parses “int none” to the
attribute value boost::optional<int>(), as expected. The number rule
then attempts to convert it to the Number attribute type. The following
overload is selected:

```
    template <typename Attribute, typename T>
    struct assign_to_attribute_from_value<Attribute, boost::optional<T>
      , typename disable_if<is_same<Attribute, boost::optional<T> > >::type>
    {
        static void
        call(boost::optional<T> const& val, Attribute& attr)
        {
            assign_to(val.get(), attr);
        }
    };
```

and val.get() aborts because the optional being assigned from is empty.


Further, if I comment out the “int none” test, the “int 42” test then
fails because val.get() extracts the int value from the optional and
assigns it to the variant, which opts to put it into its double case.


Should there be an overload of assign_to_attribute_from_value where the
first argument is a variant and the second argument is an exact match
for one of the variant’s cases, that will avoid deconstructing the
optional?

(I am using Boost 1.54 as packaged in Ubuntu 14.04 but, at first
glance, I see nothing in github:boostorg/spirit:develop that would fix
my use case.)

--
senior developer, 2GIS Unix Team

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general
Reply | Threaded
Open this post in threaded view
|

Re: Optionals in variants

Joel de Guzman
On 11/4/15 8:41 PM, Yuri Khan wrote:

> Hello All,
>
> I am having trouble with alternatives.
>
> For the sake of exposition, suppose I want to parse a value which can
> be of one of two types, e.g. int or double. The value is tagged by its
> type prefix, e.g. “int 42” or “double 3.14”. This works:
>
> ```
> #include <iostream>
> #include <string>
> #include <boost/variant.hpp>
> #include <boost/spirit/include/qi.hpp>
>
> namespace qi = boost::spirit::qi;
>
> typedef std::string::const_iterator Iterator;
> typedef boost::variant<int, double> Number;
>
> struct NumberGrammar : qi::grammar<Iterator, Number()>
> {
>      NumberGrammar() : NumberGrammar::base_type(number)
>      {
>          tagged_int %= qi::lit("int ") >> qi::int_;
>          tagged_double %= qi::lit("double ") >> qi::double_;
>          number %= tagged_int | tagged_double;
>      }
>      qi::rule<Iterator, int()> tagged_int;
>      qi::rule<Iterator, double()> tagged_double;
>      qi::rule<Iterator, Number()> number;
> };
>
> void test(const std::string& s, Number expected)
> {
>      Iterator it = s.begin();
>      Number result;
>      bool success = qi::parse(it, s.end(), NumberGrammar(), result);
>      if (!success)
>      {
>          std::cout << "no match\n";
>          return;
>      }
>      if (it != s.end())
>          std::cout << "unconsumed input: "
>              << std::string(it, s.end()) << "\n";
>      if (expected != result)
>          std::cout << "test failed: expected " << expected << ", "
>              << "got " << result << "\n";
>      else
>          std::cout << "test passed: " << result << "\n";
> }
>
> int main()
> {
>      test("int 42", 42);
>      test("double 3.14", 3.14);
> }
> ```
>
> Now let’s say we also want an indeterminate value in one of the
> branches, so “int none” is also a valid input.
>
> My first reaction is to change the int case of the variant to an
> optional<int>. The Spirit documentation seems to support me:
>
>> a: Unused, b: B --> (a | b): optional<B>
>> a: A, b: B --> (a | b): variant<A, B>
>
> (I notice the “a: A, b: B, c: Unused --> (a | b | c):
> optional<variant<A, B> >” clause. This might be a problem for a more
> advanced case, but I convince myself that it won’t kick in in my case
> for associativity reasons.)
>
> ```
> […]
> #include <boost/optional.hpp>
> #include <boost/optional/optional_io.hpp>
> […]
> typedef boost::variant<boost::optional<int>, double> Number;
> […]
>          tagged_integer %= qi::lit("int ") >>
>              (qi::lit("none") | qi::int_);
> […]
>      qi::rule<Iterator, boost::optional<int>()> tagged_integer;
> […]
>      test("int none", boost::optional<int>());
>      test("int 42", boost::optional<int>(42));
> […]
> ```
>

Have you tried something like:

   struct none {};
   typedef boost::variant<none, int, double> Number;

Then:

   tagged_integer %= qi::lit("int ")
     >> (qi::lit("none") >> attr(none()) | qi::int_);

Regards,
--
Joel de Guzman
http://www.ciere.com
http://boost-spirit.com
http://www.cycfi.com/


------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general
Reply | Threaded
Open this post in threaded view
|

Re: Optionals in variants

Yuri Khan
On 2015-11-11T03:51+0800 Joel de Guzman <[hidden email]> wrote:

> On 11/4/15 8:41 PM, Yuri Khan wrote:
> > I am having trouble with alternatives.
> >
> > For the sake of exposition, suppose I want to parse a value which
> > can be of one of two types, e.g. int or double. The value is tagged
> > by its type prefix, e.g. “int 42” or “double 3.14”.
> >
> > Now let’s say we also want an indeterminate value in one of the
> > branches, so “int none” is also a valid input.
> >
> > My first reaction is to change the int case of the variant to an
> > optional<int>.
>
> Have you tried something like:
>
>    struct none {};
>    typedef boost::variant<none, int, double> Number;
>
> Then:
>
>    tagged_integer %= qi::lit("int ")
>      >> (qi::lit("none") >> attr(none()) | qi::int_);

Sorry, I minimized the example too much. In the actual use case, we want
to preserve the type information and be able to store an indeterminate
value for *each* alternative type. So we want to distinguish between
“int none” and “double none”. A variant you propose will not allow this.

We also tried

variant<variant<none<int>, int>,
        variant<none<double>, double> >

and it has different problems.

--
senior developer, 2GIS Unix Team

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general
Reply | Threaded
Open this post in threaded view
|

Re: Optionals in variants

Michael Powell-2


On November 11, 2015 12:41:07 AM EST, Yuri Khan <[hidden email]> wrote:

>On 2015-11-11T03:51+0800 Joel de Guzman <[hidden email]> wrote:
>
>> On 11/4/15 8:41 PM, Yuri Khan wrote:
>> > I am having trouble with alternatives.
>> >
>> > For the sake of exposition, suppose I want to parse a value which
>> > can be of one of two types, e.g. int or double. The value is tagged
>> > by its type prefix, e.g. “int 42” or “double 3.14”.
>> >
>> > Now let’s say we also want an indeterminate value in one of the
>> > branches, so “int none” is also a valid input.
>> >
>> > My first reaction is to change the int case of the variant to an
>> > optional<int>.
>>
>> Have you tried something like:
>>
>>    struct none {};
>>    typedef boost::variant<none, int, double> Number;
>>
>> Then:
>>
>>    tagged_integer %= qi::lit("int ")
>>      >> (qi::lit("none") >> attr(none()) | qi::int_);
>
>Sorry, I minimized the example too much. In the actual use case, we
>want
>to preserve the type information and be able to store an indeterminate
>value for *each* alternative type. So we want to distinguish between
>“int none” and “double none”. A variant you propose will not allow
>this.
>
>We also tried
>
>variant<variant<none<int>, int>,
>        variant<none<double>, double> >
>
>and it has different problems.

This wouldn't work, along similar lines as previous suggestions?

variant<none<int>, none<double>, int, double>

>--
>senior developer, 2GIS Unix Team
>
>------------------------------------------------------------------------------
>_______________________________________________
>Spirit-general mailing list
>[hidden email]
>https://lists.sourceforge.net/lists/listinfo/spirit-general

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general
Reply | Threaded
Open this post in threaded view
|

Re: Optionals in variants

Yuri Khan
On 2015-11-11T06:15-0500 Michael <[hidden email]> wrote:

> >Sorry, I minimized the example too much. In the actual use case, we
> >want
> >to preserve the type information and be able to store an
> >indeterminate value for *each* alternative type. So we want to
> >distinguish between “int none” and “double none”. A variant you
> >propose will not allow this.
> >
> >We also tried
> >
> >variant<variant<none<int>, int>,
> >        variant<none<double>, double> >
> >
> >and it has different problems.
>
> This wouldn't work, along similar lines as previous suggestions?
>
> variant<none<int>, none<double>, int, double>

Might, though flattening the hierarchy is very inelegant from the data
model point of view. (In the real use case, there are 5 different
types, each of which can be “none”.)

Am I to assume that variants containing variants or optionals are not
supposed to work naturally with Spirit?

At this point, we’re considering using a custom optional-like class for
each alternative.

--
senior developer, 2GIS Unix Team

------------------------------------------------------------------------------
_______________________________________________
Spirit-general mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/spirit-general