Question

I'm not sure if this calls for boost::any. I'd rather use native templates if there's a pattern that does what I want. I have this class (which I've written here using boost::any) defined in a header:

template <typename T> class Observable;

class Report
{
    typedef unordered_map<wstring, std::shared_ptr<Observable<boost::any>>> ObservationMap;

public:
    void AddObservable(const wstring& name, std::shared_ptr<Observable<boost::any>> observable);
    const Observable<boost::any>& GetObservable(const wstring& name) const;

protected:
    ObservationMap observations;
};

and Observable is defined like this:

template <typename T>
struct Observable
{
    typedef T Type;

    T Quantity;
    // ...
};

I would really like some way of adding Observables of any quantity type to Reports. Is there a more effective way of doing this without incurring the overhead of boost::any?

Edit: An example of code that adds observables to the report class would be something like this:

Observable<float> obs1;
obs1.Quantity = 1.1f;
Observable<int> obs2;
obs2.Quantity = 5;

Report report;
report.AddObservable("Height", obs1);
report.AddObservable("Age", obs2);

Then somewhere else I would want to read those quantities. Say I receive the report:

void DoSomethingWithReport(Report& report)
{
    float height = report.GetObservable("Height").Quantity;
    int age = report.GetObservable("Age").Quantity;
}

Something like that. Of course, I'd really add other methods to check if those observations are present in the report, etc. I prefer it to be generic because I don't know beforehand, at the receiving site, what it will contain, so I'd like some flexibility.

Was it helpful?

Solution

boost::any is a decently efficient way to store a value of a type you have prevented the compiler (for whatever reason or design requirement) from knowing.

If there are only a limited set of operations you want to perform on the data, you can use type erasure instead and/or a base interface class. But that requires knowing all uses for the data when you define the interface.

If you have a limited set of data types, boost::variant or the like can be more efficient than any. You can even have a variant that includes any or a type erasure object (or objects) in the set of types.

You can use reflection or similar techniques to store complex data and wrap it in any accessors (or variant) if your data is sometimes structured, which can reduce storage overhead somewhat. Basically structured data becomes a type erased type with member access by name.

Much of these techniques end up mirroring type behaviour of scripting or bytecode languages: sometimes it might be a good idea to write the part of your app that needs this amount of compile time flexibilty in a mainly runtime type checked language.

Finally you may decide you do not need all this needless runtime dynamic typing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top