Document number: N3804 Date: 2013-10-09 Project: Programming Language C++ Reply-to: Beman Dawes <bdawes at acm dot org>

Kevlin Henney <kevlin at curbralan dot com>

Daniel Krügler <daniel dot kruegler at gmail dot com>

Any Library Proposal (Revision 3)

Introduction

Revision History

Motivation and Design

Design paths not taken

Examples

FAQ

Acknowledgements

Proposed Wording

ValueType Requirements

Header <experimental/any> synopsis

Class bad_any_cast

Class any

Non-member functions



This paper proposes a type-safe container for single values of value types. The C++ standards committee is planning to include the proposal in a library Technical Specification (TS).

The proposal is based on the Boost Any Library (see www.boost.org/libs/any). The Boost version of library has been in wide use for over a decade, and the library has been implemented at least twice in addition to the Boost implementation. The proposal is a pure addition to the standard library, requires no modifications to any C++14 standard library components, requires no compiler support, and will have no effect on existing namespace-disciplined code.

N3804 - Revision 3

Added Daniel Krügler to the list of authors. Daniel has worked tirelessly to improve the standardese, and has also implemented the proposal.

Remove the "Do List".

Change target to a library TS, per Bristol LEWG decision. Namespace to be decided.

Add wording to class any introduction to clarify that empty is a valid state and to clarify the relationship with any_cast .

introduction to clarify that empty is a valid state and to clarify the relationship with . Remove bad_alloc from Throws: elements to conform with standard library conventions.

from elements to conform with standard library conventions. For the reference overloads of any_cast , replace Returns: wording with explicit formulation that distinguishes between const and non-const cases, in response to Bristol LEWG concerns. The more explicit formulation also leaves less room for misinterpretation by forwarding to the pointer versions of any_cast.

, replace wording with explicit formulation that distinguishes between const and non-const cases, in response to Bristol LEWG concerns. The more explicit formulation also leaves less room for misinterpretation by forwarding to the pointer versions of From the reference overloads of any_cast , remove "[ Note: For consistency with the C++ keyword casts, a copy is returned. --end note ]". The note was confusing people.

, remove "[ For consistency with the C++ keyword casts, a copy is returned. ]". The note was confusing people. Add an example to the reference overloads of any_cast , illustrating points that have caused questions: any_cast to value any_cast to non-const reference used to modify stored value in the any object. Use of any to store a const char* , and then access it by value and by reference. Use of any to store a std::string , as an example of a movable type, and then move from and to the any object. Correct access to const any object holding a std::string . Ill-formed attempt to modify the const any object.

, illustrating points that have caused questions: For the pointer overloads of any_cast , revise the signatures to make explicit that the return type is a pointer and that return type constness is determined by argument constness.

, revise the signatures to make explicit that the return type is a pointer and that return type constness is determined by argument constness. For the pointer overloads of any_cast , add an explicit requirement that the const argument form return a const qualified pointer, as requested by the LEWG in Bristol.

, add an explicit requirement that the const argument form return a const qualified pointer, as requested by the LEWG in Bristol. For the pointer overloads of any_cast , change "a null pointer" to nullptr , and make nullptr comparison explicit in the example.

, change "a null pointer" to , and make comparison explicit in the example. In the any_cast pointer overload example, remove * from the cast type. This was a nasty bug that compiled OK but caused the example function to return the wrong result.

pointer overload example, remove from the cast type. This was a nasty bug that compiled OK but caused the example function to return the wrong result. Add missing noexcept to bad_any_cast::what . (Antony Polukhin)

to . (Antony Polukhin) Add clear() function. (Antony Polukhin)

function. (Antony Polukhin) Add any_cast(any&&) overload, so that rvalue arguments work properly, as requested by the LEWG in Bristol.

overload, so that rvalue arguments work properly, as requested by the LEWG in Bristol. Removed unneeded std:: . (Daniel Krügler).

. (Daniel Krügler). Change any construction and assignment of ValueType from pass-by-value to pass-by-rvalue-reference. Suggested by Sean Parent as more in keeping with the standard library and by Antony Polukhin's Boost.Any analysis. Daniel Krügler pointed out that a single perfect-forwarding function copes with both const and non-const arguments, and both template pass-by-rvalue-reference constructor and operator= follow his advice.

