Boost logo

Boost Users :

Subject: Re: [Boost-users] [variant] declaring recursive variants
From: Jerry Jeremiah (jerry.jeremiah_at_[hidden])
Date: 2011-11-16 18:53:30


Steven Watanabe <watanabesj <at> gmail.com> writes:

>
> AMDG
>
> On 11/15/2011 06:10 PM, Jerry Jeremiah wrote:
> > Actually, I don't want a recursive variant in the normal recursive_wrapper
> > sense. What I want is this:
> >
> > typedef std::list<a> alist;
> > typedef std::list<b> blist;
> > typedef boost::variant<std::string
> > ,long double
> > ,alist::iterator
> > ,blist::iterator
> > ,config::iterator
> > > var;
> > typedef std::vector<var> value;
> > typedef std::map<std::string,value> message;
> > typedef std::map<std::string,message> config;
> >
> > But of course I can't do that because I have no idea how to forward
> > declare config. I suspect that there is no way to do this, but I am not
> > in the same league as everybody else when it comes to this stuff.
> >
> > Any suggestions?
> >
>
> Wrap it in a struct and forward declare
> the struct.
>
> In Christ,
> Steven Watanabe
>

Thanks for the advice. I actually tried that before asking and it didn't
work. But not for the obvious reason. I assumed that the addition to the
variant of the extra forward declared config_iter struct was the problem but
it isn't that simple.

This compiles:

// test program with config_iter and nothing else
// compiles ok
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <map>

struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

struct config_iter
{
    config_iter(const config::iterator iter) : iter_(iter) {}
    operator config::iterator() const { return iter_; }
    config::iterator operator() () const { return iter_; }
protected:
    config::iterator iter_;
};

int main()
{
    return 0;
}

And so does this:

// test program with config_iter and indentifier
// compiles ok if identifier::operator==() is commented out
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <map>

struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

struct config_iter
{
    config_iter(const config::iterator iter) : iter_(iter) {}
    operator config::iterator() const { return iter_; }
    config::iterator operator() () const { return iter_; }
protected:
    config::iterator iter_;
};

struct identifier
{
    identifier()
    : name_(""), version_({0})
    {
        // std::cout << "identifier::identifier()" << std::endl;
    }
    identifier(const std::string & name, const value & version)
    : name_(name), version_(version)
    {
        // std::cout << "identifier::identifier(" << name << "," << version
<< ")" << std::endl;
    }
// bool operator == (const identifier & other) const
// {
// return name_ == other.name_ && version_ == other.version_;
// }
private:
    // updated by constructor
    std::string name_;
    value version_;
};

int main()
{
    return 0;
}

But if you put the operator== into the completely unrelated identifier class
then even though identifier has nothing to do with the rest of the program it
doesn't compile:

// test program with config_iter and indentifier
// doesn't compile if identifier::operator==() is in the program
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <map>

struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

struct config_iter
{
    config_iter(const config::iterator iter) : iter_(iter) {}
    operator config::iterator() const { return iter_; }
    config::iterator operator() () const { return iter_; }
protected:
    config::iterator iter_;
};

struct identifier
{
    identifier()
    : name_(""), version_({0})
    {
        // std::cout << "identifier::identifier()" << std::endl;
    }
    identifier(const std::string & name, const value & version)
    : name_(name), version_(version)
    {
        // std::cout << "identifier::identifier(" << name << "," << version
<< ")" << std::endl;
    }
    bool operator == (const identifier & other) const
    {
        return name_ == other.name_ && version_ == other.version_;
    }
private:
    // updated by constructor
    std::string name_;
    value version_;
};

int main()
{
    return 0;
}

I don't understand why the addition of an unrelated type makes a difference?
The errors talk about config_iter having no comparison operator:

/include/boost/variant/variant.hpp: In member function 'bool
boost::detail::variant::equal_comp::operator()(const T&, const T&) const [with
T = config_iter]':
In file included from /include/boost/variant.hpp:17:0,
                 from main.cpp:1:
