// bdlb_printmethods.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BDLB_PRINTMETHODS #define INCLUDED_BDLB_PRINTMETHODS #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide methods for uniform printing of value-semantic types. // //@CLASSES: // bdlb::PrintMethods: templates for uniform printing of value-semantic types // bdlb::HasPrintMethod: trait indicating existence of 'print' method // bdlb::TypeTraitHasPrintMethod: old-style version of 'bdlb::HasPrintMethod' // //@SEE_ALSO: bslalg_nestedtraitdeclaration // //@DESCRIPTION: This component provides a namespace for print utilities that // support uniform 'ostream' printing across all printable types, including // template types and containers. The 'bdlb::PrintMethods' namespace enables // clients to output the value of any printable object according to the // standard BDE 'print' protocol. If the parameterized 'TYPE' does not provide // a 'print' method, 'TYPE::operator<<' is used. Availability of a 'print' // method is determined by testing for the 'bdlb::HasPrintMethod' and // 'bdlb::TypeTraitHasPrintMethod' traits. // ///Traits Affecting Printing ///------------------------- // By default, 'bdlb::PrintMethods::print' uses the '<<' stream output operator // to print a value. This formats the entire output on one line, suppressing // all indentation. A class can override this behavior by declaring certain // traits related to printing. This component detects these traits and invokes // an appropriate print operation. The following lists the traits recognized // by this component: //.. // bdlb::HasPrintMethod ( highest precedence ) // bslalg::HasStlIterators // bslmf::IsPair ( lowest precedence ) //.. // Since a class may declare multiple traits (see the component-level // documentation of {'bslalg_nestedtraitdeclaration'} for information about // declaring traits), the relative precedence of the traits is shown above. // The next sub-sections describe these traits and their effects on printing. // ///Effect of 'bdlb::TypeTraitHasPrintMethod' Trait ///- - - - - - - - - - - - - - - - - - - - - - - - // If a class 'X' declares the 'bdlb::TypeTraitHasPrintMethod' trait, then it // must provide a 'print' method with the following signature: //.. // bsl::ostream& print(bsl::ostream& stream, // int level = 0, // int spacesPerLevel = 4) const; //.. // To output an 'X' object with this trait declared, the // 'bdlb::PrintMethods::print' method simply forwards to this method. This // means that the print operation is completely defined by the class. Ideally, // it should behave according to the standard BDE 'print' protocol that is // documented as follows: //.. // Format this object to the specified output 'stream' at the (absolute value // of) the optionally specified indentation 'level' and return a reference to // 'stream'. If 'level' is specified, optionally specify 'spacesPerLevel', // the number of spaces per indentation level for this and all of its nested // objects. If 'level' is negative, suppress indentation of the first line. // If 'spacesPerLevel' is negative, format the entire output on one line, // suppressing all but the initial indentation (as governed by 'level'). If // 'stream' is not valid on entry, this operation has no effect. //.. // ///Effect of 'bslalg::HasStlIterators' Trait ///- - - - - - - - - - - - - - - - - - - - - // If a class 'X' declares the 'bslalg::HasStlIterators' trait, then it must // provide access to iterators using the standard STL protocol. The BDE // implementation of STL declares this trait for all STL container types that // have STL iterators. Other containers that provide STL iterators should // declare this trait to get correct printing behavior. // // When an 'X' object with this trait is printed using // 'bdlb::PrintMethods::print', the contents of the object is traversed via an // iterator and the output is formatted according to the standard BDE 'print' // protocol, as documented above. Additionally, an opening '[' character is // prepended at the beginning of the output and a closing ']' character is // appended at the end of the output. Each iterated element is printed using // its own print method, and with an indentation level one higher than that of // the container. // ///Effect of 'bslmf::IsPair' Trait ///- - - - - - - - - - - - - - - - // If a class 'X' declares the 'bslmf::IsPair' trait, then the class must // contain two 'public' data members named 'first' and 'second'. The BDE // implementation of STL declares this trait for the 'bsl::pair' 'struct'. // Other classes that have 'public' 'first' and 'second' data members may // declare this trait to get printing behavior similar to that of 'bsl::pair'. // // When an 'X' object with this trait is printed using // 'bdlb::PrintMethods::print', its output is formatted based on the standard // BDE 'print' protocol, as documented above. Additionally, an opening '[' // character is prepended at the beginning of the output and a closing ']' // character is appended at the end of the output. The 'first' and 'second' // elements are printed using their own 'print' methods, and with an // indentation level one higher than that of the pair object. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Supplying a 'print' Method for a Parameterized Class ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we must create a value-semantic class that holds an object of // parameterized 'TYPE' and, per BDE convention for VSTs, provides a 'print' // method that shows the value in some human-readable format. // // First, we define the wrapper class: //.. // template <class TYPE> // class MyWrapper { // // An example wrapper class for a 'TYPE' object. // // // PRIVATE DATA MEMBERS // TYPE d_obj; // wrapped object // // public: // // TRAITS // BSLMF_NESTED_TRAIT_DECLARATION(MyWrapper, bdlb::HasPrintMethod); // // // CREATORS // MyWrapper(): d_obj() {}; // MyWrapper(const TYPE& value) : d_obj(value) { } // // ... other constructors and destructor ... // // // MANIPULATORS // // ... assignment operator, etc. ... // // // ACCESSORS // bsl::ostream& print(bsl::ostream& stream, // int level = 0, // int spacesPerLevel = 4) const; // // Format the contained 'TYPE' to the specified output 'stream' at // // the (absolute value of) the optionally specified indentation // // 'level' and return a reference to 'stream'. If 'level' is // // specified, optionally specify 'spacesPerLevel', the number of // // spaces per indentation level for this and all of its nested // // objects. If 'level' is negative, suppress indentation of the // // first line. If 'spacesPerLevel' is negative, format the entire // // output on one line, suppressing all but the initial indentation // // (as governed by 'level'). If 'stream' is not valid on entry, // // this operation has no effect. // }; //.. // Now, we implement the 'print' method of 'MyWrapper' using the // 'bdlb::PrintMethods' utility. Doing so gives us a method that produces // results both when 'TYPE' defines a 'print' method and when it does not. In // the latter case 'TYPE::operator<<' is used. //.. // template <class TYPE> // bsl::ostream& MyWrapper<TYPE>::print(bsl::ostream& stream, // int level, // int spacesPerLevel) const // { // return bdlb::PrintMethods::print(stream, d_obj, level, spacesPerLevel); // } //.. // Finally, we exercise our 'MyWrapper' class using several representative // types, starting with 'MyDate' (not shown) a class that implements a 'print' // method. //.. // static void usingMyWrapper() // { // BSLMF_ASSERT(bdlb::HasPrintMethod<MyDate>::value); // // MyDate myDate; // MyWrapper<MyDate> myWrapperForMyDate(myDate); // // BSLMF_ASSERT(!bdlb::HasPrintMethod<int>::value); // // bsl::ostringstream oss1; // myWrapperForMyDate.print(oss1); // No problem expected since // // 'bsls::TimeInterval' has a 'print' // // method. // assert("01JAN0001\n" == oss1.str()); //.. // Using an 'int' type shows how 'bdlb::PrintMethods::print' transparently // handles types that do not provide 'print' methods: //.. // int myInt = 123; // MyWrapper<int> myWrapperForInt(myInt); // // bsl::ostringstream oss2; // myWrapperForInt.print(oss2); // 'int' has no 'print' method. // // Problem? // assert("123\n" == oss2.str()); // No problem! //.. // Lastly, since 'MyWrapper' itself is a type that implements 'print' -- and // sets the 'bdlb::TypeTraitHasPrintMethod' trait -- one instance of the // 'MyWrapper' type can be wrapped by another. //.. // BSLMF_ASSERT(bdlb::HasPrintMethod<MyWrapper<int> >::value); // // MyWrapper<MyWrapper<int> > myWrappedWrapper; // // bsl::ostringstream oss3; // myWrappedWrapper.print(oss3); // assert("0\n" == oss3.str()); // } //.. // See the {'bslmf_nestedtraitdeclaration'} component for more information // about declaring traits for user-defined classes. #include <bdlscm_version.h> #include <bdlb_print.h> #include <bslalg_hasstliterators.h> #include <bslmf_ispair.h> #include <bslmf_nestedtraitdeclaration.h> #include <bslmf_selecttrait.h> #include <bsls_libraryfeatures.h> #include <bsl_iomanip.h> #include <bsl_ostream.h> #include <bsl_vector.h> #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bslalg_typetraithasstliterators.h> #include <bslalg_typetraitpair.h> #endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY #include <optional> #include <variant> #endif // BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY namespace bsl { template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR> class basic_string; } // close namespace bsl namespace BloombergLP { namespace bdlb { // ===================== // struct HasPrintMethod // ===================== template <class TYPE> struct HasPrintMethod : bslmf::DetectNestedTrait<TYPE, HasPrintMethod>::type { // A class, 'TYPE', should specialize this trait to derive from 'true_type' // if it has a 'print' method with the following signature: //.. // bsl::ostream& print(bsl::ostream& stream, // int level = 0, // int spacesPerLevel = 4) const; //.. }; // ============================== // struct TypeTraitHasPrintMethod // ============================== struct TypeTraitHasPrintMethod { // A class should declare this trait if it has a 'print' method with the // following signature: //.. // bsl::ostream& print(bsl::ostream& stream, // int level = 0, // int spacesPerLevel = 4) const; //.. template <class TYPE> struct NestedTraitDeclaration : bslmf::NestedTraitDeclaration<TYPE, HasPrintMethod> { // This class template ties the 'bdlb::TypeTraitHasPrintMethod' trait // tag to the 'bdlb::HasPrintMethod' trait metafunction. }; template <class TYPE> struct Metafunction : HasPrintMethod<TYPE>::type { }; }; // ====================== // namespace PrintMethods // ====================== namespace PrintMethods { // This 'namespace' contains parameterized 'print' methods having the // standard BDE signature for such methods. template <class TYPE> bsl::ostream& print(bsl::ostream& stream, const TYPE& object, int level = 0, int spacesPerLevel = 4); bsl::ostream& print(bsl::ostream& stream, char object, int level = 0, int spacesPerLevel = 4); bsl::ostream& print(bsl::ostream& stream, unsigned char object, int level = 0, int spacesPerLevel = 4); // Format the specified 'object' to the specified output 'stream' at the // (absolute value of) the optionally specified indentation 'level' and // return a reference to 'stream'. If 'level' is specified, optionally // specify 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, suppress // indentation of the first line. If 'spacesPerLevel' is negative, format // the entire output on one line, suppressing all but the initial // indentation (as governed by 'level'). If 'stream' is not valid on // entry, this operation has no effect. template <class CHAR_T, class CHAR_TRAITS_T, class ALLOC> bsl::ostream& print(bsl::ostream& stream, const bsl::basic_string<CHAR_T, CHAR_TRAITS_T, ALLOC>& object, int level = 0, int spacesPerLevel = 4); // Format the specified 'object' to the specified output 'stream' at the // (absolute value of) the optionally specified indentation 'level' and // return a reference to stream. Note that output will be formatted on one // line. If 'stream' is not valid on entry, this operation has no effect. template <class ALLOC> bsl::ostream& print(bsl::ostream& stream, const bsl::vector<char, ALLOC>& object, int level = 0, int spacesPerLevel = 4); // Format the specified 'object' to the specified output 'stream' at the // (absolute value of) the optionally specified indentation 'level' and // return a reference to stream. Note that output will be formatted on one // line. Also note that non-printable characters in 'object' will be // printed using their hexadecimal representation. If 'stream' is not // valid on entry, this operation has no effect. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY template <class TYPE> bsl::ostream& print(bsl::ostream& stream, const std::optional<TYPE>& object, int level = 0, int spacesPerLevel = 4); // Format the specified 'object' to the specified output 'stream' at the // (absolute value of) the optionally specified indentation 'level' and // return a reference to 'stream'. If 'level' is specified, optionally // specify 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, suppress // indentation of the first line. If 'spacesPerLevel' is negative, format // the entire output on one line, suppressing all but the initial // indentation (as governed by 'level'). If 'stream' is not valid on // entry, this operation has no effect. A descriptive, human-readable // message, indented according to 'level' and 'spacesPerLevel', is output // for objects having no value (i.e., 'false == object.has_value()'). template <class ... TYPE> bsl::ostream& print(bsl::ostream& stream, const std::variant<TYPE ...>& object, int level = 0, int spacesPerLevel = 4); // Format the specified 'object' to the specified output 'stream' at the // (absolute value of) the optionally specified indentation 'level' and // return a reference to 'stream'. If 'level' is specified, optionally // specify 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, suppress // indentation of the first line. If 'spacesPerLevel' is negative, format // the entire output on one line, suppressing all but the initial // indentation (as governed by 'level'). If 'stream' is not valid on // entry, this operation has no effect. A descriptive, human-readable // message, indented according to 'level' and 'spacesPerLevel', is output // for objects holding the value of type 'std::monostate'. Note that a // 'std::variant' object can hold the 'std::monostate' value only if its // template parameters explicitly mention the 'std::monostate' type. bsl::ostream& print(bsl::ostream& stream, const std::monostate& object, int level = 0, int spacesPerLevel = 4); // Format the specified 'object' to the specified output 'stream' at the // (absolute value of) the optionally specified indentation 'level' and // return a reference to 'stream'. If 'level' is specified, optionally // specify 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, suppress // indentation of the first line. If 'spacesPerLevel' is negative, format // the entire output on one line, suppressing all but the initial // indentation (as governed by 'level'). If 'stream' is not valid on // entry, this operation has no effect. As all objects of this type have // the same value, 'object' is *ignored* and a descriptive, human-readable // message is output for all objects of this type. #endif // BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY } // close namespace PrintMethods } // close package namespace // ============================================================================ // INLINE DEFINITIONS // ============================================================================ namespace bdlb { // -------------------------------------------- // struct bdlb::PrintMethods_Imp<TYPE, SELECTOR> // -------------------------------------------- template <class TYPE, class SELECTOR> struct PrintMethods_Imp; template <class TYPE> struct PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<> > { // Component-private 'struct'. Do not use outside of this component. This // 'struct' provides a 'print' function that prints objects of // parameterized 'TYPE' that do not declare any of the traits recognized by // this component. // CLASS METHODS static bsl::ostream& print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel); // Print the specified 'object' to the specified 'stream' using its // '<<' output stream operator. Note that a compiler error will result // if the specified 'TYPE' does not have a '<<' output stream operator. }; // ---------------------------------------------------- // struct PrintMethods_Imp<TYPE, HasPrintMethod<TYPE> > // ---------------------------------------------------- template <class TYPE> struct PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<HasPrintMethod> > { // Component-private 'struct'. Do not use outside of this component. This // 'struct' provides a 'print' function that prints objects of the // parameterized 'TYPE' that are associated with the 'HasPrintMethod' // trait. // CLASS METHODS static bsl::ostream& print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel); }; // ------------------------------------------------------------- // struct PrintMethods_Imp<TYPE, bslalg::HasStlIterators<TYPE> > // ------------------------------------------------------------- template <class TYPE> struct PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<bslalg::HasStlIterators> > { // Component-private 'struct'. Do not use outside of this component. This // 'struct' provides a 'print' function that prints objects of the // parameterized 'TYPE' that have the 'bslalg::HasStlIterators' trait // declared. // CLASS METHODS static bsl::ostream& print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel); }; // --------------------------------------------------- // struct PrintMethods_Imp<TYPE, bslmf::IsPair<TYPE> > // --------------------------------------------------- template <class TYPE> struct PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<bslmf::IsPair> > { // Component-private 'struct'. Do not use outside of this component. This // 'struct' provides a 'print' function that prints objects of // parameterized 'TYPE' that declare the 'bslmf::IsPair' trait. // CLASS METHODS static bsl::ostream& print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel); }; // ============================================================================ // TEMPLATE AND INLINE FUNCTION DEFINITIONS // ============================================================================ // --------------------------------------- // struct PrintMethods_Imp<TYPE, SELECTOR> // --------------------------------------- // CLASS METHODS template <class TYPE> bsl::ostream& PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<> >::print( bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel) { if (stream.bad()) { return stream; // RETURN } Print::indent(stream, level, spacesPerLevel); // A compilation error indicating the next line of code implies the 'TYPE' // parameter does not have the '<<' output stream operator. stream << object; if (0 <= spacesPerLevel) { stream << '\n'; } return stream; } // ---------------------------------------------------- // struct PrintMethods_Imp<TYPE, HasPrintMethod<TYPE> > // ---------------------------------------------------- // CLASS METHODS template <class TYPE> inline bsl::ostream& PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<HasPrintMethod> >:: print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel) { // A compilation error indicating the next line of code implies the 'TYPE' // parameter does not have a 'print' method with the expected signature. return object.print(stream, level, spacesPerLevel); } // ------------------------------------------------------------- // struct PrintMethods_Imp<TYPE, bslalg::HasStlIterators<TYPE> > // ------------------------------------------------------------- // CLASS METHODS template <class TYPE> bsl::ostream& PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<bslalg::HasStlIterators> >::print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel) { if (stream.bad()) { return stream; // RETURN } Print::indent(stream, level, spacesPerLevel); // A compilation error indicating the next line of code implies the 'TYPE' // parameter does not have STL-compliant iterators. typedef typename TYPE::const_iterator Iterator; if (0 <= spacesPerLevel) { // Multi-line output. if (level < 0) { level = -level; } stream << "[\n"; const int levelPlus1 = level + 1; for (Iterator it = object.begin(); it != object.end(); ++it) { PrintMethods::print(stream, *it, levelPlus1, spacesPerLevel); } Print::indent(stream, level, spacesPerLevel); stream << "]\n"; } else { // Output on a single line and suppress any further indentation. stream << "[ "; for (Iterator it = object.begin(); it != object.end(); ++it) { PrintMethods::print(stream, *it, 0, -1); stream << ' '; } stream << ']'; } return stream << bsl::flush; } // --------------------------------------------------- // struct PrintMethods_Imp<TYPE, bslmf::IsPair<TYPE> > // --------------------------------------------------- // CLASS METHODS template <class TYPE> bsl::ostream& PrintMethods_Imp<TYPE, bslmf::SelectTraitCase<bslmf::IsPair> >:: print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel) { if (stream.bad()) { return stream; // RETURN } Print::indent(stream, level, spacesPerLevel); if (0 <= spacesPerLevel) { // Multi-line output. if (level < 0) { level = -level; } stream << "[\n"; const int levelPlus1 = level + 1; // A compilation error indicating the next line of code implies the // 'TYPE' parameter is not a pair. PrintMethods::print(stream, object.first, levelPlus1, spacesPerLevel); PrintMethods::print(stream, object.second, levelPlus1, spacesPerLevel); Print::indent(stream, level, spacesPerLevel); stream << "]\n"; } else { // Output on a single line and suppress any further indentation. stream << "[ "; // A compilation error indicating the next line of code implies the // 'TYPE' parameter is not a pair. PrintMethods::print(stream, object.first, 0, -1); stream << ' '; PrintMethods::print(stream, object.second, 0, -1); stream << " ]"; } return stream << bsl::flush; } // ---------------------- // namespace PrintMethods // ---------------------- // CLASS METHODS template <class TYPE> bsl::ostream& PrintMethods::print(bsl::ostream& stream, const TYPE& object, int level, int spacesPerLevel) { typedef typename bslmf::SelectTrait<TYPE, HasPrintMethod, bslalg::HasStlIterators, bslmf::IsPair>::Type BdlbSelector; return PrintMethods_Imp<TYPE, BdlbSelector>::print(stream, object, level, spacesPerLevel); } template <class CHAR_T, class CHAR_TRAITS_T, class ALLOC> bsl::ostream& PrintMethods::print( bsl::ostream& stream, const bsl::basic_string<CHAR_T, CHAR_TRAITS_T, ALLOC>& object, int level, int spacesPerLevel) { return PrintMethods_Imp<bsl::basic_string<CHAR_T, CHAR_TRAITS_T, ALLOC>, bslmf::SelectTraitCase<> >::print(stream, object, level, spacesPerLevel); } template <class ALLOC> bsl::ostream& PrintMethods::print(bsl::ostream& stream, const bsl::vector<char, ALLOC>& object, int level, int spacesPerLevel) { if (stream.bad()) { return stream; // RETURN } Print::indent(stream, level, spacesPerLevel); stream << "\""; const int len = static_cast<int>(object.size()); if (0 < len) { Print::printString(stream, &object[0], len, false); } stream << "\""; if (0 <= spacesPerLevel) { stream << '\n'; } return stream; } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY template <class TYPE> bsl::ostream& PrintMethods::print(bsl::ostream& stream, const std::optional<TYPE>& object, int level, int spacesPerLevel) { if (object.has_value()) { return PrintMethods::print(stream, object.value(), level, spacesPerLevel); // RETURN } else { return PrintMethods::print(stream, "EMPTY", level, spacesPerLevel); // RETURN } } template <class ... TYPE> bsl::ostream& PrintMethods::print(bsl::ostream& stream, const std::variant<TYPE ...>& object, int level, int spacesPerLevel) { const auto lambda = [&](const auto& x) -> bsl::ostream& { return PrintMethods::print(stream, x, level, spacesPerLevel); }; return std::visit(lambda, object); } inline bsl::ostream& PrintMethods::print(bsl::ostream& stream, const std::monostate& , int level, int spacesPerLevel) { return PrintMethods::print(stream, "MONOSTATE", level, spacesPerLevel); // RETURN } #endif // BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 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 ----------------------------------