Tom
Tom

Reputation: 1291

Boost::variant pointer to type not yet set

Suppose I had a boost variant instance containing a default initialised value (DataContainer). After a certain point in the program, it is guaranteed that the variant instance will contain a certain type (float) before a critical part of the program, but I only have access to the variant instance prior to it being set to this guaranteed type (DataDispatcher::set).

How can I create a float pointer to the location where the variant instance will be stored? Is it possible? My current attempt in Dispatcher::set causes a runtime error since the variant does not yet contain the float type.

Thanks

using Variant = boost::variant<std::string, float>;

struct DataContainer {
     Variant value; // default initalised
};

class DataDispatcher {
public:

   void set(DataContainer* dc) {
        // this causes a fpointer to be equal to nullptr because dc->value is a string
        // how to fix this?
        fpointer = boost::get<float>(&dc->value);
   }

   // critical part of the program
   void dispatch() {
       importantCalculation(*val + 5);
   }

private:
   float* fpointer;
};

int main() {
    DataContainer dc;
    DataDispatcher dd;
    dd.set(&dc); // only have access to variant instance here
    dc.val = 1.4252; // guarantee it will contain a float
    dd.dispatch(); // critical part
}


Upvotes: 1

Views: 63

Answers (1)

sehe
sehe

Reputation: 393064

How can I create a float pointer to the location where the variant instance will be stored? Is it possible?

That's not reliably possible. It smells like a design issue when you "need" to store a pointer to data before it exists.

The natural solution is to store a reference to the variant instead: Live On Coliru

If you are convinced that the "optimization" of keeping a direct pointer to the float value is significant, you could do that manually:

Also Live On Coliru

#include <boost/variant.hpp>
#include <iostream>
#include <string>
void importantCalculation(float v) {
    std::cout << "importantCalculation(" << v << ")\n";
}

using Variant = boost::variant<std::string, float>;

struct DataContainer {
    Variant value;
};

class DataDispatcher {
  public:
    void reset() {
        _cached = nullptr;
        _ref    = nullptr;
    }

    void set(DataContainer const& dc) {
        _ref = &dc;
        update_cache(); // in case it is already float
    }

    // critical part of the program
    void dispatch() { importantCalculation(get() + 5); }

  private:
    DataContainer const* _ref    = nullptr;
    float const*         _cached = nullptr;

    void update_cache() {
        _cached = nullptr;
        if (_ref)
            if (auto* fp = boost::get<float>(&_ref->value))
                _cached = fp;
    }

    float get() {
        if (!_cached)
            update_cache();
        assert(_cached);
        return *_cached;
    }
};

int main() {
    DataContainer  dc;
    DataDispatcher dd;
    dd.set(dc);        // only have access to variant instance here
    dc.value = 1.4252; // guarantee it will contain a float
    dd.dispatch();     // critical part
    
    // important:
    dd.reset();
    dc.value = "3.6474";

    dc.value = 2.5363;
    dd.set(dc); // important
    dd.dispatch();
}

Prints

importantCalculation(6.4252)
importantCalculation(7.5363)

Note that other variant implementations might document a guaranteed element storage layout for small element types, and you could then rely on that documented guarantee.

Upvotes: 1

Related Questions