/include/boost/variant/variant.hpp:789:47: instantiated from 'bool
boost::detail::variant::comparer<Variant, Comp>::operator()(const T&) const
[with T = config_iter, Variant = boost::variant<std::basic_string<char>, long
double, config_iter>, Comp = boost::detail::variant::equal_comp]'
/include/boost/variant/variant.hpp:858:32: instantiated
from 'boost::detail::variant::invoke_visitor<Visitor>::result_type
boost::detail::variant::invoke_visitor<Visitor>::internal_visit(T&, int) [with
T = const config_iter, Visitor =
boost::detail::variant::comparer<boost::variant<std::basic_string<char>, long
double, config_iter>, boost::detail::variant::equal_comp>,
boost::detail::variant::invoke_visitor<Visitor>::result_type = bool]'
/include/boost/variant/detail/visitation_impl.hpp:130:9: instantiated
from 'typename Visitor::result_type
boost::detail::variant::visitation_impl_invoke_impl(int, Visitor&, VoidPtrCV,
T*, mpl_::true_) [with Visitor =
boost::detail::variant::invoke_visitor<boost::detail::variant::comparer<boost::
variant<std::basic_string<char>, long double, config_iter>,
boost::detail::variant::equal_comp> >, VoidPtrCV = const void*, T =
config_iter, typename Visitor::result_type = bool, mpl_::true_ =
mpl_::bool_<true>]'
/include/boost/variant/detail/visitation_impl.hpp:173:9: instantiated
from 'typename Visitor::result_type
boost::detail::variant::visitation_impl_invoke(int, Visitor&, VoidPtrCV, T*,
NoBackupFlag, int) [with Visitor =
boost::detail::variant::invoke_visitor<boost::detail::variant::comparer<boost::
variant<std::basic_string<char>, long double, config_iter>,
boost::detail::variant::equal_comp> >, VoidPtrCV = const void*, T =
config_iter, NoBackupFlag = boost::variant<std::basic_string<char>, long
double, config_iter>::has_fallback_type_, typename Visitor::result_type =
bool]'
/include/boost/variant/detail/visitation_impl.hpp:260:1: instantiated
from 'typename Visitor::result_type boost::detail::variant::visitation_impl
(int, int, Visitor&, VoidPtrCV, mpl_::false_, NoBackupFlag, Which*, step0*)
[with Which = mpl_::int_<0>, step0 =
boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_i
tem<mpl_::long_<3l>, std::basic_string<char>,
boost::mpl::l_item<mpl_::long_<2l>, long double,
boost::mpl::l_item<mpl_::long_<1l>, config_iter, boost::mpl::l_end> > > >,
boost::mpl::l_iter<boost::mpl::l_end> >, Visitor =
boost::detail::variant::invoke_visitor<boost::detail::variant::comparer<boost::
variant<std::basic_string<char>, long double, config_iter>,
boost::detail::variant::equal_comp> >, VoidPtrCV = const void*, NoBackupFlag =
boost::variant<std::basic_string<char>, long double,
config_iter>::has_fallback_type_, typename Visitor::result_type = bool,
mpl_::false_ = mpl_::bool_<false>]'
/include/boost/variant/variant.hpp:1802:13: [ skipping 3 instantiation
contexts ]
/include/boost/variant/variant.hpp:1745:41: instantiated from 'bool
boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
T14, T15, T16, T17, T18, T19>::operator==(const boost::variant<T0, T1, T2, T3,
T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>&)
const [with T0_ = std::basic_string<char>, T1 = long double, T2 = config_iter,
T3 = boost::detail::variant::void_, T4 = boost::detail::variant::void_, T5 =
boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 =
boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 =
boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 =
boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 =
boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 =
boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 =
boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 =
boost::detail::variant::void_, boost::variant<T0, T1, T2, T3, T4, T5, T6, T7,
T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19> =
boost::variant<std::basic_string<char>, long double, config_iter>]'
make[2]: Leaving directory `D:/Dynamo'
/include/c++/4.6.1/bits/stl_algobase.h:791:6: instantiated from 'static bool
std::__equal<_BoolType>::equal(_II1, _II1, _II2) [with _II1 = const
boost::variant<std::basic_string<char>, long double, config_iter>*, _II2 =
const boost::variant<std::basic_string<char>, long double, config_iter>*, bool
_BoolType = false]'
make[1]: Leaving directory `D:/Dynamo'
/include/c++/4.6.1/bits/stl_algobase.h:820:71: instantiated from 'bool
std::__equal_aux(_II1, _II1, _II2) [with _II1 = const
boost::variant<std::basic_string<char>, long double, config_iter>*, _II2 =
const boost::variant<std::basic_string<char>, long double, config_iter>*]'
/include/c++/4.6.1/bits/stl_algobase.h:1022:37: instantiated from 'bool
std::equal(_II1, _II1, _II2) [with _II1 = __gnu_cxx::__normal_iterator<const
boost::variant<std::basic_string<char>, long double, config_iter>*,
std::vector<boost::variant<std::basic_string<char>, long double, config_iter>
> >, _II2 = __gnu_cxx::__normal_iterator<const
boost::variant<std::basic_string<char>, long double, config_iter>*,
std::vector<boost::variant<std::basic_string<char>, long double, config_iter>
> >]'
/include/c++/4.6.1/bits/stl_vector.h:1275:58: instantiated from 'bool
std::operator==(const std::vector<_Tp, _Alloc>&, const std::vector<_Tp,
_Alloc>&) [with _Tp = boost::variant<std::basic_string<char>, long double,
config_iter>, _Alloc = std::allocator<boost::variant<std::basic_string<char>,
long double, config_iter> >]'
main.cpp:41:58: instantiated from here
/include/boost/variant/variant.hpp:807:23: error: no match for 'operator=='
in 'lhs == rhs'
/include/boost/variant/variant.hpp:807:23: note: candidate is:
/include/boost/blank.hpp:58:13: note: bool boost::operator==(const
boost::blank&, const boost::blank&)
/include/boost/blank.hpp:58:13: note: no known conversion for argument 1
from 'const config_iter' to 'const boost::blank&'

