How Does the Any Library Improve Your Programs?
- Typesafe storage and safe retrieval of arbitrary types
- A means to store heterogeneous types in Standard Library containers
- Types are being passed through layers that need not know anything about the types
The Any library provides a type, any, that allows for storage of any type for later retrieval without loss of type safety. It is like a variant type on steroids: It will hold any type, but you have to know the type to retrieve the value. There are times when you need to store unrelated types in the same container. There are times when certain code only cares about conveying data from one point to another without caring about the data's type. At face value, it is easy to do those things. They can be done with an indiscriminate type such as void*. They can be done using a discriminated union. There are numerous variant types available that rely on some type tag mechanism. Unfortunately, all of these suffer from a lack of type safety, and only in the most controlled situations should we ever purposely defeat the type system. The Standard Library containers are parameterized on the type they contain, which poses a seemingly impossible challenge for storing elements of heterogeneous types in them. Fortunately, the cure doesn't have to be spelled void*, because the Any library allows you to store objects of different types for later retrieval. There is no way to get to the contained value without knowing its exact type, and thus, type safety is preserved.
When designing frameworks, it isn't possible to know in advance about the types that will be used together with the framework classes. A common approach is to require the clients of the framework to adapt a certain interface, or inherit from base classes provided by the framework. This is reasonable, because the framework probably needs to communicate with various higher-level classes in order to be useful. There are, however, situations where the framework stores or otherwise accepts types that it doesn't need to (or can) know anything about. Rather than violating the type system and go with the void* approach, the framework can use any.
How Does Any Fit with the Standard Library?
One important property of Any is that it provides the capability to store objects of heterogeneous types in Standard Library containers. It is also a sort of variant data type, which is something sorely needed, and currently missing, in the C++ Standard Library.
The class any allows typesafe storage and retrieval of arbitrary types. Unlike indiscriminate types, any preserves the type, and actually does not let you near the stored value without knowing the correct type. Of course, there are means for querying for the type, and testing alternatives for the contained value, but in the end, the caller must know the exact type of the value in an any object, or any denies access. Think of any as a locked safe. Without the proper key, you cannot get in. any requires the following of the types it stores:
- CopyConstructible It must be possible to copy the type.
- Non-throwing destructor As all destructors should be!
- Assignable For the strong exception guarantee (types that aren't assignable can still be used with any, but without the strong guarantee).
This is the public interface of any:
The default constructor creates an empty instance of anythat is, an any that doesn't contain a value. Of course, there is no way of retrieving the value of an empty any, because no value exists.
any(const any& other);
Creates a distinct copy of an existing any object. The value that is contained in other is copied and stored in this.
template<typename ValueType> any(const ValueType&);
This templated constructor stores a copy of the argument of type ValueType passed to the constructor. The argument is a const reference, so it is legal to pass a temporary object to be stored in any. Note that the constructor is not explicit, which would make typical uses of any awkward and would not impart additional safety.
The destructor destroys the contained value, but note that because the destruction of a raw pointer does not invoke operator delete or operator delete on the pointer, you should always wrap raw pointers in smart pointers such as shared_ptr (see "Library 1: Smart_ptr 1") when using pointers with any.
any& swap(any& other);
Exchanges the values stored by the two any objects.
any& operator=(const any& other);
Discards the stored value, if the instance of any is not empty, and stores a copy of the value in other.
any& operator=(const ValueType& value);
Discards the stored value, if the instance of any is not empty, and stores a copy of value, which can be of an arbitrary type that fulfills any's requirements.
bool empty() const;
Indicates whether an instance of any currently has a value, regardless of what that value is. Thus, when an any holds a pointer, empty returns false even if the pointer value is null.
const std::type_info& type() const;
Indicates the type of the stored value. If the any is empty, the type is void.
ValueType any_cast(const any& operand);
any_cast gives you access to the value stored in an any. The argument is the any whose value is to be retrieved. If the type ValueType does not correspond to the type of the stored value, any throws a bad_any_cast exception. Note that the syntax is like that of dynamic_cast.
const ValueType* any_cast(const any* operand);
This overloaded any_cast takes a pointer to any, and returns a pointer to the stored value. If the type in the any isn't ValueType, a null pointer is returned. Note, again, that the syntax is like that of dynamic_cast.
ValueType* any_cast(any* operand);
This overloaded any_cast is similar to the preceding version, but whereas the previous version used const-qualified pointers for return type and argument type, this version doesn't.
This exception is thrown when trying to cast an any object to a type other than the type stored in the any. bad_any_cast is derived from std::bad_cast. Note that when calling any_cast with a pointer argument, no exception is thrown (similar to how dynamic_cast with pointer types return the null pointer), whereas dynamic_cast to reference types throws an exception on failure.