// bslmf_matcharithmetictype.h -*-C++-*- #ifndef INCLUDED_BSLMF_MATCHARITHMETICTYPE #define INCLUDED_BSLMF_MATCHARITHMETICTYPE #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a class supporting "do-the-right-thing clause" dispatch. // //@CLASSES: // bslmf::MatchArithmeticType: implicit conversion of arithmetic types // //@SEE_ALSO: bslmf_matchanytype, bslstl_deque, bslstl_string, bslstl_vector // //@DESCRIPTION: This component defines a class, 'bslmf::MatchArithmeticType', // to which any arithmetic type can be implicitly converted. A class with that // conversion property is useful for meeting the certain requirements of the // standard sequential containers (e.g., 'bsl::vector', 'bsl::deque', // 'bsl::string'). // // Sequential containers have several overloaded method templates that accept a // pair of input iterators (e.g., constructors, 'insert' and 'append' methods), // but which must *not* accept arithmetic types (e.g., 'bool', 'char', 'short', // 'int', 'double'). See "ISO/IEC 14882:2011 Programming Language C++" (see // 'http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf'), // "Section 23.2.3 [sequence.reqmts]", paragraphs 14-15. This requirement is // informally known as the "do-the-right-thing clause". See // 'http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#438'. // // The convertibility of arguments to 'bslmf::MatchArithmeticType' is used to // dispatch calls with arithmetic arguments to the appropriate methods. Note // that this technique (a variation of "tag dispatch") can be compromised if // one uses a class that defines a conversion operator to // 'bslmf::MatchArithmeticType' (or a conversion operator to // 'bslmf::MatchAnyType') but otherwise do not behave as arithmetic types. // // Enumerations (not arithmetic types themselves) are implicitly convertible to // 'blsmf::MatchArithmeticType', and will dispatch as some integer (a sub-set // of arithmetic) type. // ///Usage ///----- // In this section we show intended use of this component. // ///Example 1: "Do-the-Right-Thing" Clause Dispatch ///- - - - - - - - - - - - - - - - - - - - - - - - // Suppose we want to create a container with two constructors: // //: o one constructor providing initialization with multiple copies of a single //: value (a "repeated value constructor"), and //: //: o the other providing initialization from a sequence of values delimited by //: a pair of iterators (a "range constructor"). // // A naive implementation can result in common usage situations in which // arguments meaningful to the former constructor are provided but where the // compiler resolves the overload to the latter constructor. // // For example, the 'MyProblematicContainer' class outlined below provides two // such constructors. Note that each is atypically embellished with a // 'message' parameter, allowing us to trace the call flow. //.. // // ============================ // // class MyProblematicContainer // // ============================ // // template <class VALUE_TYPE> // class MyProblematicContainer { // // // ... // // public: // // CREATORS // MyProblematicContainer(std::size_t numElements, // const VALUE_TYPE& value, // const char *message); // // Create a 'MyProblematicContainer' object containing the // // specified 'numElements' of the specified 'value', and write to // // standard output the specified 'message'. // // template <class INPUT_ITER> // MyProblematicContainer(INPUT_ITER first, // INPUT_ITER last, // const char *message); // // Create a 'MyProblematicContainer' object containing the values // // in the range starting at the specified 'first' iterator and // // ending immediately before the specified 'last' iterator of the // // 'INPUT_ITER' type, and write to standard output the specified // // 'message'. // // // ... // // }; // // // ======================================================================== // // INLINE FUNCTION DEFINITIONS // // ======================================================================== // // // ============================ // // class MyProblematicContainer // // ============================ // // // CREATORS // template <class VALUE_TYPE> // MyProblematicContainer<VALUE_TYPE>::MyProblematicContainer( // std::size_t numElements, // const VALUE_TYPE& value, // const char *message) // { // assert(message); // printf("CTOR: repeated value: %s\n", message); // // ... // } // // template <class VALUE_TYPE> // template <class INPUT_ITER> // MyProblematicContainer<VALUE_TYPE>::MyProblematicContainer( // INPUT_ITER first, // INPUT_ITER last, // const char *message) // { // assert(message); // printf("CTOR: range : %s\n", message); // // ... // } //.. // The problem with the 'MyProblematicContainer' class becomes manifest when // we create several objects: //.. // const char input[] = "How now brown cow?"; // MyProblematicContainer<char> initFromPtrPair( // input, // input + sizeof(input), // "Called with pointer pair."); // // MyProblematicContainer<char> initFromIntAndChar( // 5, // 'A', // "Called with 'int' and 'char'."); // // MyProblematicContainer<char> initFromIntAndInt( // 5, // 65, // 'A' // "Called with 'int' and 'int'."); //.. // Standard output shows: //.. // CTOR: range : Called with pointer pair. // CTOR: repeated value: Called with 'int' and 'char'. // CTOR: range : Called with 'int' and 'int'. //.. // Notice that the range constructor, not the repeated value constructor, is // invoked for the creation of 'initFromIntAndInt', the third object. // // The range constructor is chosen to resolve that overload because its match // of two arguments of the same type ('int' in this usage) without conversion // is better than that provided by the repeated value constructor, which // requires conversions of two different arguments. // // Note that, in practice, range constructors (expecting iterators) dereference // their arguments, and so fail to compile when instantiated with arithmetic // types. // // If we are fortunate, range constructor code will fail to compile; otherwise, // dereferencing integer values (i.e., using them as pointers) leads to // undefined behavior. // // Note that, in many other situations, overloading resolution issues can be // avoided by function renaming; however, as these are constructors, we do not // have that option. // // Instead, we redesign our class ('MyContainer' is the redesigned class) so // that the calls to the range constructor with two 'int' arguments (or pairs // of the same integer types) are routed to the repeated value constructor. // The 'bslmf::MatchArithmeticType' class is used to distinguish between // integer types and other types. // // First, we define the 'MyContainer' class to have constructors taking // the same arguments as the constructors of 'MyProblematicContainer': //.. // // ================= // // class MyContainer // // ================= // // template <class VALUE_TYPE> // class MyContainer { // // // ... // // public: // // CREATORS // MyContainer(std::size_t numElements, // const VALUE_TYPE& value, // const char *message); // // Create a 'MyProblematicContainer' object containing the // // specified 'numElements' of the specified 'value', and write to // // standard output the specified 'message'. // // template <class INPUT_ITER> // MyContainer(INPUT_ITER first, INPUT_ITER last, const char *message); // // Create a 'MyProblematicContainer' object containing the values // // in the range starting at the specified 'first' and ending // // immediately before the specified 'last' iterators of the type // // 'INPUT_ITER', and write to standard output the specified // // 'message'. // // // ... // // }; //.. // Then, we isolate the essential actions of our two constructors into two // private, non-creator methods. This allows us to achieve the results of // either constructor, as appropriate, from the context of the range // constructor. The two 'privateInit*' methods are: //.. // private: // // PRIVATE MANIPULATORS // void privateInit(std::size_t numElements, // const VALUE_TYPE& value, // const char *message); // // Initialize a 'MyContainer' object containing the specified // // 'numElements' of the specified 'value', and write to standard // // output the specified 'message'. // // template <class INPUT_ITER> // void privateInit(INPUT_ITER first, // INPUT_ITER last, // const char *message); // // Initialize a 'MyContainer' object containing the values in the // // range starting at the specified 'first' and ending immediately // // before the specified 'last' iterators of the type 'INPUT_ITER', // // and write to standard output the specified 'message'. //.. // Note that, as in the constructors of the 'MyProblematic' class, the // 'privateInit*' methods provide display a message so we can trace the call // path. //.. // // PRIVATE MANIPULATORS // template <class VALUE_TYPE> // void MyContainer<VALUE_TYPE>::privateInit(std::size_t numElements, // const VALUE_TYPE& value, // const char *message) // { // assert(message); // printf("INIT: repeated value: %s\n", message); // // ... // } // // template <class VALUE_TYPE> // template <class INPUT_ITER> // void MyContainer<VALUE_TYPE>::privateInit(INPUT_ITER first, // INPUT_ITER last, // const char *message) // { // assert(message); // printf("INIT: range : %s\n", message); // // ... // } //.. // Now, we define two overloaded 'privateInitDispatch' methods, each taking // two parameters (the last two) which serve no run-time purpose. As we shall // see, they exist only to guide overload resolution at compile-time. //.. // template <class INTEGER_TYPE> // void privateInitDispatch(INTEGER_TYPE numElements, // INTEGER_TYPE value, // const char *message, // bslmf::MatchArithmeticType , // bslmf::Nil ); // // Initialize a 'MyContainer' object containing the specified // // 'numElements' of the specified 'value', and write to standard // // output the specified 'message'. The last two arguments are used // // only for overload resolution. // // template <class INPUT_ITER> // void privateInitDispatch(INPUT_ITER first, // INPUT_ITER last, // const char *message, // bslmf::MatchAnyType , // bslmf::MatchAnyType ); // // Initialize a 'MyContainer' object containing the values in the // // range starting at the specified 'first' and ending immediately // // before the specified 'last' iterators of the type 'INPUT_ITER', // // and write to standard output the specified 'message'. The last // // two arguments are used only for overload resolution. //.. // Notice that the first overload has strict requirements on the last two // arguments, but the second overload (accepting 'bslmf::MatchAnyType' in those // positions) will match all contexts in which the first fails to match. // // Then, we implement the two 'privateInitDispatch' overloads so that // each invokes a different overload of the 'privateInit' methods: // //: o The 'privateInit' corresponding to repeated value constructor is invoked //: from the "strict" overload of 'privateInitDispatch'. //: //: o The 'privateInit' for range construction is invoked from the other //: 'privateInitDispatch' overload. //.. // template <class VALUE_TYPE> // template <class INTEGER_TYPE> // void MyContainer<VALUE_TYPE>::privateInitDispatch( // INTEGER_TYPE numElements, // INTEGER_TYPE value, // const char *message, // bslmf::MatchArithmeticType , // bslmf::Nil ) // { // (void) message; // // privateInit(static_cast<std::size_t>(numElements), // static_cast<VALUE_TYPE>(value), // "Called via 'privateInitDispatch'."); // } // // template <class VALUE_TYPE> // template <class INPUT_ITER> // void MyContainer<VALUE_TYPE>::privateInitDispatch( // INPUT_ITER first, // INPUT_ITER last, // const char *message, // bslmf::MatchAnyType , // bslmf::MatchAnyType ) // { // privateInit(first, last, message); // } //.. // Next, we use overloaded 'privateInitDispatch' method in the range // constructor of 'MyContainer'. Note that we always supply a 'bslmf::Nil' // object (an exact type match) as the final argument, the choice of overload // will be governed according to the type of 'first'. Consequently, if 'first' // is implicitly convertible to 'bslmf::MatchArithmeticType', then the overload // leading to repeated value construction is used; otherwise, the overload // leading to range construction is used. //.. // template <class VALUE_TYPE> // template <class INPUT_ITER> // MyContainer<VALUE_TYPE>::MyContainer(INPUT_ITER first, // INPUT_ITER last, // const char *message) // { // privateInitDispatch(first, last, message, first, bslmf::Nil()); // } //.. // Notice that this design is safe for iterators that themselves happen to have // a conversion to 'int'. Such types would require two user-defined // conversions, which are disallowed by the C++ compiler, to match the // 'bslmf::MatchArithmeticType' parameter of the "strict" 'privateInitDispatch' // overload. // // Then, we implement the repeated value constructor using a direct call // to the repeated value private initializer: //.. // // CREATORS // template <class VALUE_TYPE> // MyContainer<VALUE_TYPE>::MyContainer(std::size_t numElements, // const VALUE_TYPE& value, // const char *message) // { // privateInit(numElements, value, message); // } //.. // Finally, we create three objects of 'MyContainer', using the same arguments // as we used for the three 'MyProblematicContainer' objects. //.. // const char input[] = "How now brown cow?"; // MyContainer<char> initFromPtrPair(input, // input + sizeof(input), // "Called with pointer pair."); // // MyContainer<char> initFromIntAndChar(5, // 'A', // "Called with 'int' and 'char'."); // // MyContainer<char> initFromIntAndInt(5, // 65, // 'A' // "Called with 'int' and 'int'."); //.. // Standard output shows: //.. // INIT: range : Called with pointer pair. // INIT: repeated value: Called with 'int' and 'char'. // INIT: repeated value: Called via 'privateInitDispatch'. //.. // Notice that the repeated value 'privateInit' method is called directly // for the second object, but called via 'privateInitDispatch' for the // third object. #include <bslscm_version.h> #include <bslmf_enableif.h> #include <bslmf_isarithmetic.h> #include <bslmf_isenum.h> namespace BloombergLP { namespace bslmf { // ========================= // class MatchArithmeticType // ========================= class MatchArithmeticType { // This copy-constructible mechanism can be used as a formal parameter for // functions where an arithmetic type can be confused with an iterator // type. A copy-constructible mechanism is needed so that such objects can // be used as function arguments. // // Note that if a type has a user-defined conversion to arithmetic value, // this will *not* match because passing such an object would require two // user-defined conversions. public: // CREATORS template <class t_TYPE> MatchArithmeticType( t_TYPE, typename bsl::enable_if<bsl::is_arithmetic<t_TYPE>::value || bsl::is_enum<t_TYPE>::value>::type * = 0); // IMPLICIT Conversion constructor. Does nothing. //! MatchArithmeticType(const MatchArithmeticType&) = default; // Create a 'MatchArithmeticType' object. Note that as // 'MatchArithmeticType' is an empty (stateless) type, this operation // has no observable effect. //! ~MatchArithmeticType() = default; // Destroy this object. }; // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ template <class t_TYPE> inline MatchArithmeticType::MatchArithmeticType( t_TYPE, typename bsl::enable_if<bsl::is_arithmetic<t_TYPE>::value || bsl::is_enum<t_TYPE>::value>::type *) { } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2013 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------