construction and assignment of from pass-by-value to pass-by-rvalue-reference. Suggested by Sean Parent as more in keeping with the standard library and by Antony Polukhin's Boost.Any analysis. Daniel Krügler pointed out that a single perfect-forwarding function copes with both const and non-const arguments, and both template pass-by-rvalue-reference constructor and follow his advice. Add a Remarks: constraint to the template<typename ValueType> any(ValueType&& value) constructor, constructor with allocator, and operator= because otherwise these functions would catch too much (including non-const values of type any ). (Daniel Krügler)

constraint to the constructor, constructor with allocator, and because otherwise these functions would catch too much (including non-const values of type ). (Daniel Krügler) Fix an "any assignment" discrepancy between the synopsis and semantics sections. (Daniel Krügler)

Add noexcept to the first of the allocator constructors. (Daniel Krügler)

to the first of the allocator constructors. (Daniel Krügler) Change the destructor's effects to call clear() , and specify clear() effects such that any contained value is destroyed. The wording is such that it is still correct in the presence of small-value optimization. (Daniel Krügler)

, and specify effects such that any contained value is destroyed. The wording is such that it is still correct in the presence of small-value optimization. (Daniel Krügler) Introduce a definition for contained object , and then consistently use that term in places that previously spoke of "contents" or "contained value". (Daniel Krügler)

, and then consistently use that term in places that previously spoke of "contents" or "contained value". (Daniel Krügler) Clarified the Effects of empty() to avoid a near recursive definition. (Daniel Krügler)

of to avoid a near recursive definition. (Daniel Krügler) Clarified the type() Returns non-empty case as it was somewhat blurry in that it could be read to mean typeid(this.value) where value is the contained object and could mean that typeid is used as in a polymorphic operation when the value type is a polymorphic type. (Daniel Krügler)

non-empty case as it was somewhat blurry in that it could be read to mean where is the contained object and could mean that is used as in a polymorphic operation when the value type is a polymorphic type. (Daniel Krügler) Remove a lengthy note from ValueType requirements as tutorial in nature and thus beyond the scope of the standard library or a library TS.

requirements as tutorial in nature and thus beyond the scope of the standard library or a library TS. Rework the specifications for construction from ValueType and assignment from ValueType to be more precise and to eliminate errors that had crept in as C++11 features were applied. (Daniel Krügler private email 2013/05/09)

and assignment from to be more precise and to eliminate errors that had crept in as C++11 features were applied. (Daniel Krügler private email 2013/05/09) Remove the ValueType requirements section, and add a Requires element to the signatures that actually require CopyConstructible, following current practice of the standard library for containers.

requirements section, and add a element to the signatures that actually require CopyConstructible, following current practice of the standard library for containers. Merge suggested wording changes by Daniel Krügler and Antony Polukhin, and resolve any differences to the satisfaction of all concerned. (Daniel Krügler)

Rename header <experimental/any> and place contents in inline namespace per LEWG/LWG direction in Chicago.

N3508 - Revision 2

Fix " swap(swap( " and " tr2 " typos. (Daniel Krügler)

" and " " typos. (Daniel Krügler) Change any::swap to return void , to conform to standard library practice for member swap function return values. (Daniel Krügler)

to return , to conform to standard library practice for member function return values. (Daniel Krügler) Remove noexcept from any::~any(); , to conform to standard library practice. (Daniel Krügler)

from , to conform to standard library practice. (Daniel Krügler) Change uses of "strong exception-safety guarantee" to "no effects if an exception is thrown", conforming to the usual standard library approach to specifying the strong exception-safety guarantee. (Daniel Krügler)

Eliminate mentions of assignment - CopyConstructible is sufficient. (Daniel Krügler)

Add type erased allocator constructors. (Daniel Krügler)

Change the any default constructor to noexcept . (Daniel Krügler)

default constructor to . (Daniel Krügler) Change any& operator=(any rhs) to any& operator=(const any& rhs) , and change the effects accordingly. This avoids an ambiguity. (Daniel Krügler, Sean Parent)

to , and change the effects accordingly. This avoids an ambiguity. (Daniel Krügler, Sean Parent) Add rhs.empty() postcondition to any& operator=(any&& rhs) noexcept . (Daniel Krügler)

postcondition to . (Daniel Krügler) Replace "successful" and "unsuccessful" in any_cast with explicit specification. (Chris Jefferson)

with explicit specification. (Chris Jefferson) Change name of template parameter for pointer flavor of from ValueType to ValueTypePtr . (Sylvester Hesp)

to . (Sylvester Hesp) Add any_cast examples.

examples. Updated to reflect LWG decision to target C++14.