So I thought I would get clever and add an operator== to config_iter and that
seems to compile:

// test program with config_iter and indentifier
// compiles ok if both have an operator==
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <map>

struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

struct config_iter
{
    config_iter(const config::iterator iter) : iter_(iter) {}
    operator config::iterator() const { return iter_; }
    config::iterator operator() () const { return iter_; }
    bool operator == (const config_iter & other) const
    {
        return iter_ == other.iter_;
    }
protected:
    config::iterator iter_;
};

struct identifier
{
    identifier()
    : name_(""), version_({0})
    {
        // std::cout << "identifier::identifier()" << std::endl;
    }
    identifier(const std::string & name, const value & version)
    : name_(name), version_(version)
    {
        // std::cout << "identifier::identifier(" << name << "," << version
<< ")" << std::endl;
    }
    bool operator == (const identifier & other) const
    {
        return name_ == other.name_ && version_ == other.version_;
    }
private:
    // updated by constructor
    std::string name_;
    value version_;
};

int main()
{
    return 0;
}

And then I added another completely unrelated class called message_iter:

// test program with message_iter, config_iter and indentifier
// all have an operator==
// compiles ok if message_iter is below config_iter
// doesn't compile if message_iter is above config_iter
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <map>

struct message_iter;
struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,message_iter
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

// compiles ok if message_iter is below config_iter
struct message_iter
{
    message_iter(const message::iterator iter) : iter_(iter) {}
    operator message::iterator() const { return iter_; }
    message::iterator operator() () const { return iter_; }
    bool operator == (const message_iter & other) const
    {
        return iter_ == other.iter_;
    }
protected:
    message::iterator iter_;
};

struct config_iter
{
    config_iter(const config::iterator iter) : iter_(iter) {}
    operator config::iterator() const { return iter_; }
    config::iterator operator() () const { return iter_; }
    bool operator == (const config_iter & other) const
    {
        return iter_ == other.iter_;
    }
protected:
    config::iterator iter_;
};

struct identifier
{
    identifier()
    : name_(""), version_({0})
    {
        // std::cout << "identifier::identifier()" << std::endl;
    }
    identifier(const std::string & name, const value & version)
    : name_(name), version_(version)
    {
        // std::cout << "identifier::identifier(" << name << "," << version
<< ")" << std::endl;
    }
    bool operator == (const identifier & other) const
    {
        return name_ == other.name_ && version_ == other.version_;
    }
private:
    // updated by constructor
    std::string name_;
    value version_;
};

int main()
{
    return 0;
}

If you add it below config_iter everything compiles. If you add it above
config_iter then it doesn't compile. Why does the positiion of a completely
unrealted calss change anything? It complains about config_iter not being a
complete type.

/include/boost/mpl/sizeof.hpp: In instantiation
of 'boost::mpl::sizeof_<config_iter>':
In file included from /include/boost/variant/variant.hpp:81:0,
                 from /include/boost/variant.hpp:17,
                 from main.cpp:1:
