Topic: Why is there no std::variant in C++0x?
Author: Larry Evans <cppljevans@gmail.com>
Date: Sat, 21 Aug 2010 22:02:43 CST Raw View
On Aug 20, 11:31 am, "Thomas J. Gritzan" <phygon_antis...@gmx.de>
wrote:
> Am 17.08.2010 19:45, schrieb Mathias Gaunard:
>
> > On Aug 15, 10:38 pm, Florian Goujeon <florian.gouj...@42ndart.org>
> > wrote:
> >> Hello,
>
> >> Thanks to the variadic templates, the presence of the tuples in the
> >> STL has become obvious.
>
> >> But what about the variants? I recently had to write an implementation
> >> of boost::variant using variadic templates. I could make it without
> >> any difficulty.
>
> > Exception-safety of operator= is a difficult issue with a stack-based
> > variant.
> > How did you solve it?
>
> Since it will be C++0x, I would require all types in variant to have a
> no-throw move constructor. Then it is just a straightforward copy&swap,
> or a copy-destroy-move.
>
Assume:
lhs = rhs;
where lhs and rhs are both std::variant<T1,T2,...Tn>, but
lhs.which() != rhs.which().
Now, to be more explicit, do you mean by copy&swap that you'd
copy rhs to temp then swap temp to lhs?
Likewise, could be more explicit about what 'copy-destroy-move'
means?
TIA.
-Larry
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Florian Goujeon <florian.goujeon@42ndart.org>
Date: Sat, 21 Aug 2010 22:03:52 CST Raw View
Did you try to force the may-throw copy to occur *before* the
erasing of the LHS variant's content?
I wrote that code in my implementation:
===========================================================
template<typename T, typename... Ts>
const variant<T, Ts...>&
variant<T, Ts...>::operator=(const variant<T, Ts...>& o)
{
if(!head_) //head_ is a boost::optional<T>
{
head_ = o.head_;
tail_ = o.tail_;
}
else
{
tail_ = o.tail_;
head_ = o.head_;
}
return *this;
}
===========================================================
I did some tests and it seems to work.
But it seems too simple. I probably missed something.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SG <s.gesemann@gmail.com>
Date: Sun, 22 Aug 2010 02:39:14 CST Raw View
On 22 Aug., 06:01, Mathias Gaunard wrote:
> On Aug 20, 5:31 pm, "Thomas J. Gritzan" <phygon_antis...@gmx.de>
> wrote:
>
> > Since it will be C++0x, I would require all types in variant to have a
> > no-throw move constructor.
>
> A pretty hard requirement, that you cannot even detect at compile-time
> (or can you?)
Not reliably. If I remember correctly, the noexcept operator and the
type traits are allowed to be pessimistic in this regard, that is,
noexcept(expression) or nothrow_xxx<T>::value are allowed to be false
even if the right answer would be true. So, it's a matter of QoI.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Thomas J. Gritzan" <phygon_antispam@gmx.de>
Date: Sun, 22 Aug 2010 14:05:55 CST Raw View
Am 22.08.2010 06:03, schrieb Florian Goujeon:
> Did you try to force the may-throw copy to occur *before* the
> erasing of the LHS variant's content?
>
> I wrote that code in my implementation:
> ===========================================================
> template<typename T, typename... Ts>
> const variant<T, Ts...>&
> variant<T, Ts...>::operator=(const variant<T, Ts...>& o)
> {
> if(!head_) //head_ is a boost::optional<T>
> {
> head_ = o.head_;
> tail_ = o.tail_;
> }
> else
> {
> tail_ = o.tail_;
> head_ = o.head_;
> }
>
> return *this;
> }
> ===========================================================
> I did some tests and it seems to work.
>
> But it seems too simple. I probably missed something.
Yes. Exception safety. What happens if head_ = o.head_ throws an
exception? The variant will be in an inconsistent state.
Read about the problem here:
http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html#variant.design.never-empty
--
Thomas
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Thomas J. Gritzan" <phygon_antispam@gmx.de>
Date: Sun, 22 Aug 2010 14:04:41 CST Raw View
Am 22.08.2010 06:02, schrieb Larry Evans:
> On Aug 20, 11:31 am, "Thomas J. Gritzan" <phygon_antis...@gmx.de>
> wrote:
>> Am 17.08.2010 19:45, schrieb Mathias Gaunard:
>>
>>> On Aug 15, 10:38 pm, Florian Goujeon <florian.gouj...@42ndart.org>
>>> wrote:
>>>> Hello,
>>
>>>> Thanks to the variadic templates, the presence of the tuples in the
>>>> STL has become obvious.
>>
>>>> But what about the variants? I recently had to write an implementation
>>>> of boost::variant using variadic templates. I could make it without
>>>> any difficulty.
>>
>>> Exception-safety of operator= is a difficult issue with a stack-based
>>> variant.
>>> How did you solve it?
>>
>> Since it will be C++0x, I would require all types in variant to have a
>> no-throw move constructor. Then it is just a straightforward copy&swap,
>> or a copy-destroy-move.
>>
> Assume:
>
> lhs = rhs;
>
> where lhs and rhs are both std::variant<T1,T2,...Tn>, but
> lhs.which() != rhs.which().
>
> Now, to be more explicit, do you mean by copy&swap that you'd
> copy rhs to temp then swap temp to lhs?
Yes. If the copy fails, both lhs and rhs are unchanged. If the copy
succeeds, there's no problem, since swap is no-throw (using no.throw move).
By swap I don't think of std::swap, but exchanging the complete content
of the variant (including the type indicator).
> Likewise, could be more explicit about what 'copy-destroy-move'
> means?
Looking at the boost docs:
http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html#variant.design.never-empty.heap-backup-solution
""
1. Copy-construct the content of the left-hand side to the heap; [...]
2. Destroy the content of the left-hand side.
3. Copy-construct the content of the right-hand side in the
(now-empty) storage of the left-hand side.
4. In the event of failure, copy backup to the left-hand side storage.
5. In the event of success, deallocate the data pointed to by backup.
""
The heap backup is done because the copy operation to restore the backup
could also fail. In this case, the variant just uses the heap backup as
content.
With a non-throwing move, you could do instead:
1. Copy-construct rhs to a temporary.
2. Destroy the content of lhs.
3. Move-construct the temporary to lhs.
You don't need a (slow) heap allocation, because there is only one copy
that could fail.
--
Thomas
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Florian Goujeon <florian.goujeon@42ndart.org>
Date: Sun, 22 Aug 2010 22:26:57 CST Raw View
> Read about the problem here:http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html#var...
I did.
I wrote that piece of code to fulfill that guarantee.
I also wrote an exception safety test for my implementation.
Here it is: http://pastebin.com/qp9e0wJF
Here is the code of my implementation:
http://github.com/fgoujeon/scalpel/blob/master/src/scalpel/utility/variant.hpp
The test is successful. My variant is heap-based, though.
Is my test incomplete?
> Yes. Exception safety. What happens if head_ = o.head_ throws an
> exception? The variant will be in an inconsistent state.
Whatever which "head_ = o.head_" throws, it will happen before the
erasure of the LHS variant's content, won't it?
If so, it will be effectless.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Thomas J. Gritzan" <phygon_antispam@gmx.de>
Date: Mon, 23 Aug 2010 02:19:49 CST Raw View
Am 23.08.2010 06:26, schrieb Florian Goujeon:
>> Read about the problem here:http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html#var...
>
> I did.
> I wrote that piece of code to fulfill that guarantee.
>
> I also wrote an exception safety test for my implementation.
> Here it is: http://pastebin.com/qp9e0wJF
>
> Here is the code of my implementation:
> http://github.com/fgoujeon/scalpel/blob/master/src/scalpel/utility/variant.hpp
>
> The test is successful. My variant is heap-based, though.
You didn't say that. Your example used a boost::optional.
With a heap based implementation, you can first try a copy, then simply
change the pointers. Manipulating pointers never throws.
With a stack based implementation you can't do that. boost::variant
tries to embed the value in the variant struct and also to minimize the
memory footprint by sharing the buffer space (like a real union).
It gets more complicated if you do this.
>> Yes. Exception safety. What happens if head_ = o.head_ throws an
>> exception? The variant will be in an inconsistent state.
>
> Whatever which "head_ = o.head_" throws, it will happen before the
> erasure of the LHS variant's content, won't it?
In a heap based implementation: Yes.
> If so, it will be effectless.
But in this code:
{ tail_ = o.tail_;
head_ = o.head_; }
...tail_ is changed already when you try to copy head_. If that copy
throws, the variant is in an inconsistent state.
--
Thomas
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Florian Goujeon <florian.goujeon@42ndart.org>
Date: Wed, 25 Aug 2010 11:44:51 CST Raw View
> You didn't say that. Your example used a boost::optional.
> With a heap based implementation, you can first try a copy, then simply
> change the pointers. Manipulating pointers never throws.
Indeed, I didn't say that, sorry. I thought boost::optional was a
heap-based container, so when I changed my implementation to make it
use an std::unique_ptr instead, I thought it didn't change the way
the memory is allocated.
> boost::variant
> tries to embed the value in the variant struct and also to minimize the
> memory footprint by sharing the buffer space (like a real union).
Yes, I realized this during our conversation. An hypothetical
std::variant would for sure have to be implemented that way as well.
> > If so, it will be effectless.
>
> But in this code:
> { tail_ = o.tail_;
> head_ = o.head_; }
>
> ...tail_ is changed already when you try to copy head_. If that copy
> throws, the variant is in an inconsistent state.
I insist, it won't. At least, not with an heap-based variant:
============================================================
if(!head_)
{
if(o.head_)
head_ = std::unique_ptr<T>(new T(*o.head_)); //1
tail_ = o.tail_;
}
else
{
tail_ = o.tail_;
//erase the lhs variant's content
if(o.head_)
head_ = std::unique_ptr<T>(new T(*o.head_)); //2
else
head_.reset(); //3
}
============================================================
In any case, the first executed statement is 1 (if LHS's and RHS's
types are different) or 2 (if their types are the same).
I just hoped this method could have been applied to a stack-based
variant (even if I didn't try to). But according to what you assert,
it isn't possible. Too bad.
Anyway, that answers to my initial question.
Thank you everyone!
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Larry Evans <cppljevans@gmail.com>
Date: Wed, 25 Aug 2010 16:17:25 CST Raw View
On 08/23/10 03:19, Thomas J. Gritzan wrote:
[snip]
> With a stack based implementation you can't do that. boost::variant
> tries to embed the value in the variant struct and also to minimize the
> memory footprint by sharing the buffer space (like a real union).
>
> It gets more complicated if you do this.
>
The following quote:
Similarly, if one of the bounded types of the variant is
nothrow default-constructible, then such a type could be used
as a safe "fallback" type in the event of failed copy construction.
from:
http://www.boost.org/doc/libs/1_44_0/doc/html/variant
/design.html#variant.design.never-empty
suggests there may be a solution if the variant is actually like
an optional<T> except that T is a template arg pack. IOW,
more like optional<T...>. Like haskell's Maybe:
http://www.zvon.org/other/haskell/Outputprelude/Maybe_d.html
there would be one type, Nothing, indicating no T... is valid at
the moment. This could be the result if the operator= failed for
any reason. This Nothing would be the bounded type which is
nothrow default-constructible mentioned in the boost variant
reference above.
Does that sound reasonable?
[snip]
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Florian Goujeon <florian.goujeon@42ndart.org>
Date: Sun, 15 Aug 2010 15:38:33 CST Raw View
Hello,
Thanks to the variadic templates, the presence of the tuples in the
STL has become obvious.
But what about the variants? I recently had to write an implementation
of boost::variant using variadic templates. I could make it without
any difficulty. Hence, I thought the introduction of an std::variant
template in the C++0x's STL would be as obvious as std::tuple.
I've been surprised to notice that there wasn't any plan to do this.
Some may argue that the unrestricted unions will do the job, but IMHO
it's a pity to incorporate in the core language a feature that could
be delegated to a library. Small is beautiful.
Besides, we can't create a union type by using a template parameter
pack, like the following code does with boost/std::variant:
template<typename... T>
std::variant<T...> f()
{
//...
}
So, why?
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Mathias Gaunard <loufoque@gmail.com>
Date: Tue, 17 Aug 2010 11:45:32 CST Raw View
On Aug 15, 10:38 pm, Florian Goujeon <florian.gouj...@42ndart.org>
wrote:
> Hello,
>
> Thanks to the variadic templates, the presence of the tuples in the
> STL has become obvious.
>
> But what about the variants? I recently had to write an implementation
> of boost::variant using variadic templates. I could make it without
> any difficulty.
Exception-safety of operator= is a difficult issue with a stack-based
variant.
How did you solve it?
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Florian Goujeon <florian.goujeon@42ndart.org>
Date: Wed, 18 Aug 2010 20:48:24 CST Raw View
> Exception-safety of operator= is a difficult issue with a stack-based
> variant.
> How did you solve it?
I don't know what you're talking about.
Could you please send me a code that underlines this issue?
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Larry Evans <cppljevans@gmail.com>
Date: Wed, 18 Aug 2010 20:48:18 CST Raw View
On Aug 17, 12:45 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On Aug 15, 10:38 pm, Florian Goujeon <florian.gouj...@42ndart.org>
> wrote:
>
> > Hello,
>
> > Thanks to the variadic templates, the presence of the tuples in the
> > STL has become obvious.
>
> > But what about the variants? I recently had to write an implementation
> > of boost::variant using variadic templates. I could make it without
> > any difficulty.
>
> Exception-safety of operator= is a difficult issue with a stack-based
> variant.
> How did you solve it?
>
Florian,
This:
http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html#variant.design.never-empty.problem
might provide useful background on Mathias' question.
HTH.
-Larry
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Thomas J. Gritzan" <phygon_antispam@gmx.de>
Date: Fri, 20 Aug 2010 10:31:12 CST Raw View
Am 17.08.2010 19:45, schrieb Mathias Gaunard:
> On Aug 15, 10:38 pm, Florian Goujeon <florian.gouj...@42ndart.org>
> wrote:
>> Hello,
>>
>> Thanks to the variadic templates, the presence of the tuples in the
>> STL has become obvious.
>>
>> But what about the variants? I recently had to write an implementation
>> of boost::variant using variadic templates. I could make it without
>> any difficulty.
>
> Exception-safety of operator= is a difficult issue with a stack-based
> variant.
> How did you solve it?
Since it will be C++0x, I would require all types in variant to have a
no-throw move constructor. Then it is just a straightforward copy&swap,
or a copy-destroy-move.
--
Thomas
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Mathias Gaunard <loufoque@gmail.com>
Date: Sat, 21 Aug 2010 22:01:40 CST Raw View
On Aug 20, 5:31 pm, "Thomas J. Gritzan" <phygon_antis...@gmx.de>
wrote:
> Since it will be C++0x, I would require all types in variant to have a
> no-throw move constructor.
A pretty hard requirement, that you cannot even detect at compile-time
(or can you?)
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]