Fix (()) typo in example.

Added Acknowledgements.

Added To do list.

N3390 - Revision 1

Text updated to reflect target is could be a TS, C++1Y, or a new IS.

noexcept applied where appropriate.

applied where appropriate. any copy assignment interface updated to match Boost.

copy assignment interface updated to match Boost. Added non-member swap function.

function. Added missing any_cast overload.

overload. Added any move constructor (Sean Parent).

move constructor (Sean Parent). Added any move assignment (Sean Parent).

move assignment (Sean Parent). Changed constructor from ValueType to pass by value instead of const reference (Sean Parent).

Changed any assignment from ValueType to pass by value instead of const reference (Sean Parent).

assignment from ValueType to pass by value instead of const reference (Sean Parent). Changed any_cast with pointer argument to require that the template parameter itself be a pointer, to match dynamic_cast . (Thanks to Sean for catching this.) The Boost interface added * to the return type solely to cope with historical compiler issues, and this workaround had crept into the proposal.

with pointer argument to require that the template parameter itself be a pointer, to match . (Thanks to Sean for catching this.) The Boost interface added to the return type solely to cope with historical compiler issues, and this workaround had crept into the proposal. Made several small editorial changes to improve standardese.

N1939 - Initial paper

Motivation and Design

Original Boost design

There are times when a generic (in the sense of general as opposed to template-based programming) type is needed: variables that are truly variable, accommodating values of many other more specific types rather than C++'s normal strict and static types. We can distinguish three basic kinds of generic type:

Converting types that can hold one of a number of possible value types, e.g. int and string , and freely convert between them, for instance interpreting 5 as "5" or vice-versa. Such types are common in scripting and other interpreted languages. boost::lexical_cast supports such conversion functionality.

Discriminated types that contain values of different types but do not attempt conversion between them, i.e. 5 is held strictly as an int and is not implicitly convertible either to "5" or to 5.0 . Their indifference to interpretation but awareness of type effectively makes them safe, generic containers of single values, with no scope for surprises from ambiguous conversions.

Indiscriminate types that can refer to anything but are oblivious to the actual underlying type, entrusting all forms of access and interpretation to the programmer. This niche is dominated by void * , which offers plenty of scope for surprising, undefined behavior.

The proposed any class (based on the class of the same name described in "Valued Conversions" by Kevlin Henney, C++ Report 12(7), July/August 2000) is a variant value type based on the second category. It supports copying of any value type and safe checked extraction of that value strictly against its type.

Design update for C++ 11/14

In several aspects, the design of std::any can be compared to std::function . This proposal differs from the specification of std::function type-erasure policy by emphasizing the worth of having guaranteed nothrow move-operations. While std::function imposes little restrictions upon the wrapped function object types where the small-object optimization can be applied, in this proposal std::any restricts these cases to those where the copy/move operations of the contained object cannot throw exceptions.

A similar design, offering more appropriate operators, could be used for a generalized function adaptor, a generalized iterator adaptor, and other object types that need uniform runtime treatment but support only compile-time template parameter conformance. Such components are not proposed here.

The following code demonstrates the syntax for using implicit conversions to and copying of any objects:

#include <list> #include <experimental/any> using std::experimental:: any_cast ; using std ::experimental:: any; typedef std::list< any > many; void append_int(many& values, int value) { any to_append = value; values.push_back(to_append); } void append_string(many& values, const std::string& value) { values.push_back(value); } void append_char_ptr(many& values, const char* value) { values.push_back(value); } void append_any(many& values, const any & value) { values.push_back(value); } void append_nothing(many& values) { values.push_back(any()); }

The following predicates follow from the previous definitions and demonstrate the use of queries on any objects:

bool is_empty(const any & operand) { return operand. empty (); } bool is_int(const any & operand) { return operand. type () == typeid(int); } bool is_char_ptr(const any & operand) { try { any_cast <const char *>(operand); return true; } catch(const std::tbd::bad_any_cast &) { return false; } } bool is_string(const any & operand) { return any_cast <std::string*>(&operand); } void count_all(many& values, std::ostream& out) { out << "#empty == " << std::count_if(values.begin(), values.end(), is_empty) << std::endl; out << "#int == " << std::count_if(values.begin(), values.end(), is_int) << std::endl; out << "#const char * == " << std::count_if(values.begin(), values.end(), is_char_ptr) << std::endl; out << "#string == " << std::count_if(values.begin(), values.end(), is_string) << std::endl; }