/include/boost/mpl/aux_/has_type.hpp:20:1: instantiated
from 'boost::mpl::aux::has_type<boost::mpl::sizeof_<config_iter>,
mpl_::bool_<true> >'
/include/boost/mpl/aux_/preprocessed/gcc/quote.hpp:32:36: instantiated
from 'boost::mpl::quote1<boost::mpl::sizeof_, mpl_::void_>::apply<config_iter>'
/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:36:8: instantiated
from 'boost::mpl::apply_wrap1<boost::mpl::quote1<boost::mpl::sizeof_,
mpl_::void_>, config_iter>'
/include/boost/mpl/aux_/preprocessed/gcc/bind.hpp:144:21: instantiated
from 'boost::mpl::bind1<boost::mpl::quote1<boost::mpl::sizeof_, mpl_::void_>,
mpl_::arg<1> >::apply<config_iter>'
/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:36:8: [ skipping 8
instantiation contexts ]
/include/boost/mpl/reverse_fold.hpp:41:18: instantiated
from 'boost::mpl::reverse_fold<boost::mpl::l_item<mpl_::long_<3l>,
std::basic_string<char>, boost::mpl::l_item<mpl_::long_<2l>, long double,
boost::mpl::l_item<mpl_::long_<1l>, config_iter, boost::mpl::l_end> > >,
boost::mpl::l_end, boost::mpl::bind2<boost::mpl::push_front<mpl_::na,
mpl_::na>, mpl_::arg<1>,
boost::mpl::bind1<boost::mpl::protect<boost::mpl::bind1<boost::mpl::quote1<boos
t::mpl::sizeof_, mpl_::void_>, mpl_::arg<1> >, 0>, mpl_::arg<2> > >,
mpl_::arg<1> >'
/include/boost/mpl/transform.hpp:56:8: instantiated
from 'boost::mpl::aux::reverse_transform1_impl<boost::mpl::l_item<mpl_::long_<3
l>, std::basic_string<char>, boost::mpl::l_item<mpl_::long_<2l>, long double,
boost::mpl::l_item<mpl_::long_<1l>, config_iter, boost::mpl::l_end> > >,
boost::mpl::sizeof_<mpl_::arg<1> >,
boost::mpl::front_inserter<boost::mpl::l_end> >'
/include/boost/mpl/transform.hpp:113:1: instantiated
from 'boost::mpl::transform1<boost::mpl::l_item<mpl_::long_<3l>,
std::basic_string<char>, boost::mpl::l_item<mpl_::long_<2l>, long double,
boost::mpl::l_item<mpl_::long_<1l>, config_iter, boost::mpl::l_end> > >,
boost::mpl::sizeof_<mpl_::arg<1> >, mpl_::na>'
/include/boost/variant/variant.hpp:123:57: instantiated
from 'boost::detail::variant::max_value<boost::mpl::l_item<mpl_::long_<3l>,
std::basic_string<char>, boost::mpl::l_item<mpl_::long_<2l>, long double,
boost::mpl::l_item<mpl_::long_<1l>, config_iter, boost::mpl::l_end> > >,
boost::mpl::sizeof_<mpl_::arg<1> > >'
/include/boost/variant/variant.hpp:248:17: instantiated
from 'boost::detail::variant::make_storage<boost::mpl::l_item<mpl_::long_<3l>,
std::basic_string<char>, boost::mpl::l_item<mpl_::long_<2l>, long double,
boost::mpl::l_item<mpl_::long_<1l>, config_iter, boost::mpl::l_end> > >,
boost::variant<std::basic_string<char>, long double,
config_iter>::has_fallback_type_>'
/include/boost/variant/variant.hpp:1131:17: instantiated
from 'boost::variant<std::basic_string<char>, long double, config_iter>'
main.cpp:25:31: instantiated from here
/include/boost/mpl/sizeof.hpp:26:8: error: invalid application of 'sizeof' to
incomplete type 'config_iter'

The goal, of course, is to include both in the variant:

struct message_iter;
struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,message_iter
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

But it won't compile because each of message_iter and config_iter need to be
above the other so it doesn't compile. If I remove the ALL operator== it
compiles but of course identifier needs its operator==. So what can I do?

Is there any way to get this (below) to compile with the identifier::operator==
() in the program?

// test program with message_iter, config_iter and indentifier
// compiles ok if NONE have an operator==
// doesn't compile if ANY have an operator==
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <map>

struct message_iter;
struct config_iter;

typedef boost::variant<std::string
                      ,long double
                      ,message_iter
                      ,config_iter
> var;
typedef std::vector<var> value;
typedef std::map<std::string,value> message;
typedef std::map<std::string,message> config;

struct config_iter
{
    config_iter(const config::iterator iter) : iter_(iter) {}
    operator config::iterator() const { return iter_; }
    config::iterator operator() () const { return iter_; }
// bool operator == (const config_iter & other) const
// {
// return iter_ == other.iter_;
// }
protected:
    config::iterator iter_;
};

struct message_iter
{
    message_iter(const message::iterator iter) : iter_(iter) {}
    operator message::iterator() const { return iter_; }
    message::iterator operator() () const { return iter_; }
// bool operator == (const message_iter & other) const
// {
// return iter_ == other.iter_;
// }
protected:
    message::iterator iter_;
};

struct identifier
{
    identifier()
    : name_(""), version_({0})
    {
        // std::cout << "identifier::identifier()" << std::endl;
    }
    identifier(const std::string & name, const value & version)
    : name_(name), version_(version)
    {
        // std::cout << "identifier::identifier(" << name << "," << version
<< ")" << std::endl;
    }
// bool operator == (const identifier & other) const
// {
// return name_ == other.name_ && version_ == other.version_;
// }
private:
    // updated by constructor
    std::string name_;
    value version_;
};

int main()
{
    return 0;
}

I'm sorry I was off on the wrong track with my original question. Do you have
any suggestions for getthing that last program to compile with
identifier::operator==() in the program?

Tahnks for the advice,

Jerry


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net