How to determine if a boost::variant variable is empty?
Asked Answered
S

2

11

I have defined a boost::variant var like this:

boost::variant<boost::blank, bool, int> foo;

This variable, when instantiated but not initialized, has a value of type boost::blank, because boost::blank is the first type passed to the templated boost::variant.

At some point, I want to know if foo has been initialized. I've tried this, but with no good results:

if (foo) //doesn't compile
if (foo != boost::blank()) //doesn't compile
if (!(foo == boost::blank())) //doesn't compile

I think it's worth noticing that, when foo has been initialized (eg., foo = true), it can be "reset" by doing foo = boost::blank();.

How can I check if foo has been initialized, ie, it has a different type than boost::blank?

Shabuoth answered 9/7, 2015 at 14:43 Comment(9)
bool const is_blank = boost::get<boost::blank>(&foo)Combative
@PiotrS. it works but I don't quite get why. Care to elaborate?Shabuoth
@PiotrS.: boost::variant<comment, answer> foo(getWhatThatShouldHaveBeen()); assert(foo.which() == 1);Nostology
@FerranMG: Why not read the documentation???Nostology
@Combative it also works, but I'm concerned about a possible performance hit. As far as I know, boost::get performs a static_cast and applies a visitor if the cast is successful. Would a visitor of type boost::blank be so trivial that I could neglect the overhead?Shabuoth
@FerranMG: My answer is extremely cheap. Is there a problem with it?Nostology
@LightnessRacesinOrbit I didn't read the documentation because I mistakenly assumed which worked differently. There really is no need to be pushy. Your answer is cheap and valid, but could eventually cause problems. I'll add a comment to your answer to discuss that.Shabuoth
@FerranMG: I'm not being "pushy"; reading the documentation should have been your first step and I absolutely reserve the right to point that out when you're asking for free help.Nostology
@FerranMG: AFAIK static visitor in boost::variant uses an index table based on which for visit functions. I don't think there would be significant performance difference to check against which().Combative
N
7

When the first type is "active", foo.which() == 0. Use that.

Returns: The zero-based index into the set of bounded types of the contained type of *this. (For instance, if called on a variant<int, std::string> object containing a std::string, which() would return 1.)

(http://www.boost.org/doc/libs/1_58_0/doc/html/boost/variant.html#idp288369344-bb)

Nostology answered 9/7, 2015 at 15:11 Comment(2)
This works, but could eventually be the cause of problems if ever the ordering of the types in the boost::variant definition changes. It will work perfectly fine practically always (more so because I'm trying to identify when the variable is of type boost::blank, which makes much more sense to have as the first type), but I think boost::get<boost::blank>(&foo) would be a more complete solution if it were as cheap as foo.which() == 0. Sadly, I guess I'm going to have to choose between hypothetical problems, or real overhead, so I'll probably end up using which.Shabuoth
+1 and agreed. I still showed the visitor approach in my answer, because concerns like this often stem from fear. And the fear stems from lack of experience. Visitors don't need to be intimidating :)Elaelaborate
E
11

You could define a visitor to detect the 'blankness':

struct is_blank_f : boost::static_visitor<bool> {
   bool operator()(boost::blank) const { return true; }

   template<typename T>
   bool operator()(T const&) const { return false; }
};

Use it like so:

bool is_blank(my_variant const& v) {
   return boost::apply_visitor(is_blank_f(), v);
}
Elaelaborate answered 9/7, 2015 at 15:56 Comment(3)
I like this solution because it's complete, but for my use case I'll need to avoid the overhead that calling a visitor will add. Thanks for it, though.Shabuoth
@Shabuoth my bet: it won't add overhead. Compile with optimizations enabled.Elaelaborate
Looks like the 0==which() check wins anyways, guessing from the generated assembly (clang 3.6 and gcc 5.x). Tried to benchmark but it's hard to get useful measurements on the which() check: github.com/rmartinho/nonius/issues/20Elaelaborate
N
7

When the first type is "active", foo.which() == 0. Use that.

Returns: The zero-based index into the set of bounded types of the contained type of *this. (For instance, if called on a variant<int, std::string> object containing a std::string, which() would return 1.)

(http://www.boost.org/doc/libs/1_58_0/doc/html/boost/variant.html#idp288369344-bb)

Nostology answered 9/7, 2015 at 15:11 Comment(2)
This works, but could eventually be the cause of problems if ever the ordering of the types in the boost::variant definition changes. It will work perfectly fine practically always (more so because I'm trying to identify when the variable is of type boost::blank, which makes much more sense to have as the first type), but I think boost::get<boost::blank>(&foo) would be a more complete solution if it were as cheap as foo.which() == 0. Sadly, I guess I'm going to have to choose between hypothetical problems, or real overhead, so I'll probably end up using which.Shabuoth
+1 and agreed. I still showed the visitor approach in my answer, because concerns like this often stem from fear. And the fear stems from lack of experience. Visitors don't need to be intimidating :)Elaelaborate

© 2022 - 2024 — McMap. All rights reserved.