The following type, patterned after the OMG's Property Service, defines name-value pairs for arbitrary value types:

struct property { property(); property(const std::string&, const any &); std::string name; any value; }; typedef std::list<property> properties;

The following base class demonstrates one approach to runtime polymorphism based callbacks that also require arbitrary argument types. The absence of virtual member templates requires that different solutions have different trade-offs in terms of efficiency, safety, and generality. Using a checked variant type offers one approach:

class consumer { public: virtual void notify(const any &) = 0; ... };

What is the relationship between Boost.any and Boost.variant?

Boost::any is like a "typesafe void*", while Boost::variant is a "typesafe union".

Sean Parent and Daniel Krügler provided numerous comments, corrections, and suggestions, and were particularly helpful applying C++11 features to the library. Sean also provided a copy of his Adobe C++ implementation. Daniel's expertise with library standardese markedly improved the proposed wording. Antony Polukhin, the maintainer of the Boost implementation, provided a helpful analysis of differences between the Boost implementation and the proposal.

Commentary that is not part of the proposed wording is shaded in gray.

Add the following section to the library TS working paper at a location to be determined by the project editor:

Any

This clause describes components that C++ programs may use to perform operations on objects of a discriminated type.

[Note: The discriminated type may contain values of different types but does not attempt conversion between them, i.e. 5 is held strictly as an int and is not implicitly convertible either to "5" or to 5.0 . This indifference to interpretation but awareness of type effectively allows safe, generic containers of single values, with no scope for surprises from ambiguous conversions. -- end note.]

Header <experimental/any> synopsis

