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?

FerranMG
  • 181
  • 1
  • 11
  • 3
    `bool const is_blank = boost::get(&foo)` – pmed Jul 09 '15 at 14:54
  • @PiotrS. it works but I don't quite get why. Care to elaborate? – FerranMG Jul 09 '15 at 15:12
  • 2
    @PiotrS.: `boost::variant foo(getWhatThatShouldHaveBeen()); assert(foo.which() == 1);` – Lightness Races in Orbit Jul 09 '15 at 15:12
  • @FerranMG: Why not _read the documentation_??? – Lightness Races in Orbit Jul 09 '15 at 15:13
  • @pmed 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? – FerranMG Jul 09 '15 at 15:15
  • 2
    @FerranMG: My answer is extremely cheap. Is there a problem with it? – Lightness Races in Orbit Jul 09 '15 at 15:16
  • @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. – FerranMG Jul 09 '15 at 15:49
  • @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. – Lightness Races in Orbit Jul 09 '15 at 15:57
  • @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()`. – pmed Jul 10 '15 at 08:43

2 Answers2

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);
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 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. – FerranMG Jul 09 '15 at 18:09
  • 7
    @FerranMG my bet: it won't add overhead. Compile with optimizations enabled. – sehe Jul 09 '15 at 20:46
  • 2
    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: https://github.com/rmartinho/nonius/issues/20 – sehe Jul 09 '15 at 23:24
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)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 3
    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(&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`. – FerranMG Jul 09 '15 at 15:54
  • 2
    +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 :) – sehe Jul 09 '15 at 15:58