From 337a4ceb4ea39ea9c02f6a61a5a9a24826367879 Mon Sep 17 00:00:00 2001 From: Attila Feher Date: Wed, 15 May 2024 17:33:37 -0400 Subject: [PATCH] bdld_DatumUtil::[safe]{T|t}ypedPrint (#4718) --- groups/bdl/bdlb/bdlb_indexspanutil.h | 22 +- groups/bdl/bdlb/bdlb_indexspanutil.t.cpp | 20 +- groups/bdl/bdld/bdld_datum.cpp | 16 +- groups/bdl/bdld/bdld_datummaker.h | 11 + groups/bdl/bdld/bdld_datummaker.t.cpp | 241 ++-- groups/bdl/bdld/bdld_datumutil.cpp | 667 +++++++++++ groups/bdl/bdld/bdld_datumutil.h | 211 ++++ groups/bdl/bdld/bdld_datumutil.t.cpp | 1014 +++++++++++++++++ groups/bdl/bdld/doc/bdld.txt | 6 +- groups/bdl/bdld/package/bdld.mem | 1 + groups/bdl/bdlt/bdlt_epochutil.t.cpp | 22 +- groups/bsl/bslim/bslim_printer.h | 2 +- .../bslmt/bslmt_readerwriterlockassert.t.cpp | 14 +- groups/bsl/bslmt/bslmt_rwmutex.t.cpp | 6 +- groups/bsl/bslmt/bslmt_testutil.t.cpp | 16 +- groups/bsl/bslmt/bslmt_threadattributes.t.cpp | 12 +- groups/bsl/bslmt/bslmt_threadutil.t.cpp | 15 + groups/bsl/bslmt/bslmt_turnstile.t.cpp | 2 +- groups/bsl/bslx/bslx_typecode.t.cpp | 8 +- 19 files changed, 2115 insertions(+), 191 deletions(-) create mode 100644 groups/bdl/bdld/bdld_datumutil.cpp create mode 100644 groups/bdl/bdld/bdld_datumutil.h create mode 100644 groups/bdl/bdld/bdld_datumutil.t.cpp diff --git a/groups/bdl/bdlb/bdlb_indexspanutil.h b/groups/bdl/bdlb/bdlb_indexspanutil.h index 8f0274bc9a..a5e2eb1655 100644 --- a/groups/bdl/bdlb/bdlb_indexspanutil.h +++ b/groups/bdl/bdlb/bdlb_indexspanutil.h @@ -23,17 +23,17 @@ BSLS_IDENT("$Id: $") // ///Example1: Taking a IPv6 address out of a URI /// - - - - - - - - - - - - - - - - - - - - - - -// Suppose we a class that stores a parsed URL using a string to store the full -// URL and 'IndexSpan' objects to describe the individual parts of the URL, and -// we want to add accessors that handle the case when the host part of the URL -// is an IPv6 address, such as "http://[ff:fe:9]/index.html". As observed, an -// IPv6 address is indicated by the '[' and ']' characters (the URL is ill -// formed if the closing ']' is not present). We want to implement two -// methods, one to query if the host part of the URL is IPv6 ('isIPv6Host') and -// another to get the IPv6 address (the part without the square brackets) if -// the host is actually an IPv6 address ('getIPv6Host'). +// Suppose we have a class that stores a parsed URL using a string to store the +// full URL and 'IndexSpan' objects to describe the individual parts of the +// URL, and we want to add accessors that handle the case when the host part of +// the URL is an IPv6 address, such as "http://[ff:fe:9]/index.html". As +// observed, an IPv6 address is indicated by the '[' and ']' characters (the +// URL is ill formed if the closing ']' is not present). We want to implement +// two methods, one to query if the host part of the URL is IPv6 ('isIPv6Host') +// and another to get the IPv6 address (the part without the square brackets) +// if the host is actually an IPv6 address ('getIPv6Host'). // -// First let us create a 'ParsedUrl' class. For brevity, the class has only +// First, let us create a 'ParsedUrl' class. For brevity, the class has only // those parts that are needed to implement 'isIPv6Host' and 'getIPv6Host'. //.. // class ParsedUrl { @@ -119,7 +119,7 @@ namespace bdlb { struct IndexSpanUtil { // This struct serves as a namespace for utility functions that operate on - // 'IndexSpan'objects. + // 'IndexSpan' objects. public: // CLASS METHODS static IndexSpan shrink(const IndexSpan& original, diff --git a/groups/bdl/bdlb/bdlb_indexspanutil.t.cpp b/groups/bdl/bdlb/bdlb_indexspanutil.t.cpp index 8788aefd5c..f07145dbda 100644 --- a/groups/bdl/bdlb/bdlb_indexspanutil.t.cpp +++ b/groups/bdl/bdlb/bdlb_indexspanutil.t.cpp @@ -100,17 +100,17 @@ typedef bdlb::IndexSpanUtil Util; // ///Example1: Taking a IPv6 address out of a URI /// - - - - - - - - - - - - - - - - - - - - - - -// Suppose we a class that stores a parsed URL using a string to store the full -// URL and 'IndexSpan' objects to describe the individual parts of the URL and. -// we want to add accessors that handle the case when the host part of the URL -// is an IPv6 address, such as "http://[ff:fe:9]/index.html". As observed, an -// IPv6 address is indicated by the '[' and ']' characters (the URL is ill -// formed if the closing ']' is not present). We want to implement two -// methods, one to query if the host part of the URL is IPv6 ('isIPv6Host') and -// another to get the IPv6 address (the part without the square brackets) if -// the host is actually an IPv6 address ('getIPv6Host'). +// Suppose we have a class that stores a parsed URL using a string to store the +// full URL and 'IndexSpan' objects to describe the individual parts of the +// URL, and we want to add accessors that handle the case when the host part of +// the URL is an IPv6 address, such as "http://[ff:fe:9]/index.html". As +// observed, an IPv6 address is indicated by the '[' and ']' characters (the +// URL is ill formed if the closing ']' is not present). We want to implement +// two methods, one to query if the host part of the URL is IPv6 ('isIPv6Host') +// and another to get the IPv6 address (the part without the square brackets) +// if the host is actually an IPv6 address ('getIPv6Host'). // -// First let us create a 'ParsedUrl' class. For brevity, the class has only +// First, let us create a 'ParsedUrl' class. For brevity, the class has only // those parts that are needed to implement 'isIPv6Host' and 'getIPv6Host'. //.. class ParsedUrl { diff --git a/groups/bdl/bdld/bdld_datum.cpp b/groups/bdl/bdld/bdld_datum.cpp index 377d503b86..0bc38a48e4 100644 --- a/groups/bdl/bdld/bdld_datum.cpp +++ b/groups/bdl/bdld/bdld_datum.cpp @@ -189,6 +189,7 @@ static const Datum *findElementLinear(const bslstl::StringRef& key, // ======================== // class Datum_ArrayProctor // ======================== + template class Datum_ArrayProctor { // This component-local mechanism class provides a specialized proctor @@ -460,7 +461,7 @@ Datum_CopyVisitor::Datum_CopyVisitor(Datum *result, // MANIPULATORS void Datum_CopyVisitor::operator()(bslmf::Nil value) { - (void) value; + (void)value; *d_result_p = Datum::createNull(); } @@ -588,7 +589,6 @@ class Datum_StreamVisitor { template void operator()(const BDLD_TYPE& value) const; // Write the specified 'value' into 'd_stream'. - }; // ------------------------- @@ -607,7 +607,7 @@ Datum_StreamVisitor::Datum_StreamVisitor(bsl::ostream& stream, // MANIPULATORS void Datum_StreamVisitor::operator()(bslmf::Nil value) const { - (void) value; + (void)value; if (!d_stream.bad()) { bdlb::Print::indent(d_stream, d_level, d_spacesPerLevel); d_stream << "nil"; @@ -647,6 +647,10 @@ void Datum_StreamVisitor::operator()(const BDLD_TYPE& value) const } } +// ============================================================================ +// Utility Functions +// ---------------------------------------------------------------------------- + bool compareIntLess(const DatumIntMapEntry& lhs, const DatumIntMapEntry& rhs) { return lhs.key() < rhs.key(); @@ -820,6 +824,10 @@ const Datum *findElementLinear(const bslstl::StringRef& key, } // close unnamed namespace +// ============================================================================ +// Implementation +// ---------------------------------------------------------------------------- + namespace BloombergLP { namespace bdld { @@ -883,7 +891,7 @@ Datum Datum::createDecimal64(bdldfp::Decimal64 value, #else // end - 32 bit / begin - 64 bit // Avoidance of compiler warning. We don't need to allocate memory to // store Decimal64 values on 64-bit platform. - (void) basicAllocator; + (void)basicAllocator; result.d_as.d_type = e_INTERNAL_DECIMAL64; new (result.theInlineStorage()) bdldfp::Decimal64(value); diff --git a/groups/bdl/bdld/bdld_datummaker.h b/groups/bdl/bdld/bdld_datummaker.h index 8e4235b2cb..76972b4f9b 100644 --- a/groups/bdl/bdld/bdld_datummaker.h +++ b/groups/bdl/bdld/bdld_datummaker.h @@ -2356,6 +2356,10 @@ class DatumMaker { // Return a 'bdld::Datum' having the specified 'value', or null if // 'value' is unset. + bdld::Datum bin(const void *pointer, bsl::size_t size) const; + // Return a binary 'bdld::Datum' having a value that is the copy of the + // memory area described by the specified 'pointer' and 'size'. + #if !BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES template bdld::Datum a(const ELEMENTS&... elements) const; @@ -7758,6 +7762,13 @@ bdld::Datum DatumMaker::operator()( return value.isNull() ? (*this)() : (*this)(value.value()); } + +inline +bdld::Datum DatumMaker::bin(const void *pointer, bsl::size_t size) const +{ + return bdld::Datum::copyBinary(pointer, size, d_allocator_p); +} + #if !BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES template inline diff --git a/groups/bdl/bdld/bdld_datummaker.t.cpp b/groups/bdl/bdld/bdld_datummaker.t.cpp index 570362a707..cb55357b03 100644 --- a/groups/bdl/bdld/bdld_datummaker.t.cpp +++ b/groups/bdl/bdld/bdld_datummaker.t.cpp @@ -5,8 +5,6 @@ #include -#include -#include #include #include // to verify that we do not #include // allocate any memory @@ -15,6 +13,11 @@ #include #include +#include +#include + +#include // for 'memcmp' + using namespace BloombergLP; using namespace bsl; @@ -58,12 +61,11 @@ using namespace bsl; // [ ] TBD // ---------------------------------------------------------------------------- // [ 1] BREATHING TEST -// [ ] USAGE EXAMPLE +// [ 9] USAGE EXAMPLE // [ *] CONCERN: DOES NOT ALLOCATE MEMORY // [ ] TEST APPARATUS: TBD // [ 5] PRINTING: TBD - // ============================================================================ // STANDARD BSL ASSERT TEST FUNCTION // ---------------------------------------------------------------------------- @@ -107,30 +109,6 @@ void aSsErT(bool condition, const char *message, int line) #define T_ BSLS_BSLTESTUTIL_T_ // Print a tab (w/o newline). #define L_ BSLS_BSLTESTUTIL_L_ // current Line number -// ============================================================================ -// NEGATIVE-TEST MACRO ABBREVIATIONS -// ---------------------------------------------------------------------------- - -#define ASSERT_SAFE_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_PASS(EXPR) -#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR) -#define ASSERT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_PASS(EXPR) -#define ASSERT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_FAIL(EXPR) -#define ASSERT_OPT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_PASS(EXPR) -#define ASSERT_OPT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR) - -#define ASSERT_SAFE_PASS_RAW(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_PASS_RAW(EXPR) -#define ASSERT_SAFE_FAIL_RAW(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL_RAW(EXPR) -#define ASSERT_PASS_RAW(EXPR) BSLS_ASSERTTEST_ASSERT_PASS_RAW(EXPR) -#define ASSERT_FAIL_RAW(EXPR) BSLS_ASSERTTEST_ASSERT_FAIL_RAW(EXPR) -#define ASSERT_OPT_PASS_RAW(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_PASS_RAW(EXPR) -#define ASSERT_OPT_FAIL_RAW(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_FAIL_RAW(EXPR) - -// ============================================================================ -// PRINTF FORMAT MACRO ABBREVIATIONS -// ---------------------------------------------------------------------------- - -#define ZU BSLS_BSLTESTUTIL_FORMAT_ZU - //============================================================================= // GLOBAL TYPEDEFS/CONSTANTS FOR TESTING //----------------------------------------------------------------------------- @@ -166,7 +144,6 @@ bdld::Datum numCount(const bdld::Datum arrray) } //.. - // ============================================================================ // MAIN PROGRAM // ---------------------------------------------------------------------------- @@ -197,12 +174,117 @@ int main(int argc, char *argv[]) bslma::TestAllocatorMonitor gam(&ga), dam(&da); switch (test) { case 0: + case 9: { + // -------------------------------------------------------------------- + // USAGE EXAMPLE + // + // Concerns: + //: 1 The usage example provided in the component header file compiles, + //: links, and runs as shown. + // + // Plan: + //: 1 Incorporate usage example from header into test driver, remove + //: leading comment characters, and replace 'assert' with 'ASSERT'. + // + // Testing: + // USAGE EXAMPLE + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "USAGE EXAMPLE" << endl + << "=============" << endl; + + bdlma::LocalSequentialAllocator<64> sa(&ta); + +///Example 1: Testing of a function +/// - - - - - - - - - - - - - - - - +// Suppose we want to test a function, 'numCount', that returns the number of +// numeric elements in a 'bdld::Datum' array. +// +// First we implement the function. See function implementation of 'numCount' +// in the section 'USAGE EXAMPLE' above! +// +// Then, within the test driver for 'numCount', we define a 'bdld::DatumMaker', +// and use it to initialize an array to test 'numCount': +//.. + bdld::DatumMaker m(&sa); +//.. +// Here, we create the array we want to use as an argument to 'numCount': +//.. + bdld::Datum array = m.a( + m(), + m(bdld::DatumError(-1)), + m.a( + m(true), + m(false)), + m(42.0), + m(false), + m(0), + m(true), + m(bsls::Types::Int64(424242)), + m.m( + "firstName", "Bart", + "lastName", "Simpson", + "age", 10 + ), + m(bdlt::Date(2016, 10, 14)), + m(bdlt::Time(13, 00, 00, 000)), + m(bdlt::Datetime(2016, 10, 14, 13, 01, 30, 87)), + m(bdlt::DatetimeInterval(280, 13, 41, 12, 321)), + m("foobar") + ); +//.. +// Next we call the function with the array-'Datum' as its first argument: +//.. + bdld::Datum retVal = numCount(array); +//.. +// Finally we verify the return value: +//.. + ASSERT(retVal.theInteger() == 3); +//.. + } break; case 8: { + // -------------------------------------------------------------------- + // BIN TEST + // + // Concerns: + //: 1 The 'bin' method creates binary 'Datum' content. + //: 2 The binary 'Datum' content is a *copy* of the original. + // + // Plan: + //: 1 Create a binary test pattern to store. + //: 2 Use 'bin' to create a 'Datum'. + //: 3 Verify that length is same as the original. + //: 4 Verify that the pointer differs from the original. + //: 5 Verify that the all bytes are the same as the original. + // + // Testing: + // bdld::Datum bin(const void *ptr, size_t len) const; + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "BIN TEST" << endl + << "========" << endl; + + bdlma::LocalSequentialAllocator<64> sa(&ta); + bdld::DatumMaker dm(&sa); + + static const unsigned char BIN_DATA[] = + "\xDE\xCA\xFB\xAD\0\x1F\xAD\xED\x10\xAD\xC0\xFF\xEE"; + const size_t BIN_SIZE = (sizeof BIN_DATA) - 1; // No closing null + + const bdld::Datum DATUM = dm.bin(BIN_DATA, BIN_SIZE); + + ASSERT(DATUM.isBinary()); + ASSERT(DATUM.theBinary().size() == BIN_SIZE); + ASSERT(DATUM.theBinary().data() != BIN_DATA); + ASSERT(0 == memcmp(DATUM.theBinary().data(), BIN_DATA, BIN_SIZE)); + } break; + case 7: { // -------------------------------------------------------------------- // REF TEST // // Concerns: - //: //: 1 The 'ref' method accepts different common types for representing //: string content and produces a 'Datum' that presents itself as a //: string, yet refers to the contents given as an argument. @@ -214,10 +296,8 @@ int main(int argc, char *argv[]) //: integer. On 32bit platforms the 'Datum' will need to allocate a //: string reference to accommodate for a count with more than 16bit. //: Check that the allocator of the 'DatumMaker' is used for that. - //: // // Plan: - //: //: 1 Given a test string, present it as an argument to the function //: 'ref' under test using different argument types. For each one //: check that the 'StringRef' obtained from the result points to the @@ -229,7 +309,6 @@ int main(int argc, char *argv[]) //: with the allocator will end up with an in-use count of zero. //: That check should work regardless of whether an allocation was //: necessary or not. - //: // // Testing: // bdld::Datum ref(const bslstl::StringRef&) const; @@ -296,78 +375,6 @@ int main(int argc, char *argv[]) ASSERT(localTa.numBlocksInUse() == 0); } } break; - - case 7: { - // -------------------------------------------------------------------- - // USAGE EXAMPLE - // - // Concerns: - //:1 The usage example provided in the component header file compiles, - // links, and runs as shown. - // - // Plan: - //:1 Incorporate usage example from header into test driver, remove - // leading comment characters, and replace 'assert' with 'ASSERT'. - // - // Testing: - // USAGE EXAMPLE - // -------------------------------------------------------------------- - - if (verbose) cout << endl - << "USAGE EXAMPLE" << endl - << "=============" << endl; - - bdlma::LocalSequentialAllocator<64> sa(&ta); - -///Example 1: Testing of a function -/// - - - - - - - - - - - - - - - - -// Suppose we want to test a function, 'numCount', that returns the number of -// numeric elements in a 'bdld::Datum' array. -// -// First we implement the function. See function implementation of 'numCount' -// in the section 'USAGE EXAMPLE' above! -// -// Then, within the test driver for 'numCount', we define a 'bdld::DatumMaker', -// and use it to initialize an array to test 'numCount': -//.. - bdld::DatumMaker m(&sa); -//.. -// Here, we create the array we want to use as an argument to 'numCount': -//.. - bdld::Datum array = m.a( - m(), - m(bdld::DatumError(-1)), - m.a( - m(true), - m(false)), - m(42.0), - m(false), - m(0), - m(true), - m(bsls::Types::Int64(424242)), - m.m( - "firstName", "Bart", - "lastName", "Simpson", - "age", 10 - ), - m(bdlt::Date(2016, 10, 14)), - m(bdlt::Time(13, 00, 00, 000)), - m(bdlt::Datetime(2016, 10, 14, 13, 01, 30, 87)), - m(bdlt::DatetimeInterval(280, 13, 41, 12, 321)), - m("foobar") - ); -//.. -// Next we call the function with the array-'Datum' as its first argument: -//.. - bdld::Datum retVal = numCount(array); -//.. -// Finally we verify the return value: -//.. - ASSERT(retVal.theInteger() == 3); -//.. - - } break; - case 6: { //--------------------------------------------------------------------- // INTEGER-MAP TESTS: @@ -375,7 +382,6 @@ int main(int argc, char *argv[]) // functions. // // Concerns: - //: //: 1 Integer-map constructors create 'bdld::Datum' integer-maps. //: //: 2 At least 16 entries are supported on compilers not providing @@ -388,7 +394,6 @@ int main(int argc, char *argv[]) //: constructor. // // Plan: - //: //: 1 Use 'DatumMaker' to create integer-maps. //: //: 2 Use 'bdld::Datum::create*' functions to create oracle maps. @@ -604,9 +609,7 @@ int main(int argc, char *argv[]) 36, -1, 38, -1)); #endif - } break; - case 5: { //--------------------------------------------------------------------- // OWNED KEY TESTS: @@ -614,11 +617,9 @@ int main(int argc, char *argv[]) // verifies that the keys are really copied. // // Concerns: - //: //: 1 The keys are copied into the 'Datum' map.. // // Plan: - //: //: 1 Create destroyable strings for the keys. //: //: 2 Create a key-owning map with the 'DatumMaker'. @@ -658,9 +659,7 @@ int main(int argc, char *argv[]) *value == bdld::Datum::createInteger(42)); bdld::Datum::destroy(d, &ta); - } break; - case 4: { //--------------------------------------------------------------------- // ALLOCATOR TESTS: @@ -669,14 +668,12 @@ int main(int argc, char *argv[]) // memory for the 'Datum's created. // // Concerns: - //: //: 1 The allocator specified at construction time is used to allocate //: memory for the 'Datum's created. //: //: 2 Non-allocating members do not allocate. // // Plan: - //: //: 1 Supply a 'TestAllocator' at creation to the 'DatumMaker'. //: //: 2 Use 'TestAllocatorMonitor' to determine the number of allocations @@ -884,9 +881,7 @@ int main(int argc, char *argv[]) ASSERT(dam.isInUseSame()); ASSERT(tam.isInUseSame()); } - } break; - case 3: { //--------------------------------------------------------------------- // MAP TESTS: @@ -894,7 +889,6 @@ int main(int argc, char *argv[]) // owned keys ('m' and 'mok' functions respectively). // // Concerns: - //: //: 1 Map constructors create 'bdld::Datum' maps. //: //: 2 At least 16 entries are supported on compilers not providing @@ -907,7 +901,6 @@ int main(int argc, char *argv[]) //: constructor. // // Plan: - //: //: 1 Use 'DatumMaker' to create maps. //: //: 2 Use 'bdld::Datum::create*' functions to create oracle maps. @@ -1286,16 +1279,13 @@ int main(int argc, char *argv[]) "z", -1, "w", -1)); #endif - } break; - case 2: { //--------------------------------------------------------------------- // ARRAY TESTS: // This case exercises the array constructors. // // Concerns: - //: //: 1 Array constructors create 'bdld::Datum' arrays. //: //: 2 At least 16 elements are supported on compilers not providing @@ -1308,7 +1298,6 @@ int main(int argc, char *argv[]) //: constructor. // // Plan: - //: //: 1 Use 'DatumMaker' to create arrays. //: //: 2 Use 'bdld::Datum::create*' functions to create oracle arrays. @@ -1385,18 +1374,15 @@ int main(int argc, char *argv[]) -1, -1, -1, -1, -1, -1, -1, -1, -1)); #endif } break; - case 1: { //--------------------------------------------------------------------- // SCALAR TESTS: // This case exercises the scalar constructors. // // Concerns: - //: //: 1 Scalar constructors create the required type of 'bdld::Datum'. // // Plan: - //: //: 1 Use 'DatumMaker' to create scalars. //: //: 2 Use 'bdld::Datum::create*' functions to create oracle scalars. @@ -1470,10 +1456,7 @@ int main(int argc, char *argv[]) ASSERT(bdld::Datum::createNull() == m(TriBool())); ASSERT(bdld::Datum::createBoolean(true) == m(TriBool(true))); ASSERT(bdld::Datum::createBoolean(false) == m(TriBool(false))); - - } break; - default: { cerr << "WARNING: CASE '" << test << "' NOT FOUND." << endl; testStatus = -1; diff --git a/groups/bdl/bdld/bdld_datumutil.cpp b/groups/bdl/bdld/bdld_datumutil.cpp new file mode 100644 index 0000000000..39760a077b --- /dev/null +++ b/groups/bdl/bdld/bdld_datumutil.cpp @@ -0,0 +1,667 @@ +// bdld_datumutil.cpp -*-C++-*- +#include + +#include +BSLS_IDENT_RCSID(RCSid_bdld_datumutil_cpp, "$Id$ $CSID$") + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +namespace BloombergLP { +namespace bdld { + +// ============================================================================ +// Utility Functions +// ---------------------------------------------------------------------------- + +namespace { + +static +void safeTypedPrintIntMapEntry( + bsl::ostream& outputStream, + bsl::unordered_set& seenSet, + const DatumIntMapEntry& intMapEntry, + int level, + int spacesPerLevel); + +static +void safeTypedPrintMapEntry(bsl::ostream& outputStream, + bsl::unordered_set& seenSet, + const DatumMapEntry& mapEntry, + int level, + int spacesPerLevel); + +static +void safeTypedPrintImpl(bsl::ostream& outputStream, + bsl::unordered_set& seenSet, + const Datum& object, + int level, + int spacesPerLevel) +{ + if (outputStream.bad()) { + return; // RETURN + } + + const void *ptr = 0; + bsl::string_view cycleOutput1; + bsl::string_view cycleOutput2; + switch (object.type()) { + case Datum::e_ARRAY: { + ptr = object.theArray().data(); + cycleOutput1 = "= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + outputStream << cycleOutput1 << ptr << cycleOutput2; + if (0 <= spacesPerLevel) { // Multi-line output. + outputStream << '\n'; + } + return; // RETURN + } + } + else { + // These types are scalar, do not refer to anything + DatumUtil::typedPrint(outputStream, object, level, spacesPerLevel); + return; // RETURN + } + + // When here, we need to print the "dangerous" types that may form cycles. + // We need to fully implement the printing of those using functions that + // are aware of already visited nodes. + + if (level >= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + + switch (object.type()) { + case Datum::e_ARRAY: { + const DatumArrayRef aRef = object.theArray(); + outputStream << " 0) { + outputStream << ", "; + } + safeTypedPrintImpl(outputStream, seenSet, aRef[i], 0, -1); + } + outputStream << ' '; + } + outputStream << "]>"; + } break; + + case Datum::e_INT_MAP: { + const DatumIntMapRef imRef = object.theIntMap(); + outputStream << " 0) { + outputStream << ", "; + } + safeTypedPrintIntMapEntry(outputStream, + seenSet, + imRef[i], + 0, + -1); + } + outputStream << ' '; + } + outputStream << "}>"; + } break; + + case Datum::e_MAP: { + const DatumMapRef mRef = object.theMap(); + outputStream << " 0) { + outputStream << ", "; + } + safeTypedPrintMapEntry(outputStream, seenSet, mRef[i], 0, -1); + } + outputStream << ' '; + } + outputStream << "}>"; + } break; + + default: { + // Nothing to do for other types + } break; + } + + if (0 <= spacesPerLevel) { + // Multi-line output. + outputStream << '\n'; + } + + seenSet.erase(ptr); +} + +static +void dumpInterval(bsl::ostream& outputStream, + const bdlt::DatetimeInterval& dtInterval) +{ + bsl::string_view padding; + + if (dtInterval == bdlt::DatetimeInterval(0)) { // Zero duration + outputStream << "0s"; + return; // RETURN + } + + if (dtInterval.days() > 0) { + outputStream << padding << dtInterval.days() << 'd'; + padding = " "; + } + + if (dtInterval.hours() > 0) { + outputStream << padding << dtInterval.hours() << 'h'; + padding = " "; + } + + if (dtInterval.minutes() > 0) { + outputStream << padding << dtInterval.minutes() << 'm'; + padding = " "; + } + + if (dtInterval.seconds() > 0) { + outputStream << padding << dtInterval.seconds(); + padding = " "; + } + + const int usec = dtInterval.milliseconds() * 1000 + + dtInterval.microseconds(); + + if (usec > 0) { + if (dtInterval.seconds() == 0) { + outputStream << padding << "0"; + } + outputStream << '.'; + const char old = outputStream.fill('0'); + outputStream.width(6); + outputStream << usec; + outputStream.fill(old); + } + + if (dtInterval.seconds() > 0 || usec > 0) { + outputStream << 's'; + } +} + +static +void typedPrintIntMapEntry(bsl::ostream& outputStream, + const DatumIntMapEntry& intMapEntry, + int level, + int spacesPerLevel) +{ + if (level >= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + + outputStream << intMapEntry.key() << ": "; + bdld::DatumUtil::typedPrint(outputStream, + intMapEntry.value(), + -level, + spacesPerLevel); +} + +static +void safeTypedPrintIntMapEntry( + bsl::ostream& outputStream, + bsl::unordered_set& seenSet, + const DatumIntMapEntry& intMapEntry, + int level, + int spacesPerLevel) +{ + if (level >= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + + outputStream << intMapEntry.key() << ": "; + safeTypedPrintImpl(outputStream, + seenSet, + intMapEntry.value(), + -level, + spacesPerLevel); +} + +static +void typedPrintMapEntry(bsl::ostream& outputStream, + const DatumMapEntry& mapEntry, + int level, + int spacesPerLevel) +{ + if (level >= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + + { + using bdlb::LiteralUtil; + + bdlma::LocalBufferedObject toPrint; + LiteralUtil::createQuotedEscapedCString(&*toPrint, mapEntry.key()); + outputStream << *toPrint << ": "; + } + bdld::DatumUtil::typedPrint(outputStream, + mapEntry.value(), + -level, + spacesPerLevel); +} + +static +void safeTypedPrintMapEntry(bsl::ostream& outputStream, + bsl::unordered_set& seenSet, + const DatumMapEntry& mapEntry, + int level, + int spacesPerLevel) +{ + if (level >= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + + { + using bdlb::LiteralUtil; + + bdlma::LocalBufferedObject toPrint; + LiteralUtil::createQuotedEscapedCString(&*toPrint, mapEntry.key()); + outputStream << *toPrint << ": "; + } + safeTypedPrintImpl(outputStream, + seenSet, + mapEntry.value(), + -level, + spacesPerLevel); +} + +} // close unnamed namespace + + // ---------------- + // struct DatumUtil + // ---------------- + +bsl::ostream& DatumUtil::safeTypedPrint(bsl::ostream& outputStream, + const Datum& object, + int level, + int spacesPerLevel) +{ + if (outputStream.bad()) { + return outputStream; // RETURN + } + + bsl::unordered_set seenSet; + + safeTypedPrintImpl(outputStream, seenSet, object, level, spacesPerLevel); + + return outputStream; +} + +bsl::ostream& DatumUtil::typedPrint(bsl::ostream& outputStream, + const Datum& object, + int level, + int spacesPerLevel) +{ + if (outputStream.bad()) { + return outputStream; // RETURN + } + + if (level >= 0) { + bdlb::Print::indent(outputStream, level, spacesPerLevel); + } + else { + level = -level; + } + + switch (object.type()) { + case Datum::e_NIL: { + outputStream << ""; + } break; + + case Datum::e_BOOLEAN: { + outputStream << (object.theBoolean() ? "":""); + } break; + + case Datum::e_ERROR: { + outputStream << '<' << object.theError() << '>'; + } break; + + case Datum::e_DOUBLE: { + bdlsb::MemOutStreamBuf sb; + bsl::ostream os(&sb); + os.precision(bsl::numeric_limits::digits10); + os << object.theDouble(); + + // Write the "core" value + outputStream.write(sb.data(), sb.length()); + + // Add a '.' if there wasn't one + const bsl::string_view sv(sb.data(), sb.length()); + if (sv.find('.') == bsl::string_view::npos) { + outputStream << '.'; + } + } break; + + case Datum::e_DECIMAL64: { + bdlsb::MemOutStreamBuf sb; + bsl::ostream os(&sb); + os << object.theDecimal64(); + + // Write the "core" value + outputStream.write(sb.data(), sb.length()); + + // Add '.' if there wasn't one + const bsl::string_view sv(sb.data(), sb.length()); + if (sv.find('.') == bsl::string_view::npos) { + outputStream << '.'; + } + // Add the decimal suffix unconditionally + outputStream << 'd'; + } break; + + case Datum::e_INTEGER: { + outputStream << object.theInteger() << 'i'; + } break; + + case Datum::e_INTEGER64: { + outputStream << object.theInteger64() << 'L'; + } break; + + case Datum::e_DATE: { + using bdlt::Iso8601Util; + static const bsl::size_t k_BUFLEN = Iso8601Util::k_DATE_STRLEN + 1; + char buffer[k_BUFLEN]; + Iso8601Util::generate(buffer, k_BUFLEN, object.theDate()); + outputStream << ""; + } break; + + case Datum::e_TIME: { + using bdlt::Iso8601Util; + using bdlt::Iso8601UtilConfiguration; + static const bsl::size_t k_BUFLEN = Iso8601Util::k_TIME_STRLEN + 1; + char buffer[k_BUFLEN]; + Iso8601UtilConfiguration cfg; + cfg.setFractionalSecondPrecision(6); + Iso8601Util::generate(buffer, k_BUFLEN, object.theTime(), cfg); + outputStream << ""; + } break; + + case Datum::e_DATETIME: { + using bdlt::Iso8601Util; + using bdlt::Iso8601UtilConfiguration; + static const bsl::size_t k_BUFLEN = Iso8601Util::k_DATETIME_STRLEN + 1; + char buffer[k_BUFLEN]; + Iso8601UtilConfiguration cfg; + cfg.setFractionalSecondPrecision(6); + Iso8601Util::generate(buffer, k_BUFLEN, object.theDatetime(), cfg); + + // Replace 'T' with ' ', because we do not have time zone info + + char *p = bsl::find(buffer, buffer + k_BUFLEN, 'T'); + if (p != buffer + k_BUFLEN) { + *p = ' '; + } + outputStream << ""; + } break; + + case Datum::e_DATETIME_INTERVAL: { + outputStream << ""; + } break; + + case Datum::e_STRING: { + using bdlb::LiteralUtil; + + bdlma::LocalBufferedObject toPrint; + LiteralUtil::createQuotedEscapedCString(&*toPrint, object.theString()); + outputStream << *toPrint; + } break; + + case Datum::e_USERDEFINED: { + const DatumUdt udt = object.theUdt(); + outputStream << ""; + } break; + + case Datum::e_BINARY: { + const DatumBinaryRef binRef = object.theBinary(); + typedef bdlb::PrintStringSingleLineHexDumper HexWrap; + const HexWrap asHex(static_cast(binRef.data()), + static_cast(binRef.size())); + outputStream << ""; + } break; + + case Datum::e_ARRAY: { + const DatumArrayRef aRef = object.theArray(); + outputStream << " 0) { + outputStream << ", "; + } + typedPrint(outputStream, aRef[i], 0, -1); + } + outputStream << ' '; + } + outputStream << "]>"; + } break; + + case Datum::e_INT_MAP: { + const DatumIntMapRef imRef = object.theIntMap(); + outputStream << " 0) { + outputStream << ", "; + } + typedPrintIntMapEntry(outputStream, imRef[i], 0, -1); + } + outputStream << ' '; + } + outputStream << "}>"; + } break; + + case Datum::e_MAP: { + const DatumMapRef mRef = object.theMap(); + outputStream << " 0) { + outputStream << ", "; + } + typedPrintMapEntry(outputStream, mRef[i], 0, -1); + } + outputStream << ' '; + } + outputStream << "}>"; + } break; + } + + if (0 <= spacesPerLevel) { + // Multi-line output. + outputStream << '\n'; + } + + return outputStream; +} + +} // close package namespace +} // close enterprise namespace + +// ---------------------------------------------------------------------------- +// Copyright 2024 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 ---------------------------------- diff --git a/groups/bdl/bdld/bdld_datumutil.h b/groups/bdl/bdld/bdld_datumutil.h new file mode 100644 index 0000000000..d747f49db1 --- /dev/null +++ b/groups/bdl/bdld/bdld_datumutil.h @@ -0,0 +1,211 @@ +// bdld_datumutil.h -*-C++-*- +#ifndef INCLUDED_BDLD_DATUMUTIL +#define INCLUDED_BDLD_DATUMUTIL + +#include +BSLS_IDENT("$Id: $") + +//@PURPOSE: Provide extra functions that operate on 'bdld::Datum' objects. +// +//@CLASSES: +// bdld::DatumUtil: namespace for extra functions for 'bdld::Datum' +// +//@SEE_ALSO: bdld_datum +// +//@DESCRIPTION: This component provides a struct, 'bdld::DatumUtil' that serves +// as a namespace for utility functions that operate on 'bdld::Datum' objects. +// The functions provided are 'typedPrint' and 'safeTypedPrint'. They are a +// variation on the standard BDE-style 'print' function but print the 'Datum' +// values in a manner that disambiguates the type when the value itself would +// not do so. +// +///Usage +///----- +// This section illustrates intended use of this component. +// +///Example 1: Showing the Difference Between an Integer and a Double Value +///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Suppose we are testing a system with operations that result in 'bdld::Datum' +// values. We verify that those results are what we have expected, *including* +// that their type matches. After a getting an unexpected value, we use normal +// printing and get the following test failure: "Expected 1, got 1". +// Obviously, the type difference is not visible. Instead, we can use +// 'bdld::DatumUtil::typedPrint' to display the type as well as the value. +// +// First, let us define two 'bdld::Datum' objects that have the same value, but +// use different types to represent them: +//.. +// bdld::Datum expected = bdld::Datum::createInteger(1); +// bdld::Datum actual = bdld::Datum::createDouble(1.); +// +// assert(expected != actual); +//.. +// Next, we demonstrate that printing these results in the same printout: +//.. +// bsl::ostringstream os; +// +// os << expected; +// bsl::string expectedStr = os.str(); +// +// os.str(""); +// os.clear(); +// os << actual; +// bsl::string actualStr = os.str(); +// +// assert(expectedStr == actualStr); // "1" is equal to "1" +//.. +// Then, we create a shorthand for 'bdld::DatumUtil::typedPrint': +//.. +// void printWithType(bsl::ostream& outStream, const bdld::Datum& object) +// { +// bdld::DatumUtil::typedPrint(outStream, object, 0, -1); +// } +//.. +// The 0 'level' and -1 'spacesPerLevel' results in single-line printout +// without a trailing newline, just like the stream output operator works. +// +// Finally, we verify that now we get a different printout for the two values: +//.. +// os.str(""); +// os.clear(); +// printWithType(os, expected); +// expectedStr = os.str(); +// +// os.str(""); +// os.clear(); +// printWithType(os, actual); +// actualStr = os.str(); +// +// assert(expectedStr != actualStr); // "1i" is *not* equal to "1." +//.. +// +///Example 2: Avoiding Endless Printing of Data with Cycles +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Suppose that we are testing a system that creates a complex data structure +// that it stores is 'bdld::Datum' objects. Suppose that such a system doesn't +// use the fail-safe 'Datum' builders for optimization purposes (for example it +// stores all map entries in one big allocation), and so it may be able to +// create a self-referential data structure. +// +// It is not easy to legitimately create self-referential data structures so we +// won't even attempt it in a short example code. +// +// First, we use a 'bdld::DatumMaker' with a local allocator so we can ignore +// any cleanup and allocation: +//.. +// bdlma::LocalSequentialAllocator<1024> lsa; +// bdld::DatumMaker dm(&lsa); +//.. +// Next, we create two array datums with a Nil element each: +//.. +// bdld::Datum arr1 = dm.a(dm()); +// bdld::Datum arr2 = dm.a(dm()); +//.. +// Then, we circumvent the type system to initialize their single elements to +// "contain" each other: +//.. +// const_cast(arr1.theArray()[0]) = arr2; +// const_cast(arr2.theArray()[0]) = arr1; +//.. +// Finally, we use the safe printing on this trapdoor of an endless loop to +// nevertheless safely print them: +//.. +// bsl::ostringstream os; +// bdld::DatumUtil::safeTypedPrint(os, arr1); +//.. +// Were we to print the results standard out, say +//.. +// bdld::DatumUtil::safeTypedPrint(cout, arr2); +//.. +// we would see something akin to: +//.. +// +// ]> +// ]> +//.. +// The hexadecimal numbers above identify the arrays (and maps or int-maps) so +// we can clearly see that the cycle "points" back to the top-level array. + +#include + +#include + +namespace BloombergLP { +namespace bdld { + +class Datum; + // ================ + // struct DatumUtil + // ================ +struct DatumUtil { + // This struct serves as a namespace for utility functions that operate on + // 'bdld::Datum' objects. + + // CLASS METHODS + static bsl::ostream& safeTypedPrint(bsl::ostream& outputStream, + const Datum& object, + int level = 0, + int spacesPerLevel = 4); + // Write the value of the specified 'object' to the specified + // 'outputStream' in a human-readable format that is non-ambiguous + // regarding the type of the 'bdld::Datum' value printed, and return a + // reference to the modifiable 'outputStream'. Optionally specify an + // initial indentation 'level', whose absolute value is incremented + // recursively for nested objects. If 'level' is specified, optionally + // specify 'spacesPerLevel', whose absolute value indicates 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. Note that 'safeTypedPrint' explicitly allows self- + // referential data structures, or in other words data structures with + // cycles, to be printed. When a cycle is detected, that subgraph is + // not printed again. Also note that the human-readable format is not + // fully specified, and can change without notice. + + static bsl::ostream& typedPrint(bsl::ostream& outputStream, + const Datum& object, + int level = 0, + int spacesPerLevel = 4); + // Write the value of the specified 'object' to the specified + // 'outputStream' in a human-readable format that is non-ambiguous + // regarding the type of the 'bdld::Datum' value printed, and return a + // reference to the modifiable 'outputStream'. Optionally specify an + // initial indentation 'level', whose absolute value is incremented + // recursively for nested objects. If 'level' is specified, optionally + // specify 'spacesPerLevel', whose absolute value indicates 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. The behavior is undefined unless the data structure + // represented by 'object' is a graph without any cycles. In case of + // an untrusted data structure, use 'safeTypedPrint'. Note that the + // human-readable format is not fully specified, and can change without + // notice. +}; + +} // close package namespace +} // close enterprise namespace + +#endif + +// ---------------------------------------------------------------------------- +// Copyright 2024 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 ---------------------------------- diff --git a/groups/bdl/bdld/bdld_datumutil.t.cpp b/groups/bdl/bdld/bdld_datumutil.t.cpp new file mode 100644 index 0000000000..7f9c8a9b67 --- /dev/null +++ b/groups/bdl/bdld/bdld_datumutil.t.cpp @@ -0,0 +1,1014 @@ +// bdld_datumutil.t.cpp -*-C++-*- +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace BloombergLP; +using bsl::cout; +using bsl::cerr; +using bsl::endl; + +// ============================================================================ +// TEST PLAN +// ---------------------------------------------------------------------------- +// Overview +// -------- +// This component is a utility operating on 'bldd::Datum' objects. +// ---------------------------------------------------------------------------- +// CLASS METHODS +// [2] ostream& typedPrint(object, outputStream, level, spacesPerLevel) +// [3] ostream& safeTypedPrint(object, outStream, level, spacesPerLevel) +// ---------------------------------------------------------------------------- +// [1] BREATHING TEST +// [4] USAGE EXAMPLE + +// ============================================================================ +// STANDARD BDE ASSERT TEST FUNCTION +// ---------------------------------------------------------------------------- + +namespace { + +int testStatus = 0; + +void aSsErT(bool condition, const char *message, int line) +{ + if (condition) { + cout << "Error " __FILE__ "(" << line << "): " << message + << " (failed)" << endl; + + if (0 <= testStatus && testStatus <= 100) { + ++testStatus; + } + } +} + +} // close unnamed namespace + +// ============================================================================ +// STANDARD BDE TEST DRIVER MACRO ABBREVIATIONS +// ---------------------------------------------------------------------------- + +#define ASSERT BSLIM_TESTUTIL_ASSERT +#define ASSERTV BSLIM_TESTUTIL_ASSERTV + +#define LOOP_ASSERT BSLIM_TESTUTIL_LOOP_ASSERT +#define LOOP0_ASSERT BSLIM_TESTUTIL_LOOP0_ASSERT +#define LOOP1_ASSERT BSLIM_TESTUTIL_LOOP1_ASSERT +#define LOOP2_ASSERT BSLIM_TESTUTIL_LOOP2_ASSERT +#define LOOP3_ASSERT BSLIM_TESTUTIL_LOOP3_ASSERT +#define LOOP4_ASSERT BSLIM_TESTUTIL_LOOP4_ASSERT +#define LOOP5_ASSERT BSLIM_TESTUTIL_LOOP5_ASSERT +#define LOOP6_ASSERT BSLIM_TESTUTIL_LOOP6_ASSERT + +#define Q BSLIM_TESTUTIL_Q // Quote identifier literally. +#define P BSLIM_TESTUTIL_P // Print identifier and value. +#define P_ BSLIM_TESTUTIL_P_ // P(X) without '\n'. +#define T_ BSLIM_TESTUTIL_T_ // Print a tab (w/o newline). +#define L_ BSLIM_TESTUTIL_L_ // current Line number + +//============================================================================= +// TYPE DEFINITIONS +//----------------------------------------------------------------------------- + +typedef bdld::DatumUtil Util; + +//============================================================================= +// PRINT HELPERS +//----------------------------------------------------------------------------- + +struct QuotedEscapedStringPrinterRef { + // Simple wrapper type to print a string quoted and escaped so printouts + // are easy to visually compare. + + // DATA + const bsl::string_view d_str; + + // CREATORS + QuotedEscapedStringPrinterRef(const bsl::string_view& str) + : d_str(str) + { + } +}; + +inline +bsl::ostream& +operator<<(bsl::ostream& os, const QuotedEscapedStringPrinterRef& obj) +{ + bdlma::LocalBufferedObject quotedAndEscaped; + + typedef bdlb::LiteralUtil LitUtil; + LitUtil::createQuotedEscapedCString(&*quotedAndEscaped, obj.d_str); + + return os << *quotedAndEscaped; +} + +//============================================================================= +// VERIFICATION HELPERS +//----------------------------------------------------------------------------- + +bool safeAndNormalMatches(const bsl::string_view& safeResult, + const bsl::string_view& typedResult) + // Return 'true', if the specified 'safeResult' matches the specified + // 'typedResult', where both results are from the printing of the same non- + // self-referential data structures using 'safeTypedPrint' and 'typedPrint' + // respectively. The function returns 'false' in case the two strings do + // not match. Since this is a test function it will follow a wide + // contract with what normally would be a 'BSLS_ASSERT' just be test + // failures. Passing an empty string for either arguments will return in + // such a test failure. + // + // The safe-typed-print output matches a typed-print output when the data + // types, values, and indentations printed are the same, but the safe-print + // also contain an at sign '@' and hexadecimal identifier after the type + // for each possibly self-referential type, such as arrays, maps, and + // int-maps. This method ignores the value of such identifiers except that + // it verifies that they are valid hexadecimal numbers, and that they do + // not repeat. In case either of those errors a test failure will be + // generated (test 'ASSERT'). +{ + ASSERT(!safeResult.empty()); + ASSERT(!typedResult.empty()); + if (safeResult.empty() || typedResult.empty()) { + return false; // RETURN + } + + using bsl::string_view; + bsl::unordered_set seenIDs; + + typedef string_view::size_type SizeT; + + const SizeT k_NPOS = string_view::npos; + SizeT safePos = 0; + SizeT typedPos = 0; + for (;;) { + // Find the next '@' + const SizeT atPos = safeResult.find('@', safePos); + + // Compare up to the next '@', or the end of the string if not found + const SizeT sliceLen = atPos != k_NPOS ? atPos - safePos : k_NPOS; + const string_view safeSlice = safeResult.substr( safePos, sliceLen); + const string_view typedSlice = typedResult.substr(typedPos, sliceLen); + if (safeSlice != typedSlice) { + ASSERTV(safeSlice, typedSlice, safeSlice == typedSlice); + return false; // RETURN + } + + // If there was no '@', we are done + if (k_NPOS == atPos) { + return true; // RETURN + } + + // If there *was* an '@', we need to verify/skip the hex digits + + // Let's move beyond the slices we have just compared + typedPos += sliceLen; + safePos = atPos + 1; + + // Skip 0x prefix is present + const SizeT hexPos = safeResult.substr(safePos).starts_with("0x") + ? safePos + 2 + : safePos; + + // Verify that we have hex digits + const SizeT nextSafePos = safeResult.find_first_not_of( + bdlb::CharType::stringXdigit(), + hexPos); + + // There *must* follow a value (array, map, or int-map) + ASSERT(nextSafePos != k_NPOS); + if (nextSafePos == k_NPOS) { + // Assume 'safeResult' ended prematurely + return false; // RETURN + } + + const SizeT hexIdLength = nextSafePos - safePos; + + const string_view id = safeResult.substr(safePos, hexIdLength); + + const bool alreadySeenThisID = !seenIDs.emplace(id).second; + ASSERT(!alreadySeenThisID); + + safePos = nextSafePos; + } +} + +bsl::string_view getNextID(bsl::string_view::size_type *position, + const bsl::string_view& string) + // Return the next "@ ID" from the specified 'string', or a default + // constructed (empty) string-view. Start searching for the '@' sign from + // the specified 'position'. Load, into 'position' the position of the '@' + // sign, or load 'npos' if there was no '@' sign found. + // + // For brevity this function is used both to get the next ID and the next + // named placeholder, so it allows the ID to have any alphanumeric + // character, not just hexadecimal digits. That verification is done by + // the caller. This will work because the ID as well as the name will be + // naturally delimited by a '[' character for arrays, and a '{' character + // for maps. +{ + using bsl::string_view; + typedef string_view::size_type SizeT; + + + const SizeT startPos = *position; + const SizeT atPos = string.find('@', startPos); + *position = atPos; + if (atPos == string_view::npos) { + return string_view(); // RETURN + } + + const SizeT end = string.find_first_not_of(bdlb::CharType::stringAlnum(), + atPos + 1); + ASSERT(end != string_view::npos); // Must be a '[' or '{' there + return string.substr(atPos + 1, end - atPos -1); +} + +bool isHexId(const bsl::string_view& str) + // Return 'true' if the specified 'str' is not empty, and all its + // constituent characters are hexadecimal digits. Otherwise, return + // 'false'. +{ + if (str.empty()) { + return false; // RETURN + } + + const bsl::string_view hex = str.substr(str.starts_with("0x") ? 2 : 0); + return hex.find_first_not_of(bdlb::CharType::stringXdigit()) == + bsl::string_view::npos; +} + +bool verifySafePrintResult(const bsl::string_view& safeResult, + const bsl::string_view& expected) + // Return 'true', if the specified 'safeResult' matches the specified + // 'expected' result, where the 'expected' string has named placeholders + // for identifiers, not their expected values. The function returns + // 'false' in case the two strings do not match. Since this is a test + // function it will follow a wide contract in regards to test-result + // arguments, but narrow contract in regards of the 'expected' argument. + // + // The safe-typed-print output matches the expected output when the data + // types, values, indentations printed are the same, the hexadecimal, and + // the matching hexadecimal identifiers are the same where the placeholder + // name in the 'expected' string is the same, and different for different + // names. +{ + BSLS_ASSERT(!expected.empty()); + + ASSERT(!safeResult.empty()); + if (safeResult.empty()) { + return false; // RETURN + } + + using bsl::string_view; + bsl::unordered_set seenIDs; + bsl::unordered_map nameToID; + typedef bsl::unordered_map::iterator NameIter; + + typedef string_view::size_type SizeT; + + const SizeT k_NPOS = string_view::npos; + SizeT safePos = 0; + SizeT expdPos = 0; + for (;;) { + // Find the next '@' + SizeT safeAtPos = safePos; + const string_view ident = getNextID(&safeAtPos, safeResult); + SizeT expdAtPos = expdPos; + const string_view named = getNextID(&expdAtPos, expected); + + if ((k_NPOS == safeAtPos) != (k_NPOS == expdAtPos)) { + return false; // RETURN + } + + // First we compare up to the '@' sign + { + const SizeT safeLen = k_NPOS == safeAtPos + ? k_NPOS + : safeAtPos - safePos; + const SizeT expdLen = k_NPOS == expdAtPos + ? k_NPOS + : expdAtPos - expdPos; + + const string_view safeSlice = safeResult.substr(safePos, safeLen); + const string_view expdSlice = expected.substr(expdPos, expdLen); + if (safeSlice != expdSlice) { + ASSERTV(safeSlice, expdSlice, safeSlice == expdSlice); + return false; // RETURN + } + } + + // If there was no '@', we are done + if (k_NPOS == safeAtPos) { + return true; // RETURN + } + + // Verify that the ID is hex and not empty + ASSERTV(ident, isHexId(ident)); + + // If we have seen this ID already it must have a name, and may be loop + const bool alreadySeenThisID = !seenIDs.emplace(ident).second; + const bsl::pair per = nameToID.emplace(named, ident); + if (!per.second) { + if (per.first->second != ident) { + // We have seen this name before, ID must match + const string_view storedIdForName = per.first->second; + ASSERTV(storedIdForName, ident, storedIdForName != ident); + return false; // RETURN + } + } + else { + // If we have this ID with another name, that is an error in the + // 'safeTypedPrint' code. + ASSERT(!alreadySeenThisID); + } + + // If there *was* an '@', we need to skip the name/id + safePos = safeAtPos + 1 + ident.size(); + expdPos = expdAtPos + 1 + named.size(); + } +} + +//============================================================================= +// USAGE EXAMPLE +//----------------------------------------------------------------------------- + +///Example 1: Showing the Difference Between an Integer and a Double Value +///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// See 'main()'. +//.. +// Then, we create a shorthand for 'bdld::Datumutil::typedPrint': +//.. + void printWithType(bsl::ostream& outStream, const bdld::Datum& object) + { + bdld::DatumUtil::typedPrint(outStream, object, 0, -1); + } +//.. +// See 'main()'. + +//============================================================================= +// MAIN PROGRAM +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int test = argc > 1 ? bsl::atoi(argv[1]) : 0; + bool verbose = argc > 2; + bool veryVerbose = argc > 3; (void)veryVerbose; + bool veryVeryVerbose = argc > 4; + bool veryVeryVeryVerbose = argc > 5; + + cout << "TEST " << __FILE__ << " CASE " << test << endl;; + + // CONCERN: Unexpected 'BSLS_REVIEW' failures should lead to test failures. + bsls::ReviewFailureHandlerGuard reviewGuard(bsls::Review::failByAbort); + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::Default::setDefaultAllocator(&da); + + bslma::TestAllocator ta("test", veryVeryVeryVerbose); + + switch (test) { case 0: + case 4: { + // -------------------------------------------------------------------- + // USAGE EXAMPLE + // Extracted from component header file. + // + // Concerns: + //: 1 The usage example provided in the component header file compiles, + //: links, and runs as shown. + // + // Plan: + //: 1 Incorporate usage example from header into test driver, replace + //: leading comment characters with spaces, replace 'assert' with + //: 'ASSERT', and insert 'if (veryVerbose)' before all output + //: operations. (C-1) + // + // Testing: + // USAGE EXAMPLE + // -------------------------------------------------------------------- + if (verbose) cout << "\nUSAGE EXAMPLE" + "\n=============\n"; + { +///Example 1: Showing the Difference Between an Integer and a Double Value +///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Suppose we are testing a system with operations that result in 'bdld::Datum' +// values. We verify that those results are what we have expected, *including* +// that their type matches. After a getting an unexpected value, we use normal +// printing and get the following test failure: "Expected 1, got 1". +// Obviously, the type difference is not visible. Instead, we can use +// 'bdld::DatumUtil::typedPrint' to display the type as well as the value. +// +// First, let us define two 'bdld::Datum' objects that have the same value, but +// use different types to represent them: +//.. + bdld::Datum expected = bdld::Datum::createInteger(1); + bdld::Datum actual = bdld::Datum::createDouble(1.); + + ASSERT(expected != actual); +//.. +// Next, we demonstrate that printing these results in the same printout: +//.. + bsl::ostringstream os; + + os << expected; + bsl::string expectedStr = os.str(); + + os.str(""); + os.clear(); + os << actual; + bsl::string actualStr = os.str(); + + ASSERT(expectedStr == actualStr); // "1" is equal to "1" +//.. +// Then, we create a shorthand for 'bdld::DatumUtil::typedPrint': +//.. +// void printWithType(ostream& outStream, const bdld::Datum& object) +// { +// bdld::Datumutil::typedPrint(outStream, object, 0, -1); +// } +//.. +// The 0 'level' and -1 'spacesPerLevel' results in single-line printout +// without a trailing newline, just like the stream output operator works. +// +// Finally, we verify that now we get a different printout for the two values: +//.. + os.str(""); + os.clear(); + printWithType(os, expected); + expectedStr = os.str(); + + os.str(""); + os.clear(); + printWithType(os, actual); + actualStr = os.str(); + + ASSERT(expectedStr != actualStr); // "1i" is *not* equal to "1." +//.. + } // close scope of example 1 + { +///Example 2: Avoiding Endless Printing of Data with Cycles +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Suppose that we are testing a system that creates a complex data structure +// that it stores is 'bdld::Datum' objects. Suppose that such a system doesn't +// use the fail-safe 'Datum' builders for optimization purposes (for example it +// stores all map entries in one big allocation), and so it may be able to +// create a self-referential data structure. +// +// It is not easy to legitimately create self-referential data structures so we +// won't even attempt it in a short example code. +// +// First, we use a 'bdld::DatumMaker' with a local allocator so we can ignore +// any cleanup and allocation: +//.. + bdlma::LocalSequentialAllocator<1024> lsa; + bdld::DatumMaker dm(&lsa); +//.. +// Next, we create two array datum's with a Nil element each: +//.. + bdld::Datum arr1 = dm.a(dm()); + bdld::Datum arr2 = dm.a(dm()); +//.. +// Then, we circumvent the type system to initialize their single elements to +// "contain" each other: +//.. + const_cast(arr1.theArray()[0]) = arr2; + const_cast(arr2.theArray()[0]) = arr1; +//.. +// Finally, we use the safe printing on this trapdoor of an endless loop to +// nevertheless safely print them: +//.. + bsl::ostringstream os; + bdld::DatumUtil::safeTypedPrint(os, arr1); +//.. +// Were we to print the results standard out, say +//.. + if (verbose) { + bdld::DatumUtil::safeTypedPrint(cout, arr2); + } +//.. +// we would see something akin to: +//.. +// +// ]> +// ]> +//.. +// Where the hexadecimal numbers identify the arrays (and maps or int-maps) so +// we can clearly see that the cycle "points" back to the top-level array. + } // close scope of example 2 + } break; + case 3: { + // -------------------------------------------------------------------- + // SAFE PRINTING + // + // Concerns: + //: 1 Self-referential data does not cause endless loop/recursion. + //: + //: 2 "Normal" data printing was covered by previous test case. + //: + //: 3 Identifiers match when there is a self-reference. + // + // Plan: + //: 1 Table based testing with lines with varying values that are self- + //: referential, and varying 'level' and 'spacesPerLevel' arguments. + //: + //: 2 'Datum' values are created using a 'DatumMaker' and a sequential- + //: allocator to avoid having to deal with deallocations. + //: + //: 3 Printing is done into a string stream for comparison. + //: + //: 4 All scalars that cannot create self-references have been + //: previously tested, this case is *only* for self-referential data + //: structures. + // + // Testing: + // ostream& safeTypedPrint(object, outStream, level, spacesPerLevel) + // -------------------------------------------------------------------- + + if (verbose) cout << "\nSAFE TYPED PRINTING" + "\n===================\n"; + + bdlma::LocalSequentialAllocator<1024> lsa(&ta); + bdld::DatumMaker dm(&lsa); + + // Self-referential types cannot be easily created using 'bdld', we use + // 'const_cast' here to create the input data for the test-table lines. + + // Self-referencing array + bdld::Datum arr1 = dm.a(dm()); + const_cast(arr1.theArray()[0]) = arr1; + + // Two arrays creating a loop + bdld::Datum arr2a = dm.a(dm()); + bdld::Datum arr2b = dm.a(dm()); + const_cast(arr2a.theArray()[0]) = arr2b; + const_cast(arr2b.theArray()[0]) = arr2a; + + // Three arrays creating a circle + bdld::Datum arr3a = dm.a(dm()); + bdld::Datum arr3b = dm.a(dm()); + bdld::Datum arr3c = dm.a(dm()); + const_cast(arr3a.theArray()[0]) = arr3b; + const_cast(arr3b.theArray()[0]) = arr3c; + const_cast(arr3c.theArray()[0]) = arr3a; + + // Three arrays having a sub-loop + bdld::Datum arr4a = dm.a(dm()); + bdld::Datum arr4b = dm.a(dm()); + bdld::Datum arr4c = dm.a(dm()); + const_cast(arr4a.theArray()[0]) = arr4b; + const_cast(arr4b.theArray()[0]) = arr4c; + const_cast(arr4c.theArray()[0]) = arr4b; + + // Self-referencing map + bdld::Datum map1 = dm.m("myself", dm()); + const_cast(map1.theMap()[0].value()) = map1; + + // Two maps creating a loop + bdld::Datum map2a = dm.m("vice", dm()); + bdld::Datum map2b = dm.m("versa", dm()); + const_cast(map2a.theMap()[0].value()) = map2b; + const_cast(map2b.theMap()[0].value()) = map2a; + + // Self-referencing int-map + bdld::Datum imap1 = dm.im(1, dm()); + const_cast(imap1.theIntMap()[0].value()) = imap1; + + // Two maps creating a loop + bdld::Datum imap2a = dm.im(2, dm()); + bdld::Datum imap2b = dm.im(1, dm()); + const_cast(imap2a.theIntMap()[0].value()) = imap2b; + const_cast(imap2b.theIntMap()[0].value()) = imap2a; + + static const struct { + long d_line; + bdld::Datum d_datum; + int d_level; + int d_spacesPerLevel; + const char *d_expectedOutput; + } TEST_DATA[] = { + // === ARRAY === + { L_, arr1, 0, -1, " ]>" }, + + { L_, arr2a, 0, -1, + " ]> ]>" }, + { L_, arr2b, 0, -1, + " ]> ]>" }, + + { L_, arr3a, 0, -1, + " " + "]> ]> ]>" }, + { L_, arr3b, 0, -1, + " " + "]> ]> ]>" }, + { L_, arr3c, 0, -1, + " " + "]> ]> ]>" }, + + { L_, arr4a, 0, -1, + " " + "]> ]> ]>" }, + + // === MAP === + { L_, map1, 0, -1, + " }>" }, + { L_, map1, 0, 4, "\n" + "}>\n" }, + + { L_, map2a, 0, -1, + "" + " }> }>" }, + { L_, map2b, 0, -1, + "" + " }> }>" }, + { L_, map2a, 0, 4, "\n" + " }>\n}>\n" }, + { L_, map2b, 0, 4, "\n" + " }>\n}>\n" }, + + // === INT-MAP === + { L_, imap1, 0, -1, + " }>" }, + { L_, imap1, 0, 4, "\n" + "}>\n" }, + + { L_, imap2a, 0, -1, + "" + " }> }>" }, + { L_, imap2b, 0, -1, + "" + " }> }>" }, + { L_, imap2a, 0, 4, "\n" + " }>\n}>\n" }, + { L_, imap2b, 0, 4, "\n" + " }>\n}>\n" }, + }; + const size_t TEST_SIZE = sizeof TEST_DATA / sizeof *TEST_DATA; + + for (size_t ti = 0; ti < TEST_SIZE; ++ti) { + const long LINE = TEST_DATA[ti].d_line; + const bdld::Datum DATUM = TEST_DATA[ti].d_datum; + const int LEVEL = TEST_DATA[ti].d_level; + const int SP_PER_LEV = TEST_DATA[ti].d_spacesPerLevel; + const char * const EXPECTED = TEST_DATA[ti].d_expectedOutput; + + if (veryVeryVerbose) { + T_ P_(LINE) P_(DATUM) P_(LEVEL) P(SP_PER_LEV); + } + + bsl::ostringstream os; + Util::safeTypedPrint(os, DATUM, LEVEL, SP_PER_LEV); + const bsl::string RESULT = os.str(); + + typedef QuotedEscapedStringPrinterRef QnEs; + ASSERTV(QnEs(RESULT), QnEs(EXPECTED), + verifySafePrintResult(RESULT, EXPECTED)); + } + } break; + case 2: { + // -------------------------------------------------------------------- + // TYPED PRINTING + // + // Concerns: + //: 1 Each type is distinguished as intended. + //: + //: 2 'level' and 'spacesPerLevel' arguments are obeyed. + //: + //: 3 The scalar values themselves are printed properly. + // + // Plan: + //: 1 Table based testing with lines for each type with varying values, + //: and varying 'level' and 'spacesPerLevel' arguments. + //: + //: 2 'Datum' values are created using a 'DatumMaker' and a sequential- + //: allocator to avoid having to deal with deallocations. + //: + //: 3 Printing is done into a string stream for comparison. + // + // Testing: + // ostream& typedPrint(object, outputStream, level, spacesPerLevel) + // -------------------------------------------------------------------- + if (verbose) cout << "\nTYPED PRINTING" + "\n==============\n"; + + bdlma::LocalSequentialAllocator<1024> lsa(&ta); + bdld::DatumMaker dm(&lsa); + + const unsigned char BIN_DATA[] = + "\xDE\xCA\xFB\xAD\0\x1F\xAD\xED\x10\xAD\xC0\xFF\xEE"; + const size_t BIN_SIZE = (sizeof BIN_DATA) - 1; // No closing null + const char BIN_STR[] = ""; + + static const struct { + long d_line; + bdld::Datum d_datum; + int d_level; + int d_spacesPerLevel; + const char *d_expectedOutput; + } TEST_DATA[] = { + // === BASIC TYPES === (cannot reference other types) + + { L_, dm(), 0, -1, "" }, // Nil: the type and value + + { L_, dm( 1), 0, -1, "1i" }, // Small integers + { L_, dm(-1), 0, -1, "-1i" }, + { L_, dm( 0), 0, -1, "0i" }, + { L_, dm(42), 0, -1, "42i" }, + + { L_, dm( 1ll), 0, -1, "1L" }, // 64-bit integers + { L_, dm(-1ll), 0, -1, "-1L" }, + { L_, dm( 0ll), 0, -1, "0L" }, + { L_, dm(42ll), 0, -1, "42L" }, + + { L_, dm( 1. ), 0, -1, "1." }, // Binary floating-point + { L_, dm(-1. ), 0, -1, "-1." }, + { L_, dm( 0. ), 0, -1, "0." }, + { L_, dm(42. ), 0, -1, "42." }, + + { L_, dm( 1.23), 0, -1, "1.23" }, + { L_, dm(-1.23), 0, -1, "-1.23" }, + { L_, dm( 0.05), 0, -1, "0.05" }, + { L_, dm(42.25), 0, -1, "42.25" }, + +#define MDD(X) dm(BDLDFP_DECIMAL_DD(X)) + { L_, MDD( 1. ), 0, -1, "1.d" }, // Decimal floating-point + { L_, MDD(-1. ), 0, -1, "-1.d" }, + { L_, MDD( 0. ), 0, -1, "0.d" }, + { L_, MDD(42. ), 0, -1, "42.d" }, + + { L_, MDD( 1.23), 0, -1, "1.23d" }, + { L_, MDD(-1.23), 0, -1, "-1.23d" }, + { L_, MDD( 0.05), 0, -1, "0.05d" }, + { L_, MDD(42.25), 0, -1, "42.25d" }, +#undef MDD + + { L_, dm("some text\n..more"), 0, -1,"\"some text\\n..more\"" }, + + { L_, dm(bdld::DatumUdt(&dm, 42)), 0, -1, + " + { L_, dm.a(dm()), 0, -1, " ]>" }, + { L_, dm.a(dm.a(dm())), 0, -1, + " ]> ]>" }, + { L_, dm.a(dm(), dm.a(dm())), 0, -1, + ", ]> ]>" }, + + // <| MAP |> + { L_, dm.m("nil", dm()), 0, -1, + " }>" }, + { L_, dm.m("map", dm.m("nil", dm())), 0, -1, + " }> }>" }, + { L_, dm.m("nil", dm(), + "map", dm.m("nil", dm())), 0, -1, + ", \"map\": }> }>" }, + + // <| INT-MAP |> + { L_, dm.im(1, dm()), 0, -1, " }>" }, + { L_, dm.im(2, dm.im(5, dm())), 0, -1, + " }> }>" }, + { L_, dm.im( 3, dm(), + 15, dm.im(-322, dm())), 0, -1, + ", 15: }> }>" }, + + // <| ARRAY, MAP MIX |> + { L_, dm.a(dm.m("nil", dm())), 0, -1, + " }> ]>" }, + { L_, dm.m("arr", dm.a(dm())), 0, -1, + " ]> }>" }, + + // <| ARRAY, MAP, INT-MAP MIX |> + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), 0, -1, + " }> ]>" + }, + + // === EXERCISE INDENTATION === + + // Variations on basic 'level' & 'spacesPerLevel' behavior + { L_, dm(), 0, 0, "\n" }, + + { L_, dm(), 1, 0, "\n" }, + { L_, dm(), 1, 1, " \n" }, + { L_, dm(), 1, 2, " \n" }, + { L_, dm(), 1, 4, " \n" }, + + { L_, dm(), 0, 0, "\n" }, + { L_, dm(), 0, 1, "\n" }, + { L_, dm(), 0, 2, "\n" }, + { L_, dm(), 0, 4, "\n" }, + + { L_, dm(), -5, 0, "\n" }, + { L_, dm(), -5, 1, "\n" }, + { L_, dm(), -5, 2, "\n" }, + { L_, dm(), -5, 4, "\n" }, + + // Variations on compound 'level' & 'spacesPerLevel' behavior + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), 0, 4, + "\n" + " }>\n" + "]>\n" + }, + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), 1, 4, + " \n" + " }>\n" + " ]>\n" + }, + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), -1, 4, + "\n" + " }>\n" + " ]>\n" + }, + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), 1, -4, + " }> ]>" + }, + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), 0, -4, + " }> ]>" + }, + { L_, dm.a(dm.m("nil", dm.im(1848, "revolution"))), -1, -4, + " }> ]>" + }, + }; + const size_t TEST_SIZE = sizeof TEST_DATA / sizeof *TEST_DATA; + + for (size_t ti = 0; ti < TEST_SIZE; ++ti) { + const long LINE = TEST_DATA[ti].d_line; + const bdld::Datum DATUM = TEST_DATA[ti].d_datum; + const int LEVEL = TEST_DATA[ti].d_level; + const int SP_PER_LEV = TEST_DATA[ti].d_spacesPerLevel; + const char * const EXPECTED = TEST_DATA[ti].d_expectedOutput; + + if (veryVeryVerbose) { + T_ P_(LINE) P_(DATUM) P_(LEVEL) P(SP_PER_LEV); + } + + bsl::ostringstream os; + Util::typedPrint(os, DATUM, LEVEL, SP_PER_LEV); + const bsl::string RESULT = os.str(); + + typedef QuotedEscapedStringPrinterRef QnEs; + if (DATUM.type() != bdld::Datum::e_USERDEFINED) { + ASSERTV(QnEs(RESULT), QnEs(EXPECTED), RESULT == EXPECTED); + } + else { + ASSERTV(QnEs(RESULT), QnEs(EXPECTED), + RESULT.starts_with(EXPECTED) && RESULT.ends_with(")>")); + } + + bsl::ostringstream oss; + Util::safeTypedPrint(oss, DATUM, LEVEL, SP_PER_LEV); + const bsl::string SAFE_RESULT = oss.str(); + + typedef QuotedEscapedStringPrinterRef QnEs; + ASSERTV(QnEs(SAFE_RESULT), QnEs(RESULT), + safeAndNormalMatches(SAFE_RESULT, RESULT)); + } + } break; + case 1: { + // -------------------------------------------------------------------- + // BREATHING TEST + // This case exercises (but does not fully test) basic functionality. + // + // Concerns: + //: 1 The class is sufficiently functional to enable comprehensive + //: testing in subsequent test cases. + // + // Plan: + //: 1 Call the utility functions to verify their existence and basics. + // + // Testing: + // BREATHING TEST + // -------------------------------------------------------------------- + if (verbose) cout << "\nBREATHING TEST" + "\n==============\n"; + + bdlma::LocalSequentialAllocator<1024> lsa(&ta); + bdld::DatumMaker dm(&lsa); + + const bdld::Datum arr = dm.a( + dm(1.), + dm(1), + dm(1ll), + dm(BDLDFP_DECIMAL_DD(1.)), + dm("just a\tstring"), + dm(bdld::DatumUdt(&dm, 42)), + dm.bin(&lsa, 16), + dm()); + + const bdld::Datum imap = dm.im( + 1, dm(1.), + 2, dm(1), + 3, dm(1ll), + 4, dm(BDLDFP_DECIMAL_DD(1.)), + 5, arr, + 6, dm(), + 7, dm()); + + const bdld::Datum map = dm.mok( + "dbl", dm(1.), + "int", dm(1), + "i64", dm(1ll), + "dfp", dm(BDLDFP_DECIMAL_DD(1.)), + "str", dm("at first you fail\n" + "trie and trie again"), + "udt", dm(bdld::DatumUdt(&dm, 42)), + "bin", dm.bin(&lsa, 16), + "arr", arr, + "imap", imap, + "nil1", dm(), + "nil2", dm(), + "nil3", dm()); + + if (verbose) { + Util::typedPrint(bsl::cout, arr , 0, -1); + bsl::cout << "\n"; + Util::typedPrint(bsl::cout, imap, 0, -1); + bsl::cout << "\n"; + Util::typedPrint(bsl::cout, map, 0, -1); + bsl::cout << "\n\n"; + Util::typedPrint(bsl::cout, arr); + bsl::cout << "\n"; + Util::typedPrint(bsl::cout, imap); + bsl::cout << "\n"; + Util::typedPrint(bsl::cout, map); + bsl::cout << "\n"; + } + } break; + default: { + cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; + testStatus = -1; + } break; + } + + if (testStatus > 0) { + cerr << "Error, non-zero test status = " << testStatus << "." << endl; + } + return testStatus; +} + +// ---------------------------------------------------------------------------- +// Copyright 2024 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 ---------------------------------- diff --git a/groups/bdl/bdld/doc/bdld.txt b/groups/bdl/bdld/doc/bdld.txt index 97da0caa16..d44e6f145e 100644 --- a/groups/bdl/bdld/doc/bdld.txt +++ b/groups/bdl/bdld/doc/bdld.txt @@ -15,7 +15,7 @@ /Hierarchical Synopsis /--------------------- - The 'bdld' package currently has 10 components having 4 levels of physical + The 'bdld' package currently has 11 components having 4 levels of physical dependency. The list below shows the hierarchical ordering of the components. The order of components within each level is not architecturally significant, just alphabetical. @@ -26,6 +26,7 @@ bdld_datumintmapbuilder bdld_datummapbuilder bdld_datummapowningkeysbuilder + bdld_datumutil bdld_manageddatum 2. bdld_datum @@ -64,5 +65,8 @@ : 'bdld_datumudt': : Provide a type to represent a user-defined type. : +: 'bdld_datumutil': +: Provide extra functions that operate on 'bdld::Datum' objects. +: : 'bdld_manageddatum': : Provide a smart-pointer-like manager for a 'Datum' object. diff --git a/groups/bdl/bdld/package/bdld.mem b/groups/bdl/bdld/package/bdld.mem index 668dd30d31..8fde59299f 100644 --- a/groups/bdl/bdld/package/bdld.mem +++ b/groups/bdl/bdld/package/bdld.mem @@ -7,4 +7,5 @@ bdld_datummaker bdld_datummapbuilder bdld_datummapowningkeysbuilder bdld_datumudt +bdld_datumutil bdld_manageddatum diff --git a/groups/bdl/bdlt/bdlt_epochutil.t.cpp b/groups/bdl/bdlt/bdlt_epochutil.t.cpp index ea807b84d6..c7d1a83899 100644 --- a/groups/bdl/bdlt/bdlt_epochutil.t.cpp +++ b/groups/bdl/bdlt/bdlt_epochutil.t.cpp @@ -176,17 +176,23 @@ const bdlt::Datetime &EarlyEpochCopier::copiedValue() #define INITATTR #if defined(BSLS_PLATFORM_CMP_MSVC) -#pragma init_seg(compiler) + #pragma warning(push) + #pragma warning(disable:4074) + // DISABLE: initializers put in compiler reserved initialization area + + #pragma init_seg(compiler) + + #pragma warning(pop) #elif defined(BSLS_PLATFORM_CMP_CLANG) -#if __has_attribute(init_priority) -#undef INITATTR -#define INITATTR __attribute__((init_priority(101))) -#endif + #if __has_attribute(init_priority) + #undef INITATTR + #define INITATTR __attribute__((init_priority(101))) + #endif #elif defined(BSLS_PLATFORM_CMP_GNU) && defined(BSLS_PLATFORM_OS_LINUX) -#undef INITATTR -#define INITATTR __attribute__((init_priority(101))) + #undef INITATTR + #define INITATTR __attribute__((init_priority(101))) #elif defined(BSLS_PLATFORM_CMP_IBM) -#pragma priority(-2147482623) + #pragma priority(-2147482623) #endif EarlyEpochCopier earlyEpochCopier INITATTR; diff --git a/groups/bsl/bslim/bslim_printer.h b/groups/bsl/bslim/bslim_printer.h index 3c77ea08f3..45d1d7b4fd 100644 --- a/groups/bsl/bslim/bslim_printer.h +++ b/groups/bsl/bslim/bslim_printer.h @@ -247,7 +247,7 @@ BSLS_IDENT("$Id: $") // bsl::ostream& print(bsl::ostream& stream, // const ThirdPartyStruct& data, // int level = 0, -// int spacesPerLevel = 4); +// int spacesPerLevel = 4); // // You write this function in your own code to accommodate // // 'ThirdPartyStruct'. // }; diff --git a/groups/bsl/bslmt/bslmt_readerwriterlockassert.t.cpp b/groups/bsl/bslmt/bslmt_readerwriterlockassert.t.cpp index 303b177c99..9577851f7c 100644 --- a/groups/bsl/bslmt/bslmt_readerwriterlockassert.t.cpp +++ b/groups/bsl/bslmt/bslmt_readerwriterlockassert.t.cpp @@ -540,7 +540,7 @@ void test() BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_READ_SAFE( &rwLock); BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_WRITE_SAFE(&rwLock); } catch (bsls::AssertTestException thrown) { - ASSERT(!"Reachable") + ASSERT(0 == "Reachable") } #endif // BSLS_ASSERT_SAFE_IS_ACTIVE @@ -557,19 +557,19 @@ void test() case 'a': { TestCase2::expectedLine = __LINE__ + 1; BSLMT_READERWRITERLOCKASSERT_IS_LOCKED(&rwLock); - ASSERTV(cfg, !"Reachable") + ASSERTV(cfg, 0 == "Reachable") break; } case 'b': { TestCase2::expectedLine = __LINE__ + 1; BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_READ(&rwLock); - ASSERTV(cfg, !"Reachable") + ASSERTV(cfg, 0 == "Reachable") break; } case 'c': { TestCase2::expectedLine = __LINE__ + 1; BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_WRITE(&rwLock); - ASSERTV(cfg, !"Reachable") + ASSERTV(cfg, 0 == "Reachable") break; } } @@ -600,19 +600,19 @@ void test() case 'a': { TestCase2::expectedLine = __LINE__ + 1; BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_OPT(&rwLock); - ASSERTV(cfg, !"Reachable") + ASSERTV(cfg, 0 == "Reachable") break; } case 'b': { TestCase2::expectedLine = __LINE__ + 1; BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_READ_OPT(&rwLock); - ASSERTV(cfg, !"Reachable") + ASSERTV(cfg, 0 == "Reachable") break; } case 'c': { TestCase2::expectedLine = __LINE__ + 1; BSLMT_READERWRITERLOCKASSERT_IS_LOCKED_WRITE_OPT(&rwLock); - ASSERTV(cfg, !"Reachable") + ASSERTV(cfg, 0 == "Reachable") break; } } diff --git a/groups/bsl/bslmt/bslmt_rwmutex.t.cpp b/groups/bsl/bslmt/bslmt_rwmutex.t.cpp index 5bd6ad257a..c1c96cffe3 100644 --- a/groups/bsl/bslmt/bslmt_rwmutex.t.cpp +++ b/groups/bsl/bslmt/bslmt_rwmutex.t.cpp @@ -425,14 +425,14 @@ int benchmarkSpeed (LOCK * /* lock */, static const double SCORE_SCALE = 1.33; bsls::TimeInterval start, stop; -#ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_GCC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlarger-than=" #endif LOCK lockArray[k_NUM_MUTEXES]; -#ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_GCC #pragma GCC diagnostic pop #endif @@ -875,7 +875,7 @@ int main(int argc, char *argv[]) if (0 != bslmt::ThreadUtil::create(&t1, readerThread) || 0 != bslmt::ThreadUtil::create(&t2, readerThread)) { - ASSERT(!"Could not create threads!! Bad state! Failing test."); + ASSERT(0 == "Could not create threads!!Bad state!Failing test."); } else { startBarrier.wait(); diff --git a/groups/bsl/bslmt/bslmt_testutil.t.cpp b/groups/bsl/bslmt/bslmt_testutil.t.cpp index 110f4c4c2e..4f870a8012 100644 --- a/groups/bsl/bslmt/bslmt_testutil.t.cpp +++ b/groups/bsl/bslmt/bslmt_testutil.t.cpp @@ -29,15 +29,17 @@ #include // (ditto) #if defined(BSLS_PLATFORM_OS_WINDOWS) -# include // '_dup2' -# define snprintf _snprintf + #include // '_dup2' + #define snprintf _snprintf #else -# include + #include #endif #ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wstringop-overread" + #pragma GCC diagnostic ignored "-Wpragmas" +#endif +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_GCC + #pragma GCC diagnostic ignored "-Wstringop-overread" #endif // Ensure that the 'BSLMT_TESTUTIL_*' macros do not require: @@ -195,9 +197,9 @@ typedef BloombergLP::bsls::AtomicInt AtomicInt; typedef BloombergLP::bslmt::Mutex Mutex; #if defined(BSLS_PLATFORM_OS_WINDOWS) -# define U_SLASH_STR "\\" + #define U_SLASH_STR "\\" #else -# define U_SLASH_STR "/" + #define U_SLASH_STR "/" #endif //============================================================================= diff --git a/groups/bsl/bslmt/bslmt_threadattributes.t.cpp b/groups/bsl/bslmt/bslmt_threadattributes.t.cpp index 9006012479..489105277f 100644 --- a/groups/bsl/bslmt/bslmt_threadattributes.t.cpp +++ b/groups/bsl/bslmt/bslmt_threadattributes.t.cpp @@ -28,7 +28,7 @@ #include #ifdef BSLMT_PLATFORM_POSIX_THREADS -#include + #include #endif using namespace BloombergLP; @@ -128,15 +128,15 @@ typedef bslmt::ThreadAttributes Obj; void myThreadFunction() { -#ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlarger-than=" +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlarger-than=" #endif int bufferLocal[k_BUFFER_SIZE]; -#ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_GCC + #pragma GCC diagnostic pop #endif // Perform some calculation that involves no subroutine calls or diff --git a/groups/bsl/bslmt/bslmt_threadutil.t.cpp b/groups/bsl/bslmt/bslmt_threadutil.t.cpp index c0b7d927bf..3887a10330 100644 --- a/groups/bsl/bslmt/bslmt_threadutil.t.cpp +++ b/groups/bsl/bslmt/bslmt_threadutil.t.cpp @@ -1488,6 +1488,15 @@ long myAbs(long a) return a >= 0 ? a : -a; } +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_CLANG + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Winfinite-recursion" +#endif +#ifdef BSLS_PLATFORM_CMP_MSVC + #pragma warning(push) + #pragma warning(disable:4717) +#endif + void testCaseMinus1Recurser(const char *start) { enum { k_BUF_LEN = 1024 }; @@ -1511,6 +1520,12 @@ extern "C" void *testCaseMinus1ThreadMain(void *) return 0; } +#ifdef BSLS_PLATFORM_CMP_MSVC + #pragma warning(pop) +#endif +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_CLANG + #pragma GCC diagnostic pop +#endif // ---------------------------------------------------------------------------- // TEST CASE -2 diff --git a/groups/bsl/bslmt/bslmt_turnstile.t.cpp b/groups/bsl/bslmt/bslmt_turnstile.t.cpp index 50619de57f..8df2c18e52 100644 --- a/groups/bsl/bslmt/bslmt_turnstile.t.cpp +++ b/groups/bsl/bslmt/bslmt_turnstile.t.cpp @@ -696,7 +696,7 @@ int main(int argc, char *argv[]) Obj mX(RATE); const Obj& X = mX; - int numTurns = RATE; + int numTurns = static_cast(RATE); ASSERT(0 <= X.lagTime()); // (likely) lagging on the first turn ASSERT(0 == mX.waitTurn()); // first turn can be taken immediately diff --git a/groups/bsl/bslx/bslx_typecode.t.cpp b/groups/bsl/bslx/bslx_typecode.t.cpp index bf4ede27b3..23ff976902 100644 --- a/groups/bsl/bslx/bslx_typecode.t.cpp +++ b/groups/bsl/bslx/bslx_typecode.t.cpp @@ -16,9 +16,11 @@ #include #ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wstringop-overread" -#pragma GCC diagnostic ignored "-Wstringop-overflow=" + #pragma GCC diagnostic ignored "-Wpragmas" +#endif +#ifdef BSLS_PLATFORM_PRAGMA_GCC_DIAGNOSTIC_GCC + #pragma GCC diagnostic ignored "-Wstringop-overread" + #pragma GCC diagnostic ignored "-Wstringop-overflow=" #endif using namespace BloombergLP;