namespace std { namespace experimental { inline namespace any_v1 { class bad_any_cast : public bad_cast { public: virtual const char* what() const noexcept; }; class any { public: // construct/destruct any() noexcept; any(const any& other); any(any&& x) noexcept; template <typename ValueType> any(ValueType&& value); template <class Allocator> any(allocator_arg_t, const Allocator& a) noexcept; template <class Allocator, typename ValueType> any(allocator_arg_t, const Allocator& a, ValueType&& value); template <class Allocator> any(allocator_arg_t, const Allocator& a, const any& other); template <class Allocator> any(allocator_arg_t, const Allocator& a, any&& other) noexcept; ~any(); // assignments any& operator=(const any& rhs); any& operator=(any&& rhs) noexcept; template <typename ValueType> any& operator=(ValueType&& rhs); // modifiers void clear() noexcept; void swap(any& rhs) noexcept; // observers bool empty() const noexcept; const type_info& type() const noexcept; }; void swap(any& x, any& y) noexcept; template<typename ValueType> ValueType any_cast(const any& operand); template<typename ValueType> ValueType any_cast(any& operand); template<typename ValueType> ValueType any_cast(any&& operand); template<typename ValueType> const ValueType* any_cast(const any* operand) noexcept; template<typename ValueType> ValueType* any_cast(any* operand) noexcept; }}}

Objects of type bad_any_cast are thrown by a failed any_cast .

An object of class any stores an instance of any type that satisfies the constructor requirements or is empty, and this is referred to as the state of the class any object. The stored instance is called the contained object. Two states are equivalent if they are either both empty or if both are not empty and if the contained objects are equivalent.

The non-member any_cast functions provide type-safe access to the contained object.

Implementations should avoid the use of dynamically allocated memory for a small contained object. [Example: where the object constructed is holding only an int. -- end example] Such small-object optimization shall only be applied to nothrow copyable types.

The use of "should" in the above paragraph provides ISO normative encouragement. This is a deliberate design decision.

any construct/destruct

any() noexcept;

Postconditions: this->empty()

any(const any& other);

Effects: Constructs an object of type any with an equivalent state as other . Throws: Any exceptions arising from the copy constructor of the contained object.

any(any&& other) noexcept;

Effects: Constructs an object of type any with a state equivalent to the original state of other . Postconditions: other is left in a valid but otherwise unspecified state.

template<typename ValueType> any(ValueType&& value);

Let T be equal to decay< ValueType>::type . Requires: T shall satisfy the CopyConstructible requirements. If is_copy_constructible<T>::value is false, the program is ill-formed. Effects: Constructs an object of type any that contains an object of type T direct-initialized with std::forward<ValueType>(value) . Remarks: This constructor shall not participate in overload resolution if decay<ValueType>::type is the same type as std::any . Throws: Any exception thrown by the selected constructor of T .

template <class Allocator> any(allocator_arg_t, const Allocator& a) noexcept; template <class Allocator, typename ValueType> any(allocator_arg_t, const Allocator& a, ValueType&& value); template <class Allocator> any(allocator_arg_t, const Allocator& a, const any& other); template <class Allocator> any(allocator_arg_t, const Allocator& a, any&& other) noexcept;

Requires: Allocator shall meet the requirements for an Allocator ([allocator.requirements]). Effects: Equivalent to the preceding constructors except that the contained object is constructed with uses-allocator construction ([allocator.uses.construction]) if memory allocation is performed.

~any();

Effects: clear() .

any assignments

any& operator=(const any& rhs);

Effects: any(rhs).swap(*this) , however, no effects if an exception is thrown. Returns: *this Throws: Any exceptions arising from the copy constructor of the contained object.

any& operator=(any&& rhs) noexcept;

Effects: any(std::move(rhs)).swap(*this) . Returns: *this Postconditions: The state of *this is equivalent to the original state of rhs and rhs is left in a valid but otherwise unspecified state.

template<typename ValueType> any& operator=(ValueType&& rhs);

Let T be equal to decay< ValueType>::type . Requires: T shall satisfy the CopyConstructible requirements. If is_copy_constructible<T>::value is false, the program is ill-formed. Effects: Constructs an object tmp of type any that contains an object of type T direct-initialized with std::forward<ValueType>(rhs) , and tmp.swap(*this) ; however, no effects if an exception is thrown. Returns: *this Remarks: This operator shall not participate in overload resolution if decay<ValueType>::type is the same type as std::any . Throws: Any exception thrown by the selected constructor of T .

any modifiers

void clear() noexcept;

Effects: If not empty, destroys the contained object. Postconditions: empty() == true .

void swap(any& rhs) noexcept;

Effects: Exchange the states of *this and rhs .

any observers

bool empty() const noexcept;

Returns: true if *this has no contained object, otherwise false .

const type_info& type() const noexcept ;

Returns: If *this has a contained object of type T, typeid(T) ; otherwise typeid(void) . [Note: Useful for querying against types known either at compile time or only at runtime. --end note]

void swap(any& x, any& y) noexcept;

Effects: x.swap(y) .

The following specifications for any_cast have been revised to increase clarity. For the existing two signatures these changes were to exposition only. The actual behavior is unchanged from prior revisions. The examples have been compiled and tested with GCC and VC++.

template<typename ValueType> ValueType any_cast(const any& operand); template<typename ValueType> ValueType any_cast(any& operand); template<typename ValueType> ValueType any_cast(any&& operand);

Requires: is_reference<ValueType>::value is true or is_copy_constructible<ValueType>::value is true. Otherwise the program is ill-formed. Returns: For the first form, *any_cast<typename add_const<typename remove_reference<ValueType>::type>::type >(&operand) . For the second and third forms, *any_cast<typename remove_reference<ValueType>::type>(&operand) . Throws: bad_any_cast if operand.type() != typeid(remove_reference<ValueType>::type) . [Example: any x(5); // x holds int assert(any_cast<int>(x) == 5); // cast to value any_cast<int&>(x) = 10; // cast to reference assert(any_cast<int>(x) == 10); x = "Meow"; // x holds const char* assert(strcmp(any_cast<const char*>(x), "Meow") == 0); any_cast<const char*&>(x) = "Harry"; assert(strcmp(any_cast<const char*>(x), "Harry") == 0); x = string("Meow"); // x holds string string s, s2("Jane"); s = move(any_cast<string&>(x)); // move from any assert(s == "Meow"); any_cast<string&>(x) = move(s2); // move to any assert(any_cast<const string&>(x) == "Jane"); string cat("Meow"); const any y(cat); // const y holds string assert(any_cast<const string&>(y) == cat); any_cast<string&>(y); // error; cannot // any_cast away const --end example]

template<typename ValueType> const ValueType* any_cast(const any* operand) noexcept; template<typename ValueType> ValueType* any_cast(any* operand) noexcept;

Returns: If operand != nullptr && operand->type() == typeid(ValueType) , a pointer to the object contained by operand , otherwise nullptr . [Example: bool is_string(const any& operand) { return any_cast<string>(&operand) != nullptr; } --end example]

© Copyright 2001, 2012, 2013 Kevlin Henney

© Copyright 2006, 2012, 2013 Beman Dawes

© Copyright 2013 Daniel Krügler