diff --git a/groups/bdl/bdljsn/bdljsn_error.t.cpp b/groups/bdl/bdljsn/bdljsn_error.t.cpp index eaf065cb36..2b3f305b5b 100644 --- a/groups/bdl/bdljsn/bdljsn_error.t.cpp +++ b/groups/bdl/bdljsn/bdljsn_error.t.cpp @@ -1960,7 +1960,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, 2*sizeof(Obj) == fa.numBytesInUse()); @@ -2312,7 +2312,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(Z, objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, sizeof(Obj) == fa.numBytesInUse()); @@ -3314,7 +3314,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(LOC, MESSAGE, objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, @@ -3635,7 +3635,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } diff --git a/groups/bdl/bdljsn/bdljsn_json.cpp b/groups/bdl/bdljsn/bdljsn_json.cpp index aa9d03ce36..4d7ca36b28 100644 --- a/groups/bdl/bdljsn/bdljsn_json.cpp +++ b/groups/bdl/bdljsn/bdljsn_json.cpp @@ -6,6 +6,8 @@ BSLS_IDENT_RCSID(bdljsn_json_cpp, "$Id$ $CSID$") #include +#include + #include #include @@ -91,9 +93,156 @@ void PrintVisitor::operator()(const JsonArray& value) const void PrintVisitor::operator()(bslmf::Nil) const { - BSLS_ASSERT(false); + BSLA_UNREACHABLE; + BSLS_ASSERT_INVOKE_NORETURN("Unreachable"); +} + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + // ====================== + // class ConstructVisitor + // ====================== + +class ConstructVisitor { + Json *d_this_p; + + public: + explicit ConstructVisitor(Json *json); + // Create a 'ConstructVisitor' object configured write into the + // specified 'json'. + + void operator()(const JsonNull &jNull) const; + // Store the specified 'jNull' into the Json object pointed to by + // 'd_this_p'. + + void operator()(bool b) const; + // Store the specified 'b' into the Json object pointed to by + // 'd_this_p'. + + void operator()(long long ll) const; + // Store the specified 'll' into the Json object pointed to by + // 'd_this_p'. + + void operator()(unsigned long long ull) const; + // Store the specified 'ull' into the Json object pointed to by + // 'd_this_p'. + + void operator()(double d) const; + // Store the specified 'd' into the Json object pointed to by + // 'd_this_p'. + + void operator()(bdldfp::Decimal64 value) const; + // Store the specified 'value' into the Json object pointed to by + // 'd_this_p'. + + void operator()(const bsl::string_view& sv) const; + // Store the specified 'sv' into the Json object pointed to by + // 'd_this_p'. + + void operator()(const JsonObject *jObj) const; + // Store the object pointed to by the specified 'jObj' to the 'Json' + // object pointed to by 'd_this_p'. + + void operator()(const JsonArray *jArr) const; + // Store the object pointed to by the specified 'jArr' to the 'Json' + // object pointed to by 'd_this_p'. + + void operator()(const JsonNumber *jNum) const; + // Store the object pointed to by the specified 'jNum' to the 'Json' + // object pointed to by 'd_this_p'. + + void operator()(const Json *json) const; + // Store the object pointed to by the specified 'json' to the 'Json' + // object pointed to by 'd_this_p'. + + void operator()(std::initializer_list il) const; + // Store a JsonArray containing the elements of the specified 'il' into + // the Json object pointed to by 'd_this_p'. + + void operator()(bslmf::Nil) const; + // The behavior is undefined when this method is called. +}; + + // --------------------- + // class ConstructVisitor + // --------------------- + +ConstructVisitor::ConstructVisitor(Json *json) +: d_this_p(json) +{ +} + +void ConstructVisitor::operator()(const JsonNull &jNull) const +{ + (void)jNull; +} + +void ConstructVisitor::operator()(bool b) const +{ + d_this_p->makeBoolean(b); +} + +void ConstructVisitor::operator()(long long ll) const +{ + d_this_p->makeNumber(JsonNumber(ll)); +} + +void ConstructVisitor::operator()(unsigned long long ull) const +{ + d_this_p->makeNumber(JsonNumber(ull)); +} + +void ConstructVisitor::operator()(double d) const +{ + d_this_p->makeNumber(JsonNumber(d)); +} + +void ConstructVisitor::operator()(bdldfp::Decimal64 value) const +{ + d_this_p->makeNumber(JsonNumber(value)); +} + +void ConstructVisitor::operator()(const bsl::string_view& sv) const +{ + d_this_p->makeString(sv); } +void ConstructVisitor::operator()(const JsonObject *jObj) const +{ + d_this_p->makeObject(*jObj); +} + +void ConstructVisitor::operator()(const JsonArray *jArr) const +{ + d_this_p->makeArray(*jArr); +} + +void ConstructVisitor::operator()(const JsonNumber *jNum) const +{ + d_this_p->makeNumber(*jNum); +} + +void ConstructVisitor::operator()(const Json *json) const +{ + *d_this_p = *json; +} + +void ConstructVisitor::operator()( + std::initializer_list il) const +{ + JsonArray arr; + for (const auto & val : il) { + arr.pushBack(val); + } + d_this_p->makeArray(bslmf::MovableRefUtil::move(arr)); +} + +void ConstructVisitor::operator()(bslmf::Nil) const +{ + BSLA_UNREACHABLE; + BSLS_ASSERT_INVOKE_NORETURN("Unreachable"); +} + +#endif } // close unnamed namespace // --------------- @@ -133,6 +282,19 @@ bsl::ostream& JsonArray::print(bsl::ostream& stream, // class JsonObject // ---------------- +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS +bsl::pair JsonObject::insert( + const bsl::string_view& key, + const Json_Initializer& init) +{ + BSLS_ASSERT(bdlde::Utf8Util::isValid(key.data(), key.size())); + Json json(init, allocator()); + Member member(key, Json(), allocator()); + member.second = bslmf::MovableRefUtil::move(json); + return insert(bslmf::MovableRefUtil::move(member)); +} +#endif + bsl::ostream& JsonObject::print(bsl::ostream& stream, int level, int spacesPerLevel) const @@ -169,6 +331,14 @@ bsl::ostream& JsonObject::print(bsl::ostream& stream, // class Json // ---------- +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS +Json::Json(const Json_Initializer& init, bslma::Allocator *basicAllocator) +: d_value(JsonNull(), basicAllocator) +{ + init.get_storage().apply(ConstructVisitor(this)); +} +#endif + bsl::ostream& Json::print(bsl::ostream& stream, int level, int spacesPerLevel) const diff --git a/groups/bdl/bdljsn/bdljsn_json.h b/groups/bdl/bdljsn/bdljsn_json.h index 74e3dfa086..fe70844383 100644 --- a/groups/bdl/bdljsn/bdljsn_json.h +++ b/groups/bdl/bdljsn/bdljsn_json.h @@ -183,6 +183,206 @@ BSLS_IDENT("$Id: $") // subObject.insert("boolean", false); // json.theObject().insert("object", bsl::move(subObject)); // ``` +// +///Example 3: Using the 'visit' Method to Traverse a 'Json' Object +///--------------------------------------------------------------- +// The `Json` class provides the (overloaded) `visit` method that invokes a +// user-supplied "visitor" functor according to the current `type()` of the +// `Json` object. +// +// For example, suppose one needs to survey the structure of the `Json` object +// created in {Example 1} (and again in {Example 2}) and, in doing so, compile +// a tally of each of each of the `Json` sub-objects and their their types +// (i.e., object, array, string, ...). +// +// First, we define a compliant visitor class, `TallyByTypeVisitor`: +// ``` +// // ================== +// // TallyByTypeVisitor +// // ================== +// +// class TallyByTypeVisitor { +// +// int d_tally[6] = { 0, 0, 0, 0, 0, 0 }; +// +// public: +// // CREATORS +// +// /// Create a `TallyByTypeVisitor` object that when passed to the +// /// `Json::visit` method will increment the specified `tally` array +// /// according to `type()` and visit subordinate `Json` objects, if +// /// any. The behavior is undefined unless `tally` has at least +// /// 6 elements. +// explicit TallyByTypeVisitor(); +// +// // ACCESSORS +// +// /// Increment the element corresponding to object in the tally array +// /// supplied at construction and visit the value of each member of +// /// the specified `object`. +// void operator()(const JsonObject& object); +// +// +// /// Increment the element corresponding to array in the tally array +// /// supplied at construction and visit each element of the specified +// /// `array`. +// void operator()(const JsonArray& array); +// +// /// Increment the element corresponding to +// /// string/number/boolean/null in the tally array supplied at +// /// construction. Ignore the specified +// /// `string`/`number`/`boolean`/`null`. +// void operator()(const bsl::string& string ); +// void operator()(const JsonNumber& number ); +// void operator()(const bool& boolean); +// void operator()(const JsonNull& null ); +// +// /// Return the address of an array of 6 elements containing the +// /// tally by type. The array is ordered according to +// /// `JsonType::Enum`. +// const int *tally() const; +// }; +// ``` +// Notice that we have no need to change the value of the examined `Json` +// objects so we use a set of `operator()` overloads compatible with the +// `visit` accessor method. Accordingly, we careful below to use this visitor +// only with `const`-qualified `Json` objects +// +// Then, we define the constructor and the six `operator()` overloads. In each +// overload by type we increment the appropriate element in the user-supplied +// array of integers. +// ``` +// // ------------------ +// // TallyByTypeVisitor +// // ------------------ +// +// // CREATORS +// TallyByTypeVisitor::TallyByTypeVisitor() +// { +// } +// +// // ACCESSORS +// void TallyByTypeVisitor::operator()(const bsl::string& string) +// { +// (void) string; +// ++d_tally[bdljsn::JsonType::e_STRING]; +// } +// +// void TallyByTypeVisitor::operator()(const JsonNumber& number) +// { +// (void) number; +// ++d_tally[bdljsn::JsonType::e_NUMBER]; +// } +// +// void TallyByTypeVisitor::operator()(const bool& boolean) +// { +// (void) boolean; +// ++d_tally[bdljsn::JsonType::e_BOOLEAN]; +// } +// +// void TallyByTypeVisitor::operator()(const JsonNull& null) +// { +// (void) null; +// ++d_tally[bdljsn::JsonType::e_NULL]; +// } +// ``` +// Next, we define the visitor overload for array types. After incrementing +// the tally array we pass this same visitor object to the `Json` object in the +// array so those objects are included in the tally. +// ``` +// void TallyByTypeVisitor::operator()(const JsonArray& array) +// { +// ++d_tally[bdljsn::JsonType::e_ARRAY]; +// +// typedef JsonArray::ConstIterator ConstItr; +// +// for (ConstItr cur = array.cbegin(), +// end = array.cend(); +// end != cur; ++cur) { +// const Json constElement = *cur; +// constElement.visit(*this); +// } +// } +// +// const int *TallyByTypeVisitor::tally() const +// { +// return d_tally; +// } +// ``` +// Notice that `element` is `const`-qualified so the accessor `visit` method is +// invoked. +// +// Then, we implement the visitor overload for the object type. Examination +// of the object produces a sequence of name-value pairs where the `second` +// part is a `Json` object that we must visit. +// ``` +// void TallyByTypeVisitor::operator()(const JsonObject& object) +// { +// ++d_tally[bdljsn::JsonType::e_OBJECT]; +// +// typedef JsonObject::ConstIterator ConstItr; +// +// for (ConstItr cur = object.cbegin(), +// end = object.cend(); +// end != cur; ++cur) { +// const bsl::pair member = *cur; +// const Json& json = member.second; +// json.visit(*this); +// } +// } +// ``` +// Again, notice that this visitor is used as an argument to a +// `const`-qualified `Json` object. +// +// Finally, we make a survey of the `Json` object created in {Example 1} (and +// duplicated in {Example 2}). From visual inspection of the source JSON +// document we expect 10 `Json` objects distributed thus: +// +// * Object +// 1. top-level of the document is an object +// 2. '{"boolean": false }', interior object +// +// * Array +// 1. '[2.76, true]' +// +// * String +// 1. '"text"' +// +// * Number +// 1. '2.76' +// 2. '3.14' +// +// * Boolean +// 1. 'false', from the internal object +// 2. 'true', from the array +// 3. 'true', from top-level object +// +// * Null +// 1. null +// +// Use of our visitor functor on `example1` confirms these observations: +// ``` +// int main() +// { +// TallyByTypeVisitor visitor; +// +// const Json& constExample1 = example1; +// +// constExample1.visit(&visitor); +// +// const int *const tally = visitor.tally(); +// +// assert(2 == tally[bdljsn::JsonType::e_OBJECT ]); +// assert(1 == tally[bdljsn::JsonType::e_ARRAY ]); +// assert(1 == tally[bdljsn::JsonType::e_STRING ]); +// assert(2 == tally[bdljsn::JsonType::e_NUMBER ]); +// assert(3 == tally[bdljsn::JsonType::e_BOOLEAN]); +// assert(1 == tally[bdljsn::JsonType::e_NULL ]); +// +// return 0; +// } +// ``` + #include #include @@ -199,6 +399,7 @@ BSLS_IDENT("$Id: $") #include #include +#include #include #include @@ -208,16 +409,27 @@ BSLS_IDENT("$Id: $") #include #include +#include + #include #include #include #include #include +#include // 'bsl::uncaught_exceptions' #include +#include +#include +#include // 'bsl::enable_if', 'bsl::is_same' #include +#include // 'bsl::forward' #include +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) +#include +#endif + namespace BloombergLP { namespace bdljsn { @@ -225,6 +437,8 @@ namespace bdljsn { class Json; class JsonArray; class JsonObject; +class Json_Initializer; +class Json_MemberInitializer; // =============== // class JsonArray @@ -326,14 +540,13 @@ class JsonArray { bslma::Allocator *basicAllocator = 0); #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) - /// Create a `JsonArray` and insert (in order) each `Json` object in the /// specified `elements` initializer list. Optionally specify a /// `basicAllocator` used to supply memory. If `basicAllocator` is not /// specified, the currently installed default allocator is used. - JsonArray(std::initializer_list elements, - bslma::Allocator *basicAllocator = 0); // IMPLICIT - + JsonArray(std::initializer_list elements, + bslma::Allocator *basicAllocator = 0); + // IMPLICIT #endif // MANIPULATORS @@ -359,13 +572,13 @@ class JsonArray { /// specified `initializer` initializer list. Return a reference to /// `*this`. If an exception is thrown, `*this` is left in a valid but /// unspecified state. - JsonArray& operator=(std::initializer_list initializer); + JsonArray& operator=(std::initializer_list initializer); /// Assign to this object the value resulting from first clearing this /// JsonArray and then inserting (in order) each `Json` object in the /// specified `initializer` initializer list. If an exception is /// thrown, `*this` is left in a valid but unspecified state. - void assign(std::initializer_list initializer); + JsonArray& assign(std::initializer_list initializer); #endif /// Return a reference providing modifiable access to the element at the @@ -387,7 +600,7 @@ class JsonArray { /// behavior is undefined unless `first` and `last` refer to a range of /// valid values where `first` is at a position at or before `last`. template - void assign(INPUT_ITERATOR first, INPUT_ITERATOR last); + JsonArray& assign(INPUT_ITERATOR first, INPUT_ITERATOR last); // BDE_VERIFY pragma: -FABC01 @@ -521,23 +734,66 @@ class JsonArray { void popBack(); /// Append to the end of this `JsonArray` a copy of the specified - /// `json`. If an exception is thrown, `*this` is unaffected. Throw - /// `bsl::length_error` if `size() == maxSize()`. - void pushBack(const Json& json); + /// `json`/`array`/`object`/`number` and return a non-`const` reference + /// to this array. If an exception is thrown, `*this` is unaffected. + /// Throw `bsl::length_error` if `size() == maxSize()`. + JsonArray& pushBack(const Json& json); + JsonArray& pushBack(const JsonArray& array); + JsonArray& pushBack(const JsonObject& object); + JsonArray& pushBack(const JsonNumber& number); /// Append to the end of this `JsonArray` the specified move-insertable - /// `Json`. `value` is left in a valid but unspecified state. If an + /// `json`/`array`/`object`/`number` and return a non-`const` reference + /// to this array. `json`/`array`/`object`/`number` is left in a valid + /// but unspecified state. If an exception is thrown, '*this' is + /// unaffected. Throw `bsl::length_error` if `size() == maxSize()`. + JsonArray& pushBack(bslmf::MovableRef json); + JsonArray& pushBack(bslmf::MovableRef array); + JsonArray& pushBack(bslmf::MovableRef object); + JsonArray& pushBack(bslmf::MovableRef number); + + + /// Append to the end of this `JsonArray` a copy of the specified + /// `value` and return a non-`const` reference to this array. If an /// exception is thrown, `*this` is unaffected. Throw /// `bsl::length_error` if `size() == maxSize()`. - void pushBack(bslmf::MovableRef json); + JsonArray& pushBack(const JsonNull& value); + JsonArray& pushBack(bool value); + JsonArray& pushBack(int value); + JsonArray& pushBack(unsigned int value); + JsonArray& pushBack(long value); + JsonArray& pushBack(unsigned long value); + JsonArray& pushBack(long long value); + JsonArray& pushBack(unsigned long long value); + JsonArray& pushBack(float value); + JsonArray& pushBack(double value); + JsonArray& pushBack(bdldfp::Decimal64 value); - /// Change the size of this `JsonArray` to the specified `count`. If - /// `count < size()`, the elements in the range `[count .. size())` are - /// erased, and this function does not throw. If `count > size()`, the - /// (newly created) elements in the range `[size() .. count)` are - /// default-constructed `Json` objects, and if an exception is thrown, - /// `*this` is unaffected. Throw `bsl::length_error` if - /// `count > maxSize()`. + /// Append to the end of this `JsonArray` a copy of the specified + /// `string` and return a non-`const` reference to this array. If an + /// exception is thrown, `*this` is unaffected. Throw + /// `bsl::length_error` if `size() == maxSize()`. + JsonArray& pushBack(const char *string); + JsonArray& pushBack(const bsl::string_view& string); + + /// Append to the end of this `JsonArray` an element having the value of + /// the specified `string` by moving (in constant time) the contents of + /// `string` to the new element and return a non-`const` reference to + /// this array. `string` is left in a valid but unspecified state. + /// This function does not participate in overload resolution unless the + /// specified `STRING_TYPE` is `bsl::string`. The behavior is undefined + /// unless `string` is valid UTF-8 (see `bdlde::Utf8Util::isValid`). + template + typename bsl::enable_if::value, + JsonArray&>::type + pushBack(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string); + + /// Change the size of this JsonArray to the specified `count`. + /// If `count < size()`, the elements in the range `[count .. size())` are + /// erased `json` is ignored, and this method does not throw. If + /// `count > size()` and an exception is thrown, `*this` is unaffected. + /// Throw `bsl::length_error` if `count > maxSize()`. void resize(bsl::size_t count); /// Change the size of this JsonArray to the specified `count`, @@ -565,17 +821,15 @@ class JsonArray { // BDE_VERIFY pragma: -FABC01 - ConstIterator begin() const BSLS_KEYWORD_NOEXCEPT; - /// Return an iterator providing non-modifiable access to the first /// element in this `JsonArray`, and the past-the-end iterator if this /// `JsonArray` is empty. + ConstIterator begin() const BSLS_KEYWORD_NOEXCEPT; ConstIterator cbegin() const BSLS_KEYWORD_NOEXCEPT; - ConstIterator end() const BSLS_KEYWORD_NOEXCEPT; - /// Return the past-the-end iterator providing non-modifiable access to /// this `JsonArray`. + ConstIterator end() const BSLS_KEYWORD_NOEXCEPT; ConstIterator cend() const BSLS_KEYWORD_NOEXCEPT; /// Return a reference providing non-modifiable access to the first @@ -775,16 +1029,27 @@ class JsonObject { bslma::Allocator *basicAllocator = 0); #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) + /// Create an `JsonObject` containing a single member consisting of the + /// specified `key` and a value constructed from the specified `init`. + /// The behavior is undefined unless `key` is valid UTF-8 (see + /// `bdlde::Utf8Util::isValid`). + JsonObject(const std::string_view& key, + const Json_Initializer& init, + bslma::Allocator *basicAllocator = 0); // IMPLICIT +#endif /// Create an empty `JsonObject`, and then create a `Json` object for /// each in the range specified by `members` argument, ignoring elements /// having a key that appears earlier in the sequence. Optionally /// specify the `basicAllocator` used to supply memory. If /// `basicAllocator` is not specified, the currently installed default /// allocator is used to supply memory. The behavior is undefined - /// unless the keys of all `Member` objects in `members` are valid UTF-8 - /// (see `bdlde::Utf8Util::isValid`). - JsonObject(std::initializer_list members, - bslma::Allocator *basicAllocator = 0); // IMPLICIT + /// unless the keys of all `Json_MemberInitializer` objects in `members` + /// have a valid UTF-8 `key()` (see `bdlde::Utf8Util::isValid`). + JsonObject( + std::initializer_list members, + bslma::Allocator *basicAllocator = 0); + // IMPLICIT #endif // MANIPULATORS @@ -808,7 +1073,8 @@ class JsonObject { /// the specified `members` initializer list. Return a reference to /// `*this`. If an exception is thrown, `*this` is left in a valid but /// unspecified state. - JsonObject& operator=(std::initializer_list members); + JsonObject& operator=(std::initializer_list + members); #endif /// Return a reference providing modifiable access to the `Json` object @@ -904,7 +1170,7 @@ class JsonObject { template typename bsl::enable_if::value, - void>::type + JsonObject&>::type insert(INPUT_ITERATOR first, INPUT_ITERATOR last); #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) @@ -913,7 +1179,7 @@ class JsonObject { /// is not already contained. The behavior is undefined unless the keys /// of all `Member` objects inserted are valid UTF-8 (see /// `bdlde::Utf8Util::isValid`). - void insert(std::initializer_list members); + JsonObject& insert(std::initializer_list members); #endif /// Insert into this `JsonObject` a `Member` constructed from the @@ -930,6 +1196,20 @@ class JsonObject { const bsl::string_view& key, BSLS_COMPILERFEATURES_FORWARD_REF(VALUE) value); +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + /// Insert into this `JsonObject` a `Member` constructed from the + /// specified `key` and the specified `init`, respectively, if `key` + /// does not already exist in this `JsonObject`; otherwise, this method + /// has no effect. Return a `pair` whose `first` member is an iterator + /// referring to the (possibly newly inserted) `Member` object in this + /// `JsonObject` whose key is the equivalent to that of the object to be + /// inserted, and whose `second` member is `true` if a new value was + /// inserted, and `false` otherwise. The behavior is undefined unless + /// `key` is valid UTF-8 (see `bdlde::Utf8Util::isValid`). + bsl::pair insert(const bsl::string_view& key, + const Json_Initializer& init); +#endif + /// Exchange the value of this object with that of the specified `other` /// object. If an exception is thrown, both objects are left in valid /// but unspecified states. This operation guarantees O[1] complexity. @@ -948,19 +1228,17 @@ class JsonObject { // BDE_VERIFY pragma: -FABC01 - ConstIterator begin() const BSLS_KEYWORD_NOEXCEPT; - /// Return an iterator providing non-modifiable access to the first /// `Member` object in the sequence of `Member` objects maintained by /// this `JsonObject`, or the `end` iterator if this `JsonObject` is /// empty. + ConstIterator begin() const BSLS_KEYWORD_NOEXCEPT; ConstIterator cbegin() const BSLS_KEYWORD_NOEXCEPT; - ConstIterator end() const BSLS_KEYWORD_NOEXCEPT; - /// Return an iterator providing non-modifiable access to the /// past-the-end position in the sequence of `Member` objects maintained /// by this `JsonObject`. + ConstIterator end() const BSLS_KEYWORD_NOEXCEPT; ConstIterator cend() const BSLS_KEYWORD_NOEXCEPT; // BDE_VERIFY pragma: +FABC01 @@ -1072,8 +1350,10 @@ class Json { // FRIENDS friend bool operator==(const Json&, const Json&); friend bool operator!=(const Json&, const Json&); + template friend void hashAppend(HASHALG&, const Json&); + friend void swap(Json&, Json&); public: @@ -1108,24 +1388,6 @@ class Json { /// `original`. Use the specified `basicAllocator` to supply memory. Json(bslmf::MovableRef original, bslma::Allocator *basicAllocator); - /// Create a `Json` object having the type `JsonArray` and the same - /// value as the specified `array`. Optionally specify the - /// `basicAllocator` used to supply memory. If `basicAllocator` is not - /// specified, the currently installed default allocator is used to - /// supply memory. - explicit Json(const JsonArray& array, - bslma::Allocator *basicAllocator = 0); - - /// Create a `Json` object having the type `JsonArray` and the same - /// value as the specified `array` object by moving (in constant time) - /// the contents of `array` to the new `Json` object. Optionally - /// specify the `basicAllocator` used to supply memory. If - /// `basicAllocator` is not specified, the allocator associated with - /// `array` is propagated for use in the newly-created `Json` object. - /// `array` is left in a valid but unspecified state. - explicit Json(bslmf::MovableRef array, - bslma::Allocator *basicAllocator = 0); - /// Create a `Json` object having the type `bool` and the same value as /// the specified `boolean`. Optionally specify the `basicAllocator` /// used to supply memory. If `basicAllocator` is not specified, the @@ -1138,51 +1400,75 @@ class Json { /// currently installed default allocator is used to supply memory. explicit Json(const JsonNull& null, bslma::Allocator *basicAllocator = 0); - /// Create a `Json` object having the type `JsonNumber` and the same - /// value as the specified `number`. Optionally specify the - /// `basicAllocator` used to supply memory. If `basicAllocator` is not - /// specified, the currently installed default allocator is used to - /// supply memory. - explicit Json(float number, bslma::Allocator *basicAllocator = 0); - explicit Json(double number, bslma::Allocator *basicAllocator = 0); - explicit Json(bdldfp::Decimal64 number, - bslma::Allocator *basicAllocator = 0); - explicit Json(int number, bslma::Allocator *basicAllocator = 0); - explicit Json(unsigned int number, bslma::Allocator *basicAllocator = 0); - explicit Json(bsls::Types::Int64 number, - bslma::Allocator *basicAllocator = 0); - explicit Json(bsls::Types::Uint64 number, - bslma::Allocator *basicAllocator = 0); + /// Create a `Json` object having the type `JsonArray`/`JsonObject`/ + /// `JsonNumber` and the value of the specified + /// `array`/`object`/`number`. Optionally specify `basicAllocator` + /// used to supply memory. If `basicAllocator` is not specified, the + /// currently installed default allocator is used to supply memory. + Json(const JsonArray& array, + bslma::Allocator *basicAllocator = 0); // IMPLICIT + Json(const JsonObject& object, + bslma::Allocator *basicAllocator = 0); // IMPLICIT explicit Json(const JsonNumber& number, bslma::Allocator *basicAllocator = 0); - /// Create a `Json` object having the type `JsonNumber` and the same - /// value as the specified `number` object by moving (in constant time) - /// the contents of `number` to the new `Json` object. Optionally - /// specify the `basicAllocator` used to supply memory. If - /// `basicAllocator` is not specified, the allocator associated with - /// `number` is propagated for use in the newly-created `Json` object. - /// `number` is left in a valid but unspecified state. + /// Create a `Json` object having the type `JsonArray`/`JsonObject`/ + /// `JsonNumber` and the value of the specified `array`/`object`/ + /// `number` object by moving the contents of `array`/`object`/`number` + /// to the new object. Optionally specify `basicAllocator` used to + /// supply memory. If `basicAllocator` is not specified, the allocator + /// associated with `array`/`object`/`number` is propagated for use in + /// the newly-created `Json` object. The allocator or `array`/`object`/ + /// `number` is unchanged and is otherwise left in (valid) unspecified + /// state. + Json(bslmf::MovableRef array, + bslma::Allocator *basicAllocator = 0); + // IMPLICIT + Json(bslmf::MovableRef object, + bslma::Allocator *basicAllocator = 0); + // IMPLICIT explicit Json(bslmf::MovableRef number, bslma::Allocator *basicAllocator = 0); - /// Create a `Json` object having the type `JsonObject` and the same - /// value as the specified `object`. Optionally specify the - /// `basicAllocator` used to supply memory. If `basicAllocator` is not - /// specified, the currently installed default allocator is used to - /// supply memory. - explicit Json(const JsonObject& object, - bslma::Allocator *basicAllocator = 0); +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + /// Create a `Json` object having the same structure as the specified + /// `init`. Optionally specify the `basicAllocator` used to supply + /// memory. If `basicAllocator` is not specified, the currently + /// installed default allocator is used to supply memory. + Json(const Json_Initializer& init, bslma::Allocator *basicAllocator = 0); + // IMPLICIT - /// Create a `Json` object having the type `JsonObject` and the same - /// value as the specified `object` by moving (in constant time) the - /// contents of `object` to the new `Json` object. Optionally specify - /// the `basicAllocator` used to supply memory. If `basicAllocator` is - /// not specified, the allocator associated with `object` is propagated - /// for use in the newly-created `Json` object. `object` is left in a - /// valid but unspecified state. - explicit Json(bslmf::MovableRef object, - bslma::Allocator *basicAllocator = 0); + /// Create a `Json` object having the same structure as the specified + /// `initializer` list. Optionally specify the `basicAllocator` used to + /// supply memory. If `basicAllocator` is not specified, the currently + /// installed default allocator is used to supply memory. + Json(std::initializer_list initializer, + bslma::Allocator *basicAllocator = 0); + // IMPLICIT +#endif + + /// Create a `Json` object having the type `JsonNumber` and the the + /// specified `value`. Optionally specify the `basicAllocator` used to + /// supply memory. If `basicAllocator` is not specified, the currently + /// installed default allocator is used to supply memory. + explicit Json(int value, + bslma::Allocator *basicAllocator = 0); + explicit Json(unsigned int value, + bslma::Allocator *basicAllocator = 0); + explicit Json(long value, + bslma::Allocator *basicAllocator = 0); + explicit Json(unsigned long value, + bslma::Allocator *basicAllocator = 0); + explicit Json(long long value, + bslma::Allocator *basicAllocator = 0); + explicit Json(unsigned long long value, + bslma::Allocator *basicAllocator = 0); + explicit Json(float value, + bslma::Allocator *basicAllocator = 0); + explicit Json(double value, + bslma::Allocator *basicAllocator = 0); + explicit Json(bdldfp::Decimal64 value, + bslma::Allocator *basicAllocator = 0); // BDE_VERIFY pragma: -FD06 'string' and 'bsl::string' are too similar // BDE_VERIFY pragma: -FD07 'string' and 'bsl::string' are too similar @@ -1193,9 +1479,12 @@ class Json { /// specified, the currently installed default allocator is used to /// supply memory. The behavior is undefined unless `string` is valid /// UTF-8 (see `bdlde::Utf8Util::isValid`). - explicit Json(const char *string, bslma::Allocator *basicAllocator = 0); + explicit Json(const char *string, + bslma::Allocator *basicAllocator = 0); +#ifndef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS explicit Json(const bsl::string_view& string, bslma::Allocator *basicAllocator = 0); +#endif // BDE_VERIFY pragma: -IND01 DEDUCE macro confuses bde_verify @@ -1210,11 +1499,11 @@ class Json { /// `bsl::string`. The behavior is undefined unless `string` is valid /// UTF-8 (see `bdlde::Utf8Util::isValid`). template - explicit Json( - BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string, - bslma::Allocator *basicAllocator = 0, - typename bsl::enable_if< - bsl::is_same::value>::type * = 0); + Json(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string, + bslma::Allocator *basicAllocator = 0, + typename bsl::enable_if< + bsl::is_same::value>::type * = 0); + // IMPLICIT // BDE_VERIFY pragma: +IND01 DEDUCE macro confuses bde_verify // BDE_VERIFY pragma: +FD06 'string' and 'bsl::string' are too similar @@ -1239,14 +1528,16 @@ class Json { /// the specified `rhs`, and return a reference providing modifiable /// access to this object. The value currently held by this object (if /// any) is destroyed if that value's type is not `JsonNumber`. - Json& operator=(float rhs); - Json& operator=(double rhs); - Json& operator=(bdldfp::Decimal64 rhs); - Json& operator=(int rhs); - Json& operator=(unsigned int rhs); - Json& operator=(bsls::Types::Int64 rhs); - Json& operator=(bsls::Types::Uint64 rhs); - Json& operator=(const JsonNumber& rhs); + Json& operator=(const JsonNumber& rhs); + Json& operator=(int rhs); + Json& operator=(unsigned int rhs); + Json& operator=(long rhs); + Json& operator=(unsigned long rhs); + Json& operator=(long long rhs); + Json& operator=(unsigned long long rhs); + Json& operator=(float rhs); + Json& operator=(double rhs); + Json& operator=(bdldfp::Decimal64 rhs); /// Assign to this object the value of the specified `rhs` object, and /// return a reference providing modifiable access to this object. The @@ -1263,8 +1554,8 @@ class Json { /// any) is destroyed if that value's type is not `bsl::string`. The /// behavior is undefined unless `rhs` is valid UTF-8 (see /// `bdlde::Utf8Util::isValid`). - Json& operator=(const char *rhs); - Json& operator=(const bsl::string_view& rhs); + Json& operator=(const char *rhs); + Json& operator=(const bsl::string_view& rhs); /// Assign to this object the value of the specified `rhs` object, and /// return a reference providing modifiable access to this object. The @@ -1337,7 +1628,7 @@ class Json { /// This method first destroys the current value held by this object /// (even if the type currently held is `JsonArray`). JsonArray& makeArray(); - JsonArray& makeArray(const JsonArray& array); + JsonArray& makeArray(const JsonArray& array); JsonArray& makeArray(bslmf::MovableRef array); /// Create an instance of type `bool` in this object and return a @@ -1358,7 +1649,7 @@ class Json { /// This method first destroys the current value held by this object /// (even if the type currently held is `JsonNumber`). JsonNumber& makeNumber(); - JsonNumber& makeNumber(const JsonNumber& number); + JsonNumber& makeNumber(const JsonNumber& number); JsonNumber& makeNumber(bslmf::MovableRef number); /// Create an instance of type `JsonObject` in this object, using the @@ -1368,7 +1659,7 @@ class Json { /// This method first destroys the current value held by this object /// (even if the type currently held is `JsonObject`). JsonObject& makeObject(); - JsonObject& makeObject(const JsonObject& object); + JsonObject& makeObject(const JsonObject& object); JsonObject& makeObject(bslmf::MovableRef object); // BDE_VERIFY pragma: -FD06 'string' and 'bsl::string' are too similar @@ -1381,8 +1672,8 @@ class Json { /// (even if the type currently held is `bsl::string`). The behavior is /// undefined unless `string` is valid UTF-8 (see /// `bdlde::Utf8Util::isValid`). - void makeString(const char *string); - void makeString(const bsl::string_view& string); + void makeString(const char *string); + void makeString(const bsl::string_view& string); /// Create an instance of type `bsl::string` in this object, using the /// allocator currently held by this object to supply memory. @@ -1400,13 +1691,6 @@ class Json { // BDE_VERIFY pragma: +FD06 'string' and 'bsl::string' are too similar // BDE_VERIFY pragma: +FD07 'string' and 'bsl::string' are too similar - /// Exchange the value of this with that of the specified `other`. If - /// an exception is thrown, both objects are left in valid but - /// unspecified states. This operation guarantees O[1] complexity. The - /// behavior is undefined unless this object was created with the same - /// allocator as `other`. - void swap(Json& other); - /// Return a reference providing modifiable access to the value of type /// `JsonArray` held by this object. The behavior is undefined unless /// `isArray()` returns true. @@ -1464,7 +1748,7 @@ class Json { /// object; if the `JsonObject` does not already contain a `Json` object /// associated with `key`, first insert a new default-constructed `Json` /// object associated with `key`. The behavior is undefined unless - /// `isObject()` returns true. + /// one of `isObject()` or `isNull()` returns true. Json& operator[](const bsl::string_view& key); /// Return a reference providing modifiable access to the element at the @@ -1473,29 +1757,168 @@ class Json { /// `index < theArray().size()`. Json& operator[](bsl::size_t index); +// BDE_VERIFY pragma: -FABC01 // not in order + + // 'pushBack' methods + + /// Append to the end of this `Json` object's array a copy of the + /// specified `json`/`array`/`object`/`number` and return a non-`const` + /// reference to this object. If `isNull()`, then the type is changed + /// to array. If an exception is thrown, type to array. `*this` is + /// unaffected. Throw `bsl::length_error` if `size() == maxSize()`. + /// The behavior is undefined unless `isArray()` or `isNull()`. + Json& pushBack(const Json& json); + Json& pushBack(const JsonArray& array); + Json& pushBack(const JsonObject& object); + Json& pushBack(const JsonNumber& number); + + /// Append to the end of this `Json` object's array the specified + /// move-insertable `json`/`array`/`object`/`number` and return a + /// non-`const` reference to this object. + /// `json`/`array`/`object`/`number` is left in a valid but unspecified + /// state. If `isNull`, then the type is changed to array. If an + /// exception is thrown, `*this` is unaffected. Throw + /// `bsl::length_error` if `size() == maxSize()`. The behavior is + /// undefined unless `isArray()` or `isNull()`. + Json& pushBack(bslmf::MovableRef json); + Json& pushBack(bslmf::MovableRef array); + Json& pushBack(bslmf::MovableRef object); + Json& pushBack(bslmf::MovableRef number); + + /// Append to the end of this `Json` object's array a copy of the + /// specified `value` and return a non-`const` reference to this object. + /// If `isNull()`, then type is changed to array. If an exception is + /// thrown, `*this` is unaffected. Throw `bsl::length_error` if + /// `size() == maxSize()`. The behavior is undefined unless `isArray()` + /// or `isNull()`. + Json& pushBack(const JsonNull& value); + Json& pushBack(bool value); + Json& pushBack(int value); + Json& pushBack(unsigned int value); + Json& pushBack(long value); + Json& pushBack(unsigned long value); + Json& pushBack(long long value); + Json& pushBack(unsigned long long value); + Json& pushBack(float value); + Json& pushBack(double value); + Json& pushBack(bdldfp::Decimal64 value); + + /// Append to the end of this `Json` objects' array a copy of the + /// specified `string` and return a non-`const` reference to this + /// object. If `isNull`, then type is changed to array. If an + /// exception is thrown, `*this` is unaffected. Throw + /// `bsl::length_error` if `size() == maxSize()`. The behavior is + /// undefined unless `isArray()` or `isNull`. + Json& pushBack(const char *string); + Json& pushBack(const bsl::string_view& string); + + // Append to the end of this `Json` objects' array an element having + // value of the specified `string` by moving (in constant time) the + // contents of `string` to the new element and return a non-`const` + // reference to this object. If `isNull()`, then the type is changed + // to array. `string` is left in a valid but unspecified state. This + // function does not participate in overload resolution unless the + // specified `STRING_TYPE` is `bsl::string`. The behavior is undefined + // unless `string` is valid UTF-8 (see `bdlde::Utf8Util::isValid`). + // The behavior is undefined unless `isArray()` or `isNull()`. + template + typename bsl::enable_if::value, + Json&>::type + pushBack(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string); + + /// Return the result (having `RETURN_TYPE`) of invoking the specified + /// `visitor` (callable) object on this `Json` object. `visitor` must + /// be callable as if it provides the following overloads of + /// `operator()`: + ///``` + /// RETURN_TYPE operator()(JsonObject *object ); + /// RETURN_TYPE operator()(JsonArray *array ); + /// RETURN_TYPE operator()(bsl::string *string ); + /// RETURN_TYPE operator()(JsonNumber *number ); + /// RETURN_TYPE operator()(bool *boolean); + /// RETURN_TYPE operator()(JsonNull *null ); + ///``` + /// The overload corresponding to the current `type()` of this `Json` + /// object is invoked. For C++03 `visitor` must be `const` qualified. + /// The behavior is undefined unless all of these six overloads are + /// defined. The behavior is undefined if the `string` overload leaves + /// this `Json` object with a string that is not valid UTF-8. Note that + /// overloads that are not applicable in a given application need not do + /// anything other than return. Also note that if `visitor` is `const` + /// qualified then each of its `operator()` overloads must also be + /// `const` qualified. + template + RETURN_TYPE visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor); + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY + /// Return the result (having a deduced type) of invoking the specified + /// `visitor` (callable) object on this `Json` object. `visitor` must + /// be callable as if it provides the following overloads of + /// `operator()`, any of which can have deduced return types: + ///``` + /// decltype(auto) operator()(JsonObject *object ); + /// decltype(auto) operator()(JsonArray *array ); + /// decltype(auto) operator()(bsl::string *string ); + /// decltype(auto) operator()(JsonNumber *number ); + /// decltype(auto) operator()(bool *boolean); + /// decltype(auto) operator()(JsonNull *null ); + ///``` + /// The overload corresponding to the current `type()` of this `Json` + /// object is invoked. The behavior is undefined unless all of these + /// six overloads are defined *and* each has the same return type. The + /// behavior is undefined if the `string` overload leaves this `Json` + /// object with a string that is not valid UTF-8. Note that overloads + /// that are not applicable in a given application need not do anything + /// other than return. Also note that if `visitor` is `const` qualified + /// then each of its `operator()` overloads must also be `const` + /// qualified. + template + decltype(auto) visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor); +#endif + + // Aspects + + /// Exchange the value of this with that of the specified `other`. If + /// an exception is thrown, both objects are left in valid but + /// unspecified states. This operation guarantees O[1] complexity. The + /// behavior is undefined unless this object was created with the same + /// allocator as `other`. + void swap(Json& other); + // ACCESSORS - // BDE_VERIFY pragma: -FABC01 - /// Load into the specified `result` the integer value of the value of type - /// `JsonNumber` held by this object. Return 0 on success, - /// `JsonNumber::k_OVERFLOW` if `value` is larger than can be represented - /// by `result`, `JsonNumber::k_UNDERFLOW` if `value` is smaller than can - /// be represented by `result`, and `JsonNumber::k_NOT_INTEGRAL` if `value` - /// is not an integral number (i.e., there is a fractional part). For - /// underflow, `result` will be loaded with the minimum representable - /// value, for overflow, `result` will be loaded with the maximum - /// representable value, for non-integral values `result` will be loaded - /// with the integer part of `value` (truncating the value to the nearest - /// integer). If the result is not an integer and also either overflows or - /// underflows, it is treated as an overflow or underflow (respectively). - /// Note that this operation returns a status value (unlike similar - /// floating point conversions) because typically it is an error if a - /// conversion to an integer results in an inexact value. The behavior is - /// undefined unless `isNumber()` returns true. - int asInt(int *result) const; - int asInt64(bsls::Types::Int64 *result) const; - int asUint(unsigned int *result) const; - int asUint64(bsls::Types::Uint64 *result) const; +// BDE_VERIFY pragma: -FABC01 + + /// Load into the specified `result` the integer value of the value of + /// type `JsonNumber` held by this object. Return 0 on success, + /// `JsonNumber::k_OVERFLOW` if `value` is larger than can be + /// represented by `result`, `JsonNumber::k_UNDERFLOW` if `value` is + /// smaller than can be represented by `result`, and + /// `JsonNumber::k_NOT_INTEGRAL` if `value` is not an integral number + /// (i.e., there is a fractional part). For underflow, `result` will be + /// loaded with the minimum representable value, for overflow, `result` + /// will be loaded with the maximum representable value, for + /// non-integral values `result` will be loaded with the integer part of + /// `value` (truncating the value to the nearest integer). If the + /// result is not an integer and also either overflows or underflows, it + /// is treated as an overflow or underflow (respectively). Note that + /// this operation returns a status value (unlike similar floating point + /// conversions) because typically it is an error if a conversion to an + /// integer results in an inexact value. The behavior is undefined + /// unless `isNumber()` returns true. Also note that on all supported + /// platforms `bsls::Types::Int64` and `bsls::Types::Uint64` are, + /// respectively, aliases for `long long` and `unsigned long long`. + int asShort (short *result) const; + int asInt (int *result) const; + int asLong (long *result) const; + int asLonglong (long long *result) const; + int asInt64 (bsls::Types::Int64 *result) const; + int asUshort (unsigned short *result) const; + int asUint (unsigned int *result) const; + int asUlong (unsigned long *result) const; + int asUlonglong(unsigned long long *result) const; + int asUint64 (bsls::Types::Uint64 *result) const; /// Return the closest floating point representation to the value of the /// type `JsonNumber` held by this object. If this number is outside the @@ -1505,7 +1928,7 @@ class Json { double asDouble() const; bdldfp::Decimal64 asDecimal64() const; - // BDE_VERIFY pragma: +FABC01 +// BDE_VERIFY pragma: +FABC01 // not in order /// Load the specified `result` with the closest floating point /// representation to the value of type `JsonNumber` held by this @@ -1626,6 +2049,54 @@ class Json { /// `isArray() || isObject()` evaluates to true. bsl::size_t size() const; + /// Return the result (having `RETURN_TYPE`) of invoking the specified + /// `visitor` (callable) object on this `Json` object. `visitor` must + /// be callable as if it provides the following overloads of + /// `operator()`: + ///``` + /// RETURN_TYPE operator()(const JsonObject& object ); + /// RETURN_TYPE operator()(const JsonArray& array ); + /// RETURN_TYPE operator()(const bsl::string& string ); + /// RETURN_TYPE operator()(const JsonNumber& number ); + /// RETURN_TYPE operator()(const bool& boolean); + /// RETURN_TYPE operator()(const JsonNull& null ); + ///``` + /// The overload corresponding to the current `type()` of this `Json` + /// object is invoked. For C++03 visitor must be `const`-qualified. + /// The behavior is undefined unless all of these six overloads are + /// defined. Note that overloads that are not applicable in a given + /// application need not do anything other than return. Also note that + /// if `visitor` is `const` qualified then each of its `operator()` + /// overloads must also be `const` qualified. + template + RETURN_TYPE visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor) + const; + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY + /// Return the result (having a deduced type) of invoking the specified + /// `visitor` (callable) object on this `Json` object. `visitor` must + /// be callable as if it provides the following overloads of + /// 'operator()', any of which can have deduced return types: + ///``` + /// decltype(auto) operator()(JsonObject *object ); + /// decltype(auto) operator()(JsonArray *array ); + /// decltype(auto) operator()(bsl::string *string ); + /// decltype(auto) operator()(JsonNumber *number ); + /// decltype(auto) operator()(bool *boolean); + /// decltype(auto) operator()(JsonNull *null ); + ///``` + /// The overload corresponding to the current `type()` of this `Json` + /// object is invoked. The behavior is undefined unless all of these + /// six overloads are defined *and* each has the same return type. Note + /// that overloads that are not applicable in a given application need + /// not do anything other than return. Also note that if `visitor` is + /// `const` qualified then each of its `operator()` overloads must also + /// be `const` qualified. + template + decltype(auto) visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor) + const; +#endif + // Aspects /// Return the allocator used by this object to allocate memory. @@ -1673,6 +2144,80 @@ bool operator==(const Json& lhs, const Json& rhs); /// have the same value. bool operator!=(const Json& lhs, const Json& rhs); +// Return `true` if the specified `lhs` and `rhs` arguments have the same +// value, and `false` otherwise. Two arguments have the same value if the +// `Json` object has the same value as a `Json` object constructed from the +// other argument. +bool operator==(const JsonArray& lhs, const Json& rhs); +bool operator==(const JsonObject& lhs, const Json& rhs); +bool operator==(const JsonNumber& lhs, const Json& rhs); +bool operator==(const JsonNull& lhs, const Json& rhs); +bool operator==(bool lhs, const Json& rhs); +bool operator==(int lhs, const Json& rhs); +bool operator==(unsigned int lhs, const Json& rhs); +bool operator==(long lhs, const Json& rhs); +bool operator==(unsigned long lhs, const Json& rhs); +bool operator==(long long lhs, const Json& rhs); +bool operator==(unsigned long long lhs, const Json& rhs); +bool operator==(float lhs, const Json& rhs); +bool operator==(double lhs, const Json& rhs); +bool operator==(bdldfp::Decimal64 lhs, const Json& rhs); +bool operator==(const char *lhs, const Json& rhs); +bool operator==(const bsl::string_view& lhs, const Json& rhs); +bool operator==(const Json& lhs, const JsonArray& rhs); +bool operator==(const Json& lhs, const JsonObject& rhs); +bool operator==(const Json& lhs, const JsonNumber& rhs); +bool operator==(const Json& lhs, const JsonNull& rhs); +bool operator==(const Json& lhs, bool rhs); +bool operator==(const Json& lhs, int rhs); +bool operator==(const Json& lhs, unsigned int rhs); +bool operator==(const Json& lhs, long rhs); +bool operator==(const Json& lhs, unsigned long rhs); +bool operator==(const Json& lhs, long long rhs); +bool operator==(const Json& lhs, unsigned long long rhs); +bool operator==(const Json& lhs, float rhs); +bool operator==(const Json& lhs, double rhs); +bool operator==(const Json& lhs, bdldfp::Decimal64 rhs); +bool operator==(const Json& lhs, const char *rhs); +bool operator==(const Json& lhs, const bsl::string_view& rhs); + +// Return `true` if the specified `lhs` and `rhs` arguments do not have the +// same value, and `false` otherwise. Two arguments do not the same value +// if the `Json` object does not has the same value as a `Json` object +// constructed from the other argument. +bool operator!=(const JsonArray& lhs, const Json& rhs); +bool operator!=(const JsonObject& lhs, const Json& rhs); +bool operator!=(const JsonNumber& lhs, const Json& rhs); +bool operator!=(const JsonNull& lhs, const Json& rhs); +bool operator!=(bool lhs, const Json& rhs); +bool operator!=(int lhs, const Json& rhs); +bool operator!=(unsigned int lhs, const Json& rhs); +bool operator!=(long lhs, const Json& rhs); +bool operator!=(unsigned long lhs, const Json& rhs); +bool operator!=(long long lhs, const Json& rhs); +bool operator!=(unsigned long long lhs, const Json& rhs); +bool operator!=(float lhs, const Json& rhs); +bool operator!=(double lhs, const Json& rhs); +bool operator!=(bdldfp::Decimal64 lhs, const Json& rhs); +bool operator!=(const char *lhs, const Json& rhs); +bool operator!=(const bsl::string_view& lhs, const Json& rhs); +bool operator!=(const Json& lhs, const JsonArray& rhs); +bool operator!=(const Json& lhs, const JsonObject& rhs); +bool operator!=(const Json& lhs, const JsonNumber& rhs); +bool operator!=(const Json& lhs, const JsonNull& rhs); +bool operator!=(const Json& lhs, bool rhs); +bool operator!=(const Json& lhs, int rhs); +bool operator!=(const Json& lhs, unsigned int rhs); +bool operator!=(const Json& lhs, long rhs); +bool operator!=(const Json& lhs, unsigned long rhs); +bool operator!=(const Json& lhs, long long rhs); +bool operator!=(const Json& lhs, unsigned long long rhs); +bool operator!=(const Json& lhs, float rhs); +bool operator!=(const Json& lhs, double rhs); +bool operator!=(const Json& lhs, bdldfp::Decimal64 rhs); +bool operator!=(const Json& lhs, const char *rhs); +bool operator!=(const Json& lhs, const bsl::string_view& rhs); + /// Invoke the specified `hashAlg` on the attributes of the specified /// `object`. template @@ -1685,6 +2230,247 @@ void hashAppend(HASHALG& hashAlg, const Json& object); /// the same allocator as `b`. void swap(Json& a, Json& b); +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + + // ====================== + // class Json_Initializer + // ====================== + +/// This component-private type is designed capture a set of values that can be +/// used to initialize a `Json` object, or an element in a `JsonArray`. It is +/// not modifiable, and (except for small types like `int`) does not own the +/// objects that are passed in. The purpose here is to enable +/// list-initialization (including nested lists) of JSON objects. +class Json_Initializer { + + public: + // PUBLIC TYPES + typedef bdlb::Variant, + const JsonObject *, + const JsonArray *, + const JsonNumber *, + const Json *> Storage; + + private: + // DATA + Storage d_storage; + + public: + // CREATORS + + /// Create a `Json_Initializer` object containing a `JsonNull` in the + /// internal variant. + Json_Initializer(); + Json_Initializer(const JsonNull& ); // IMPLICIT + + // Create a `Json_Initializer` object containing the boolean value of + // the specified `b` in the internal variant. + Json_Initializer(bool b); // IMPLICIT + + /// Create a `Json_Initializer` object containing the value of the + /// specified `num` in the internal variant. If `num` is a signed + /// integral type, then the value is stored as a `long long`. If `num` + /// is an unsigned integral type, then the value is stored as an + /// `unsigned long long`. + template ::value>::type> + Json_Initializer(NUMBER num); // IMPLICIT + + /// Create a `Json_Initializer` object containing the specified `f`. + Json_Initializer(float f); // IMPLICIT + + /// Create a `Json_Initializer` object containing the specified `d`. + Json_Initializer(double d); // IMPLICIT + + /// Create a `Json_Initializer` object containing the specified `value`. + Json_Initializer(bdldfp::Decimal64 value); // IMPLICIT + + /// Create a `Json_Initializer` object containing a `bsl::string_view` + /// referring to the null-terminated sequence pointed to by the + /// specified `p`. + Json_Initializer(const char *p); // IMPLICIT + + /// Create a `Json_Initializer` object containing a 'bsl::string_view' + /// referring to the contents of the specified `s`. + Json_Initializer(const bsl::string& s); // IMPLICIT + Json_Initializer(const std::string& s); // IMPLICIT +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING + Json_Initializer(const std::pmr::string& s); // IMPLICIT +#endif + + /// Create a `Json_Initializer` object containing a `bsl::string_view` + /// containing the specified `sv`. + Json_Initializer(const bsl::string_view& sv); // IMPLICIT + +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) \ +&& !defined(BSLS_LIBRARYFEATURES_HAS_CPP20_BASELINE_LIBRARY) + /// Create a `Json_Initializer` object containing a `bsl::string_view` + /// containing the specified `sv`. + Json_Initializer(const std::string_view& sv); // IMPLICIT +#endif + + /// Create a `Json_Initializer` object containing an initializer list + /// with the value of the specified `il`. + Json_Initializer(std::initializer_list il); // IMPLICIT + + /// Create a `Json_Initializer` object containing a pointer to the + /// specified `jObj`. + Json_Initializer(const JsonObject& jObj); // IMPLICIT + + /// Create a `Json_Initializer` object containing a pointer to the + /// specified `jArr`. + Json_Initializer(const JsonArray& jArr); // IMPLICIT + + /// Create a `Json_Initializer` object containing a pointer to the + /// specified `jNum`. + Json_Initializer(const JsonNumber& jNum); // IMPLICIT + + /// Create a `Json_Initializer` object containing a pointer to the + /// specified `json`. + Json_Initializer(const Json& json); // IMPLICIT + + // ACCESSORS + + /// Return a reference providing non-modifiable access to the internal + /// variant. + const Storage& get_storage() const; +}; + +/// This component-private type is designed capture a set of values that can +/// be used to initialize an element in a `JsonObject`. The purpose here is +/// to enable list-initialization (including nested lists) of JSON objects. +class Json_MemberInitializer { + + private: + // DATA + bsl::string_view d_first; + Json_Initializer d_second; + + public: +// BDE_VERIFY pragma: push +// BDE_VERIFY pragma: -IEC01 // implicit conversions is the reason we exist + + // CREATORS + + /// Create a `Json_MemberInitializer` object a key whose value is equal to + /// the specified `s` and whose value is equal to the specified `ji`. + /// specified `jNum`. + Json_MemberInitializer(const char *s, const Json_Initializer& ji); + Json_MemberInitializer(const bsl::string& s, const Json_Initializer& ji); + Json_MemberInitializer(const std::string& s, const Json_Initializer& ji); + + /// Create a `Json_MemberInitializer` object a key whose value is equal to + /// the specified `sv` and whose value is equal to the specified `ji`. + Json_MemberInitializer(const bsl::string_view& sv, + const Json_Initializer& ji); + +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) \ +&& !defined(BSLS_LIBRARYFEATURES_HAS_CPP20_BASELINE_LIBRARY) + // Create a `Json_MemberInitializer` object a key whose value is equal to + // the specified `sv` and whose value is equal to the specified `ji`. + Json_MemberInitializer(const std::string_view& sv, + const Json_Initializer& ji); + +#endif + + // ACCESSORS + + /// Return the key that the object was created with. + bsl::string_view key() const; + + /// Return a reference providing non-modifiable access to the value that + /// the object was created with. + const Json_Initializer& value() const; + +// BDE_VERIFY pragma: pop + +}; + +#endif // BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + + // ===================== + // struct Json_VisitUtil + // ===================== + +/// This component-private utility `struct` provides a namespace for a suite of +/// operations used in the implementation of "visit" operations provided by +/// classes in this component. +struct Json_VisitUtil { + + // CLASS METHODS + + /// Invoke the specified `visitor` (callable object) with the specified + /// `argument` and return the computed value of (template parameter) the + /// `RETURN_TYPE`. Note that in typical use, `argument` has the + /// current value and type of a `Json` object. + template + static + RETURN_TYPE invokeVisitor(VISITOR *visitor, + const ARGUMENT& argument); + template + static + RETURN_TYPE invokeVisitor(VISITOR& visitor, + const ARGUMENT& argument); + template + static + RETURN_TYPE invokeVisitor(const VISITOR& visitor, + const ARGUMENT& argument); + template + static + RETURN_TYPE invokeVisitor(BSLMF_MOVABLEREF_DEDUCE(VISITOR) visitor, + const ARGUMENT& argument); +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY + template + static + decltype(auto) invokeVisitor(const VISITOR& visitor, + const ARGUMENT& argument); + template + static + decltype(auto) invokeVisitor(BSLMF_MOVABLEREF_DEDUCE(VISITOR) visitor, + const ARGUMENT& argument); +#endif + +}; + + // =============================== + // class Json_StringInvariantGuard + // =============================== + +/// This is a component-private guard class. +class Json_StringInvariantGuard { + + // DATA + bsl::string *d_string_p; + + public: + // CREATORS + + /// Create an object that guards the invariant (valid UTF-8) of the + /// JSON string at the specified `string`. + explicit Json_StringInvariantGuard(bsl::string *string); + + /// Validate the JSON string specified on construction using + /// `BSLS_ASSERT` and destroy this guard object. + ~Json_StringInvariantGuard() BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(false); +}; + // ============================================================================ // INLINE DEFINITIONS // ============================================================================ @@ -1747,10 +2533,14 @@ JsonArray::JsonArray(INPUT_ITERATOR first, #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) inline -JsonArray::JsonArray(std::initializer_list elements, - bslma::Allocator *basicAllocator) -: d_elements(elements, basicAllocator) +JsonArray::JsonArray(std::initializer_list elements, + bslma::Allocator *basicAllocator) +: d_elements(basicAllocator) { + d_elements.reserve(elements.size()); + for (const auto& e : elements) { + d_elements.emplace_back(e); + } } #endif @@ -1772,9 +2562,10 @@ JsonArray& JsonArray::operator=(bslmf::MovableRef rhs) #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) inline -JsonArray& JsonArray::operator=(std::initializer_list initializer) +JsonArray& JsonArray::operator=(std::initializer_list + initializer) { - d_elements = initializer; + assign(initializer); return *this; } #endif @@ -1788,16 +2579,24 @@ Json& JsonArray::operator[](bsl::size_t index) template inline -void JsonArray::assign(INPUT_ITERATOR first, INPUT_ITERATOR last) +JsonArray& JsonArray::assign(INPUT_ITERATOR first, INPUT_ITERATOR last) { d_elements.assign(first, last); + return *this; } #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) inline -void JsonArray::assign(std::initializer_list initializer) +JsonArray& JsonArray::assign(std::initializer_list + initializer) { - d_elements.assign(initializer); + Elements newElements(d_elements.get_allocator()); + newElements.reserve(initializer.size()); + for (const auto& e : initializer) { + newElements.emplace_back(e); + } + bslalg::SwapUtil::swap(&d_elements, &newElements); + return *this; } #endif @@ -1901,54 +2700,205 @@ void JsonArray::popBack() } inline -void JsonArray::pushBack(const Json& json) +JsonArray& JsonArray::pushBack(const Json& json) { d_elements.push_back(json); + return *this; } inline -void JsonArray::pushBack(bslmf::MovableRef json) +JsonArray& JsonArray::pushBack(bslmf::MovableRef json) { d_elements.push_back(bslmf::MovableRefUtil::move(json)); + return *this; } inline -void JsonArray::resize(bsl::size_t count) +JsonArray& JsonArray::pushBack(const JsonArray& array) { - d_elements.resize(count); + d_elements.emplace_back(array); + return *this; } inline -void JsonArray::resize(bsl::size_t count, const Json& json) +JsonArray& JsonArray::pushBack(const JsonObject& object) { - d_elements.resize(count, json); + d_elements.emplace_back(object); + return *this; } inline -void JsonArray::swap(JsonArray& other) +JsonArray& JsonArray::pushBack(const JsonNumber& number) { - BSLS_ASSERT(allocator() == other.allocator()); - d_elements.swap(other.d_elements); + d_elements.emplace_back(number); + return *this; } -// ACCESSORS inline -const Json& JsonArray::operator[](bsl::size_t index) const +JsonArray& JsonArray::pushBack(bslmf::MovableRef array) { - BSLS_ASSERT(index < d_elements.size()); - return d_elements[index]; + d_elements.emplace_back(bslmf::MovableRefUtil::move(array)); + return *this; } inline -bslma::Allocator *JsonArray::allocator() const BSLS_KEYWORD_NOEXCEPT +JsonArray& JsonArray::pushBack(bslmf::MovableRef object) { - return d_elements.get_allocator().mechanism(); + d_elements.emplace_back(bslmf::MovableRefUtil::move(object)); + return *this; } inline -const Json& JsonArray::back() const +JsonArray& JsonArray::pushBack(bslmf::MovableRef number) { - return d_elements.back(); + d_elements.emplace_back(bslmf::MovableRefUtil::move(number)); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(const JsonNull& value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(bool value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(int value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(unsigned int value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(long value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(unsigned long value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(long long value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(unsigned long long value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(float value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(double value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(bdldfp::Decimal64 value) +{ + d_elements.emplace_back(value); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(const char *string) +{ + BSLS_ASSERT(string); + + d_elements.emplace_back(string); + return *this; +} + +inline +JsonArray& JsonArray::pushBack(const bsl::string_view& string) +{ + d_elements.emplace_back(string); + return *this; +} + +template +inline +typename bsl::enable_if::value, + JsonArray&>::type +JsonArray::pushBack(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string) +{ + BSLS_ASSERT(bdlde::Utf8Util::isValid( + bslmf::MovableRefUtil::access(string))); + + d_elements.emplace_back(bslmf::MovableRefUtil::move(string)); + return *this; +} + +inline +void JsonArray::resize(bsl::size_t count) +{ + d_elements.resize(count); +} + +inline +void JsonArray::resize(bsl::size_t count, const Json& json) +{ + d_elements.resize(count, json); +} + +inline +void JsonArray::swap(JsonArray& other) +{ + BSLS_ASSERT(allocator() == other.allocator()); + d_elements.swap(other.d_elements); +} + +// ACCESSORS +inline +const Json& JsonArray::operator[](bsl::size_t index) const +{ + BSLS_ASSERT(index < d_elements.size()); + return d_elements[index]; +} + +inline +bslma::Allocator *JsonArray::allocator() const BSLS_KEYWORD_NOEXCEPT +{ + return d_elements.get_allocator().mechanism(); +} + +inline +const Json& JsonArray::back() const +{ + return d_elements.back(); } inline @@ -2065,19 +3015,30 @@ JsonObject::JsonObject(INPUT_ITERATOR first, #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) inline -JsonObject::JsonObject(std::initializer_list members, - bslma::Allocator *basicAllocator) -: d_members(members, basicAllocator) +JsonObject::JsonObject( + std::initializer_list members, + bslma::Allocator *basicAllocator) +: d_members(basicAllocator) { -#ifdef BSLS_ASSERT_IS_ACTIVE - for (Container::const_iterator iter = d_members.begin(); - iter != d_members.end(); - ++iter) { - BSLS_ASSERT(bdlde::Utf8Util::isValid(iter->first.data(), - iter->first.size())); + d_members.reserve(members.size()); + for (const auto& mem : members) { + BSLS_ASSERT(bdlde::Utf8Util::isValid(mem.key().data(), + mem.key().size())); + insert(mem.key(), mem.value()); } -#endif } + +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) +inline +JsonObject::JsonObject(const std::string_view& key, + const Json_Initializer& init, + bslma::Allocator *basicAllocator) +: d_members(basicAllocator) +{ + BSLS_ASSERT(bdlde::Utf8Util::isValid(key.data(), key.size())); + insert(key, init); +} +#endif #endif // MANIPULATORS @@ -2103,9 +3064,22 @@ JsonObject& JsonObject::operator=(bslmf::MovableRef rhs) #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) inline -JsonObject& JsonObject::operator=(std::initializer_list members) -{ - d_members = members; +JsonObject& JsonObject::operator=( + std::initializer_list members) +{ + Container new_container(d_members.get_allocator()); + new_container.reserve(members.size()); + for (const auto& mem : members) { + BSLS_ASSERT(bdlde::Utf8Util::isValid(mem.key().data(), + mem.key().size())); + if (!new_container.contains(mem.key())) { + Json json(mem.value(), allocator()); + Member member(mem.key(), Json(), allocator()); + member.second = bslmf::MovableRefUtil::move(json); + new_container.insert(bslmf::MovableRefUtil::move(member)); + } + } + bslalg::SwapUtil::swap(&d_members, &new_container); return *this; } #endif @@ -2201,7 +3175,7 @@ template inline typename bsl::enable_if< !bsl::is_convertible::value, - void>::type + JsonObject&>::type JsonObject::insert(INPUT_ITERATOR first, INPUT_ITERATOR last) { d_members.insert(first, last); @@ -2213,19 +3187,20 @@ JsonObject::insert(INPUT_ITERATOR first, INPUT_ITERATOR last) bdlde::Utf8Util::isValid(iter->first.data(), iter->first.size())); } #endif + return *this; } #if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS) inline -void JsonObject::insert(std::initializer_list members) +JsonObject& JsonObject::insert(std::initializer_list + members) { -#ifdef BSLS_ASSERT_IS_ACTIVE - for (auto member : members) { - BSLS_ASSERT(bdlde::Utf8Util::isValid(member.first.data(), - member.first.size())); + for (const auto& mem : members) { + BSLS_ASSERT(bdlde::Utf8Util::isValid(mem.key().data(), + mem.key().size())); + insert(mem.key(), mem.value()); } -#endif - d_members.insert(members); + return *this; } #endif @@ -2348,6 +3323,18 @@ Json::Json(bslmf::MovableRef original, bslma::Allocator *basicAllocator) { } +inline +Json::Json(bool boolean, bslma::Allocator *basicAllocator) +: d_value(boolean, basicAllocator) +{ +} + +inline +Json::Json(const JsonNull& null, bslma::Allocator *basicAllocator) +: d_value(null, basicAllocator) +{ +} + inline Json::Json(const JsonArray& array, bslma::Allocator *basicAllocator) : d_value(array, basicAllocator) @@ -2355,89 +3342,89 @@ Json::Json(const JsonArray& array, bslma::Allocator *basicAllocator) } inline -Json::Json(bslmf::MovableRef array, - bslma::Allocator *basicAllocator) -: d_value(bslmf::MovableRefUtil::move(array), basicAllocator) +Json::Json(const JsonObject& object, bslma::Allocator *basicAllocator) +: d_value(object, basicAllocator) { } inline -Json::Json(bool boolean, bslma::Allocator *basicAllocator) -: d_value(boolean, basicAllocator) +Json::Json(const JsonNumber& number, bslma::Allocator *basicAllocator) +: d_value(number, basicAllocator) { } inline -Json::Json(const JsonNull& null, bslma::Allocator *basicAllocator) -: d_value(null, basicAllocator) +Json::Json(bslmf::MovableRef array, + bslma::Allocator *basicAllocator) +: d_value(bslmf::MovableRefUtil::move(array), basicAllocator) { } inline -Json::Json(float number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(bslmf::MovableRef object, + bslma::Allocator *basicAllocator) +: d_value(bslmf::MovableRefUtil::move(object), basicAllocator) { } inline -Json::Json(double number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(bslmf::MovableRef number, + bslma::Allocator *basicAllocator) +: d_value(bslmf::MovableRefUtil::move(number), basicAllocator) { } inline -Json::Json(bdldfp::Decimal64 number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(int value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(int number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(unsigned int value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(unsigned int number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(long value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(bsls::Types::Int64 number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(unsigned long value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(bsls::Types::Uint64 number, bslma::Allocator *basicAllocator) -: d_value(JsonNumber(number), basicAllocator) +Json::Json(bsls::Types::Int64 value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(const JsonNumber& number, bslma::Allocator *basicAllocator) -: d_value(number, basicAllocator) +Json::Json(bsls::Types::Uint64 value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(bslmf::MovableRef number, - bslma::Allocator *basicAllocator) -: d_value(bslmf::MovableRefUtil::move(number), basicAllocator) +Json::Json(float value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(const JsonObject& object, bslma::Allocator *basicAllocator) -: d_value(object, basicAllocator) +Json::Json(double value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } inline -Json::Json(bslmf::MovableRef object, - bslma::Allocator *basicAllocator) -: d_value(bslmf::MovableRefUtil::move(object), basicAllocator) +Json::Json(bdldfp::Decimal64 value, bslma::Allocator *basicAllocator) +: d_value(JsonNumber(value), basicAllocator) { } @@ -2449,6 +3436,7 @@ Json::Json(const char *string, bslma::Allocator *basicAllocator) d_value.createInPlace(string); } +#ifndef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS inline Json::Json(const bsl::string_view& string, bslma::Allocator *basicAllocator) : d_value(basicAllocator) @@ -2456,6 +3444,19 @@ Json::Json(const bsl::string_view& string, bslma::Allocator *basicAllocator) BSLS_ASSERT(bdlde::Utf8Util::isValid(string)); d_value.createInPlace(string); } +#endif + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS +// Construct from 'Json_Initializer' is in the 'cpp' file. + +inline +Json::Json(std::initializer_list initializer, + bslma::Allocator *basicAllocator) +: d_value(basicAllocator) +{ + makeArray(JsonArray(initializer, basicAllocator)); +} +#endif template inline @@ -2521,14 +3522,28 @@ Json& Json::operator=(unsigned int rhs) } inline -Json& Json::operator=(bsls::Types::Int64 rhs) +Json& Json::operator=(long rhs) +{ + d_value.createInPlace(rhs); + return *this; +} + +inline +Json& Json::operator=(unsigned long rhs) { d_value.createInPlace(rhs); return *this; } inline -Json& Json::operator=(bsls::Types::Uint64 rhs) +Json& Json::operator=(long long rhs) +{ + d_value.createInPlace(rhs); + return *this; +} + +inline +Json& Json::operator=(unsigned long long rhs) { d_value.createInPlace(rhs); return *this; @@ -2607,7 +3622,7 @@ Json& Json::operator=(bslmf::MovableRef rhs) } inline -Json& Json::operator=(const JsonNull&) +Json& Json::operator=(const JsonNull& ) { makeNull(); return *this; @@ -2723,91 +3738,420 @@ typename bsl::enable_if::value>::type d_value = bslmf::MovableRefUtil::move(string); } + // 'pushBack' methods + inline -void Json::swap(Json& other) +Json& Json::pushBack(const Json& json) { - BSLS_ASSERT(allocator() == other.allocator()); - d_value.swap(other.d_value); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(json); + return *this; } -// ACCESSORS inline -bool Json::isArray() const +Json& Json::pushBack(const JsonArray& array) { - return type() == JsonType::e_ARRAY; + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(array); + return *this; } inline -bool Json::isBoolean() const +Json& Json::pushBack(const JsonObject& object) { - return type() == JsonType::e_BOOLEAN; + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(object); + return *this; } inline -bool Json::isNull() const +Json& Json::pushBack(const JsonNumber& number) { - return type() == JsonType::e_NULL; + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(number); + return *this; } inline -bool Json::isNumber() const +Json& Json::pushBack(bslmf::MovableRef json) { - return type() == JsonType::e_NUMBER; + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(bslmf::MovableRefUtil::move(json)); + return *this; } inline -bool Json::isObject() const +Json& Json::pushBack(bslmf::MovableRef array) { - return type() == JsonType::e_OBJECT; + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(bslmf::MovableRefUtil::move(array)); + return *this; } inline -bool Json::isString() const +Json& Json::pushBack(bslmf::MovableRef object) { - return type() == JsonType::e_STRING; + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(bslmf::MovableRefUtil::move(object)); + return *this; } inline -JsonArray& Json::theArray() +Json& Json::pushBack(bslmf::MovableRef number) { - return d_value.the(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(bslmf::MovableRefUtil::move(number)); + return *this; } inline -bool& Json::theBoolean() +Json& Json::pushBack(const JsonNull& value) { - return d_value.the(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; } inline -JsonNull& Json::theNull() +Json& Json::pushBack(bool value) { - return d_value.the(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; } inline -JsonNumber& Json::theNumber() +Json& Json::pushBack(int value) { - return d_value.the(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; } inline -JsonObject& Json::theObject() +Json& Json::pushBack(unsigned int value) { - return d_value.the(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; } -#if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT) inline -Json::operator JsonArray &() +Json& Json::pushBack(long value) { - return theArray(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; } inline -Json::operator bool &() +Json& Json::pushBack(unsigned long value) { - return theBoolean(); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; +} + +inline +Json& Json::pushBack(long long value) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; +} + +inline +Json& Json::pushBack(unsigned long long value) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; +} + +inline +Json& Json::pushBack(float value) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; +} + +inline +Json& Json::pushBack(double value) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; +} + +inline +Json& Json::pushBack(bdldfp::Decimal64 value) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(value); + return *this; +} + +inline +Json& Json::pushBack(const char *string) +{ + BSLS_ASSERT(string); + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(string); + return *this; +} + +inline +Json& Json::pushBack(const bsl::string_view& string) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(string); + return *this; +} + +template +inline +typename bsl::enable_if::value, + Json&>::type +Json::pushBack(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string) +{ + BSLS_ASSERT(isArray() || isNull()); + if (isNull()) { + makeArray(); + } + theArray().pushBack(bslmf::MovableRefUtil::move(string)); + return *this; +} + +template +RETURN_TYPE Json::visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor) +{ + typedef Json_VisitUtil Util; + + switch (type()) { + case JsonType::e_OBJECT : { + return Util::invokeVisitor(visitor, &theObject()); + // RETURN + } break; + case JsonType::e_ARRAY : { + return Util::invokeVisitor(visitor, &theArray()); + // RETURN + } break; + case JsonType::e_STRING : { +#ifdef BSLS_ASSERT_IS_ACTIVE + Json_StringInvariantGuard guard(&d_value.the()); +#endif + return Util::invokeVisitor(visitor, + &d_value.the()); + // RETURN + } break; + case JsonType::e_NUMBER : { + return Util::invokeVisitor(visitor, &theNumber()); + // RETURN + } break; + case JsonType::e_BOOLEAN: { + return Util::invokeVisitor(visitor, &theBoolean()); + // RETURN + } break; + case JsonType::e_NULL : { + return Util::invokeVisitor(visitor, &theNull()); + // RETURN + } break; + default: { + BSLS_ASSERT_OPT(false && "reachable"); + } break; + } + BSLA_UNREACHABLE; +} + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +template +decltype(auto) Json::visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor) +{ + using Util = Json_VisitUtil; + + switch (type()) { + case JsonType::e_OBJECT : { + return Util::invokeVisitor(visitor, &theObject()); // RETURN + } break; + case JsonType::e_ARRAY : { + return Util::invokeVisitor(visitor, &theArray()); // RETURN + } break; + case JsonType::e_STRING : { +#ifdef BSLS_ASSERT_IS_ACTIVE + Json_StringInvariantGuard guard(&d_value.the()); +#endif + return Util::invokeVisitor(visitor, &d_value.the()); + // RETURN + } break; + case JsonType::e_NUMBER : { + return Util::invokeVisitor(visitor, &theNumber()); // RETURN + } break; + case JsonType::e_BOOLEAN: { + return Util::invokeVisitor(visitor, &theBoolean()); // RETURN + } break; + case JsonType::e_NULL : { + return Util::invokeVisitor(visitor, &theNull()); // RETURN + } break; + default: { + BSLS_ASSERT_OPT(false && "reachable"); + } break; + } + BSLA_UNREACHABLE; +} +#endif + + // Aspects + +inline +void Json::swap(Json& other) +{ + BSLS_ASSERT(allocator() == other.allocator()); + d_value.swap(other.d_value); +} + +// ACCESSORS +inline +bool Json::isArray() const +{ + return type() == JsonType::e_ARRAY; +} + +inline +bool Json::isBoolean() const +{ + return type() == JsonType::e_BOOLEAN; +} + +inline +bool Json::isNull() const +{ + return type() == JsonType::e_NULL; +} + +inline +bool Json::isNumber() const +{ + return type() == JsonType::e_NUMBER; +} + +inline +bool Json::isObject() const +{ + return type() == JsonType::e_OBJECT; +} + +inline +bool Json::isString() const +{ + return type() == JsonType::e_STRING; +} + +inline +JsonArray& Json::theArray() +{ + return d_value.the(); +} + +inline +bool& Json::theBoolean() +{ + return d_value.the(); +} + +inline +JsonNull& Json::theNull() +{ + return d_value.the(); +} + +inline +JsonNumber& Json::theNumber() +{ + return d_value.the(); +} + +inline +JsonObject& Json::theObject() +{ + return d_value.the(); +} + +#if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT) +inline +Json::operator JsonArray &() +{ + return theArray(); +} + +inline +Json::operator bool &() +{ + return theBoolean(); } inline @@ -2832,7 +4176,10 @@ Json::operator JsonObject &() inline Json& Json::operator[](const bsl::string_view& key) { - BSLS_ASSERT(d_value.is()); + BSLS_ASSERT(isObject() || isNull()); + if (isNull()) { + makeObject(); + } return theObject()[key]; } @@ -2843,6 +4190,7 @@ Json& Json::operator[](bsl::size_t index) return theArray()[index]; } +// ACCESSORS inline bsl::size_t Json::size() const { @@ -2850,12 +4198,7 @@ bsl::size_t Json::size() const return isArray() ? theArray().size() : theObject().size(); } -// ACCESSORS -inline -bslma::Allocator *Json::allocator() const BSLS_KEYWORD_NOEXCEPT -{ - return d_value.getAllocator(); -} +// BDE_VERIFY pragma: -FABC01 // not in order inline bdldfp::Decimal64 Json::asDecimal64() const @@ -2885,6 +4228,13 @@ float Json::asFloat() const return theNumber().asFloat(); } +inline +int Json::asShort(short *result) const +{ + BSLS_ASSERT(isNumber()); + return theNumber().asShort(result); +} + inline int Json::asInt(int *result) const { @@ -2892,6 +4242,20 @@ int Json::asInt(int *result) const return theNumber().asInt(result); } +inline +int Json::asLong(long *result) const +{ + BSLS_ASSERT(isNumber()); + return theNumber().asLong(result); +} + +inline +int Json::asLonglong(long long *result) const +{ + BSLS_ASSERT(isNumber()); + return theNumber().asLonglong(result); +} + inline int Json::asInt64(bsls::Types::Int64 *result) const { @@ -2899,6 +4263,13 @@ int Json::asInt64(bsls::Types::Int64 *result) const return theNumber().asInt64(result); } +inline +int Json::asUshort(unsigned short *result) const +{ + BSLS_ASSERT(isNumber()); + return theNumber().asUshort(result); +} + inline int Json::asUint(unsigned int *result) const { @@ -2906,6 +4277,20 @@ int Json::asUint(unsigned int *result) const return theNumber().asUint(result); } +inline +int Json::asUlong(unsigned long *result) const +{ + BSLS_ASSERT(isNumber()); + return theNumber().asUlong(result); +} + +inline +int Json::asUlonglong(unsigned long long *result) const +{ + BSLS_ASSERT(isNumber()); + return theNumber().asUlonglong(result); +} + inline int Json::asUint64(bsls::Types::Uint64 *result) const { @@ -2913,6 +4298,7 @@ int Json::asUint64(bsls::Types::Uint64 *result) const return theNumber().asUint64(result); } +// BDE_VERIFY pragma: +FABC01 // not in order inline const JsonArray& Json::theArray() const @@ -3007,6 +4393,84 @@ Json::operator const bsl::string &() const } #endif +template +RETURN_TYPE Json::visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor) + const +{ + typedef Json_VisitUtil Util; + + switch (type()) { + case JsonType::e_OBJECT : { + return Util::invokeVisitor(visitor, theObject()); + // RETURN + } break; + case JsonType::e_ARRAY : { + return Util::invokeVisitor(visitor, theArray()); + // RETURN + } break; + case JsonType::e_STRING : { + return Util::invokeVisitor(visitor, theString()); + // RETURN + } break; + case JsonType::e_NUMBER : { + return Util::invokeVisitor(visitor, theNumber()); + // RETURN + } break; + case JsonType::e_BOOLEAN: { + return Util::invokeVisitor(visitor, theBoolean()); + // RETURN + } break; + case JsonType::e_NULL : { + return Util::invokeVisitor(visitor, theNull()); // RETURN + } break; + default: { + BSLS_ASSERT_OPT(false && "reachable"); + } break; + } + BSLA_UNREACHABLE; +} + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +template +decltype(auto) Json::visit(BSLS_COMPILERFEATURES_FORWARD_REF(VISITOR) visitor) + const +{ + using Util = Json_VisitUtil; + + switch (type()) { + case JsonType::e_OBJECT : { + return Util::invokeVisitor(visitor, theObject()); // RETURN + } break; + case JsonType::e_ARRAY : { + return Util::invokeVisitor(visitor, theArray()); // RETURN + } break; + case JsonType::e_STRING : { + return Util::invokeVisitor(visitor, theString()); // RETURN + } break; + case JsonType::e_NUMBER : { + return Util::invokeVisitor(visitor, theNumber()); // RETURN + } break; + case JsonType::e_BOOLEAN: { + return Util::invokeVisitor(visitor, theBoolean()); // RETURN + } break; + case JsonType::e_NULL : { + return Util::invokeVisitor(visitor, theNull()); // RETURN + } break; + default: { + BSLS_ASSERT_OPT(false && "reachable"); + } break; + } + BSLA_UNREACHABLE; +} +#endif + // Aspects + +inline +bslma::Allocator *Json::allocator() const BSLS_KEYWORD_NOEXCEPT +{ + return d_value.getAllocator(); +} + } // close package namespace // FREE OPERATORS @@ -3107,19 +4571,704 @@ bool bdljsn::operator!=(const bdljsn::Json& lhs, const bdljsn::Json& rhs) return lhs.d_value != rhs.d_value; } -template inline -void bdljsn::hashAppend(HASHALG& hashAlg, const bdljsn::Json& object) +bool bdljsn::operator==(const JsonArray& lhs, const Json& rhs) { - hashAppend(hashAlg, object.d_value); + return rhs.isArray() && rhs.theArray() == lhs; } inline -void bdljsn::swap(bdljsn::Json& a, bdljsn::Json& b) +bool bdljsn::operator==(const JsonObject& lhs, const Json& rhs) { - bslalg::SwapUtil::swap(&a.d_value, &b.d_value); + return rhs.isObject() && rhs.theObject() == lhs; +} + +inline +bool bdljsn::operator==(const JsonNumber& lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == lhs; +} + +inline +bool bdljsn::operator==(const JsonNull& , const Json& rhs) +{ + return rhs.isNull(); +} + +inline +bool bdljsn::operator==(bool lhs, const Json& rhs) +{ + return rhs.isBoolean() && rhs.theBoolean() == lhs; +} + +inline +bool bdljsn::operator==(int lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(unsigned int lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(long lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(unsigned long lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(long long lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(unsigned long long lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(float lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(double lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); } +inline +bool bdljsn::operator==(bdldfp::Decimal64 lhs, const Json& rhs) +{ + return rhs.isNumber() && rhs.theNumber() == JsonNumber(lhs); +} + +inline +bool bdljsn::operator==(const char *lhs, const Json& rhs) +{ + BSLS_ASSERT(lhs); + return rhs.isString() && rhs.theString() == lhs; +} + +inline +bool bdljsn::operator==(const bsl::string_view& lhs, const Json& rhs) +{ + return rhs.isString() && rhs.theString() == lhs; +} + +inline +bool bdljsn::operator==(const Json& lhs, const JsonArray& rhs) +{ + return lhs.isArray() && lhs.theArray() == rhs; +} + +inline +bool bdljsn::operator==(const Json& lhs, const JsonObject& rhs) +{ + return lhs.isObject() && lhs.theObject() == rhs; +} + +inline +bool bdljsn::operator==(const Json& lhs, const JsonNumber& rhs) +{ + return lhs.isNumber() && lhs.theNumber() == rhs; +} + +inline +bool bdljsn::operator==(const Json& lhs, const JsonNull& ) +{ + return lhs.isNull(); +} + +inline +bool bdljsn::operator==(const Json& lhs, bool rhs) +{ + return lhs.isBoolean() && lhs.theBoolean() == rhs; +} + +inline +bool bdljsn::operator==(const Json& lhs, int rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, unsigned int rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, long rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, unsigned long rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, long long rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, unsigned long long rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, float rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, double rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, bdldfp::Decimal64 rhs) +{ + return lhs.isNumber() && lhs.theNumber() == JsonNumber(rhs); +} + +inline +bool bdljsn::operator==(const Json& lhs, const char *rhs) +{ + BSLS_ASSERT(rhs); + return lhs.isString() && lhs.theString() == rhs; +} + +inline +bool bdljsn::operator==(const Json& lhs, const bsl::string_view& rhs) +{ + return lhs.isString() && lhs.theString() == rhs; +} + +inline +bool bdljsn::operator!=(const JsonArray& lhs, const Json& rhs) +{ + return !rhs.isArray() || rhs.theArray() != lhs; +} + +inline +bool bdljsn::operator!=(const JsonObject& lhs, const Json& rhs) +{ + return !rhs.isObject() || rhs.theObject() != lhs; +} + +inline +bool bdljsn::operator!=(const JsonNumber& lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != lhs; +} + +inline +bool bdljsn::operator!=(const JsonNull& , const Json& rhs) +{ + return !rhs.isNull(); +} + +inline +bool bdljsn::operator!=(bool lhs, const Json& rhs) +{ + return !rhs.isBoolean() || rhs.theBoolean() != lhs; +} + +inline +bool bdljsn::operator!=(int lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(unsigned int lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(long lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(unsigned long lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(long long lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(unsigned long long lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(float lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(double lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(bdldfp::Decimal64 lhs, const Json& rhs) +{ + return !rhs.isNumber() || rhs.theNumber() != JsonNumber(lhs); +} + +inline +bool bdljsn::operator!=(const char *lhs, const Json& rhs) +{ + BSLS_ASSERT(lhs); + return !rhs.isString() || rhs.theString() != lhs; +} + +inline +bool bdljsn::operator!=(const bsl::string_view& lhs, const Json& rhs) +{ + return !rhs.isString() || rhs.theString() != lhs; +} + +inline +bool bdljsn::operator!=(const Json& lhs, const JsonArray& rhs) +{ + return !lhs.isArray() || lhs.theArray() != rhs; +} + +inline +bool bdljsn::operator!=(const Json& lhs, const JsonObject& rhs) +{ + return !lhs.isObject() || lhs.theObject() != rhs; +} + +inline +bool bdljsn::operator!=(const Json& lhs, const JsonNumber& rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != rhs; +} + +inline +bool bdljsn::operator!=(const Json& lhs, const JsonNull& ) +{ + return !lhs.isNull(); +} + +inline +bool bdljsn::operator!=(const Json& lhs, bool rhs) +{ + return !lhs.isBoolean() || lhs.theBoolean() != rhs; +} + +inline +bool bdljsn::operator!=(const Json& lhs, int rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, unsigned int rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, long rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, unsigned long rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, long long rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, unsigned long long rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, float rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, double rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, bdldfp::Decimal64 rhs) +{ + return !lhs.isNumber() || lhs.theNumber() != JsonNumber(rhs); +} + +inline +bool bdljsn::operator!=(const Json& lhs, const char *rhs) +{ + BSLS_ASSERT(rhs); + return !lhs.isString() || lhs.theString() != rhs; +} + +inline +bool bdljsn::operator!=(const Json& lhs, const bsl::string_view& rhs) +{ + return !lhs.isString() || lhs.theString() != rhs; +} + +template +inline +void bdljsn::hashAppend(HASHALG& hashAlg, const bdljsn::Json& object) +{ + hashAppend(hashAlg, object.d_value); +} + +inline +void bdljsn::swap(bdljsn::Json& a, bdljsn::Json& b) +{ + bslalg::SwapUtil::swap(&a.d_value, &b.d_value); +} + +namespace bdljsn { + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + + // ---------------------- + // class Json_Initializer + // ---------------------- + +// CREATORS +inline +Json_Initializer::Json_Initializer() +: d_storage(JsonNull()) +{ +} + +inline +Json_Initializer::Json_Initializer(const JsonNull &) +: d_storage(JsonNull()) +{ +} + +inline +Json_Initializer::Json_Initializer(bool b) +: d_storage(b) +{ +} + +template +inline +Json_Initializer::Json_Initializer(NUMBER num) { + + BSLMF_ASSERT(bsl::is_unsigned::value || + bsl::is_signed::value); + + if (bsl::is_unsigned::value) { + d_storage = static_cast(num); + } + else { + d_storage = static_cast(num); + } +} + +inline +Json_Initializer::Json_Initializer(float f) +: d_storage(static_cast(f)) +{ +} + +inline +Json_Initializer::Json_Initializer(double d) +: d_storage(d) +{ +} + +inline +Json_Initializer::Json_Initializer(bdldfp::Decimal64 value) +: d_storage(value) +{ +} + +inline +Json_Initializer::Json_Initializer(const char *p) +: d_storage(bsl::string_view(p)) +{ +} + +inline +Json_Initializer::Json_Initializer(const bsl::string &s) +: d_storage(bsl::string_view(s)) +{ +} + +inline +Json_Initializer::Json_Initializer(const std::string &s) +: d_storage(bsl::string_view(s.data(), s.size())) +{ +} + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING +inline +Json_Initializer::Json_Initializer(const std::pmr::string &s) +: d_storage(bsl::string_view(s.data(), s.size())) +{ +} +#endif + +inline +Json_Initializer::Json_Initializer(const bsl::string_view& sv) +: d_storage(sv) +{ +} + +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) \ +&& !defined(BSLS_LIBRARYFEATURES_HAS_CPP20_BASELINE_LIBRARY) +inline +Json_Initializer::Json_Initializer(const std::string_view& sv) +: d_storage(bsl::string_view(sv.data(), sv.size())) +{ +} +#endif + +inline +Json_Initializer::Json_Initializer( + std::initializer_list il) +: d_storage(il) +{ +} + +inline +Json_Initializer::Json_Initializer(const JsonObject& jObj) +: d_storage(&jObj) +{ +} + +inline +Json_Initializer::Json_Initializer(const JsonArray& jArr) +: d_storage(&jArr) +{ +} + +inline +Json_Initializer::Json_Initializer(const JsonNumber& jNum) +: d_storage(&jNum) +{ +} + +inline +Json_Initializer::Json_Initializer(const Json& json) +: d_storage(&json) +{ +} + +// ACCESSORS +inline +const Json_Initializer::Storage& +Json_Initializer::get_storage() const +{ + return d_storage; +} + + // ---------------------------- + // class Json_MemberInitializer + // ---------------------------- + +// CREATORS +inline +Json_MemberInitializer::Json_MemberInitializer(const char *s, + const Json_Initializer& ji) +: d_first (s) +, d_second(ji) +{ +} + +inline +Json_MemberInitializer::Json_MemberInitializer(const bsl::string& s, + const Json_Initializer& ji) +: d_first (s.data(), s.size()) +, d_second(ji) +{ +} + +inline +Json_MemberInitializer::Json_MemberInitializer(const std::string& s, + const Json_Initializer& ji) +: d_first (s.data(), s.size()) +, d_second(ji) +{ +} + +inline +Json_MemberInitializer::Json_MemberInitializer(const bsl::string_view& sv, + const Json_Initializer& ji) +: d_first (sv.data(), sv.size()) +, d_second(ji) +{ +} + +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) \ +&& !defined(BSLS_LIBRARYFEATURES_HAS_CPP20_BASELINE_LIBRARY) +inline +Json_MemberInitializer::Json_MemberInitializer(const std::string_view& sv, + const Json_Initializer& ji) +: d_first (sv.data(), sv.size()) +, d_second(ji) +{ +} +#endif + +// ACCESSORS +inline +bsl::string_view Json_MemberInitializer::key() const +{ + return d_first; +} + +inline +const Json_Initializer& Json_MemberInitializer::value() const +{ + return d_second; +} + +#endif + + // --------------------- + // struct Json_VisitUtil + // --------------------- + +template +RETURN_TYPE Json_VisitUtil::invokeVisitor(VISITOR *visitor, + const ARGUMENT& argument) +{ + BSLS_ASSERT(visitor); + return (*visitor)(argument); +} + +template +RETURN_TYPE Json_VisitUtil::invokeVisitor(VISITOR& visitor, + const ARGUMENT& argument) +{ + return visitor(argument); +} + +template +RETURN_TYPE Json_VisitUtil::invokeVisitor(const VISITOR& visitor, + const ARGUMENT& argument) +{ + return visitor(argument); +} + +template +RETURN_TYPE Json_VisitUtil::invokeVisitor( + BSLMF_MOVABLEREF_DEDUCE(VISITOR) visitor, + const ARGUMENT& argument) +{ + #ifdef BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES + return (bsl::forward(visitor))(argument); + #else + return (bslmf::MovableRefUtil::access(visitor))(argument); + #endif +} + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +template +decltype(auto) Json_VisitUtil::invokeVisitor(const VISITOR& visitor, + const ARGUMENT& argument) +{ + return visitor(argument); +} + +template +decltype(auto) Json_VisitUtil::invokeVisitor( + BSLMF_MOVABLEREF_DEDUCE(VISITOR) visitor, + const ARGUMENT& argument) +{ + #ifdef BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES + return (bsl::forward(visitor))(argument); + #else + return (bslmf::MovableRefUtil::access(visitor))(argument); + #endif +} +#endif + + // ------------------------------- + // class Json_StringInvariantGuard + // ------------------------------- + +// CREATORS +inline +Json_StringInvariantGuard::Json_StringInvariantGuard(bsl::string *string) +: d_string_p(string) +{ + BSLS_ASSERT(string); +} + +inline +Json_StringInvariantGuard::~Json_StringInvariantGuard() + BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(false) +{ + if (0 == bsl::uncaught_exceptions()) { + if (false == bdlde::Utf8Util::isValid(*d_string_p)) { + BSLS_ASSERT_INVOKE_NORETURN( + "false == bdlde::Utf8Util::isValid(*d_string_p)"); + } + } +} + +} // close package namespace } // close enterprise namespace #endif // INCLUDED_BDLJSN_JSON diff --git a/groups/bdl/bdljsn/bdljsn_json.t.cpp b/groups/bdl/bdljsn/bdljsn_json.t.cpp index 7e71247a43..62534d9904 100644 --- a/groups/bdl/bdljsn/bdljsn_json.t.cpp +++ b/groups/bdl/bdljsn/bdljsn_json.t.cpp @@ -14,11 +14,17 @@ #include #include +#include +#include + #include #include #include #include +#include +#include +#include #include #include @@ -50,14 +56,15 @@ using bsl::flush; // TEST PLAN // ---------------------------------------------------------------------------- // Note that this component is comprised of three different classes, no one of -// which can be described without describing the others. Classes bdljsn::Json, -// bdljsn::JsonObject, and bdljsn::JsonArray are essentially specializations of -// bdlb::Variant, bsl::unordered_map, and bsl::vector, respectively. +// which can be described without describing the others. Classes +// 'bdljsn::Json', 'bdljsn::JsonObject', and 'bdljsn::JsonArray' are +// essentially specializations of 'bdlb::Variant', 'bsl::unordered_map', and +// 'bsl::vector', respectively. // // Overview // -------- // -// The first class to be tested is JsonArray. The testing methodology will be +// The first class to be tested is 'JsonArray.' The testing methodology will be // that of a value-semantic type. // // Primary Manipulators: @@ -112,6 +119,27 @@ using bsl::flush; // [13] JsonArray::operator=(initializer_list l); // [ 2] JsonArray::pushBack(const Json& j); // [13] JsonArray::pushBack(MovableRef j); +// [43] JsonArray::pushBack(const JsonArray& array); +// [43] JsonArray::pushBack(const JsonObject& object); +// [43] JsonArray::pushBack(const JsonNumber& number); +// [43] JsonArray::pushBack(bslmf::MovableRef array); +// [43] JsonArray::pushBack(bslmf::MovableRef object); +// [43] JsonArray::pushBack(bslmf::MovableRef number); +// [43] JsonArray::pushBack(const JsonNull& value); +// [43] JsonArray::pushBack(bool value); +// [43] JsonArray::pushBack(int value); +// [43] JsonArray::pushBack(unsigned int value); +// [43] JsonArray::pushBack(long value); +// [43] JsonArray::pushBack(unsigned long value); +// [43] JsonArray::pushBack(long long value); +// [43] JsonArray::pushBack(unsigned long long value); +// [43] JsonArray::pushBack(float value); +// [43] JsonArray::pushBack(double value); +// [43] JsonArray::pushBack(bdldfp::Decimal64 value); +// [43] JsonArray::pushBack(const char *string); +// [43] JsonArray::pushBack(const bsl::string_view& string); +// [43] JsonArray::pushBack(bsl::string&& string); +// [46] JsonArray::pushBack(Json_Initializer init); // [13] Json& JsonArray::operator[](size_t i); // [13] JsonArray::assign(initializer_list l); // [13] JsonArray::assign(INPUT_ITERATOR first, INPUT_ITERATOR last); @@ -168,6 +196,7 @@ using bsl::flush; // [21] JsonObject(MovableRef o, *a); // [16] JsonObject(INPUT_ITER first, INPUT_ITER last, *a); // [26] JsonObject(initializer_list members, *a); +// [26] JsonObject(initializer_list members, *a); // [15] ~JsonObject(); // // MANIPULATORS @@ -180,8 +209,9 @@ using bsl::flush; // [26] void JsonObject::clear(); // [26] pair JsonObject::insert(const Member& m); // [26] pair JsonObject::insert(MovableRef m); -// [26] void JsonObject::insert(INPUT_ITER first, INPUT_ITER last); -// [26] void JsonObject::insert(initializer_list members); +// [26] JsonObject& JsonObject::insert(INPUT_ITER first, INPUT_ITER last); +// [26] JsonObject& JsonObject::insert(initializer_list members); +// [46] JsonObject::insert(string_view, Json_Initializer init); // [26] pair JsonObject::insert(string_view key, V&& v); // [26] bsl::size_t JsonObject::erase(const bsl::string_view& key); // [26] Iterator JsonObject::erase(Iterator position); @@ -220,36 +250,41 @@ using bsl::flush; // [33] Json(const Json &original, *a); // [34] Json(bslmf::MovableRef original); // [34] Json(bslmf::MovableRef original, *a); -// [29] Json(const JsonArray &array, *a); -// [29] Json(bslmf::MovableRef array, *a); -// [29] Json(bool boolean, *a); -// [29] Json(const JsonNull& null, *a); -// [29] Json(float number, *a); -// [29] Json(double number, *a); -// [29] Json(bdldfp::Decimal64 number, *a); -// [29] Json(int number, *a); -// [29] Json(unsigned int number, *a); -// [29] Json(bsls::Types::Int64 number, *a); -// [29] Json(bsls::Types::Uint64 number, *a); -// [29] Json(const JsonNumber& number, *a); -// [29] Json(bslmf::MovableRef number, *a); -// [29] Json(const JsonObject& object, *a); +// [29] Json(bool boolean, *a); +// [29] Json(const JsonNull& null, *a); +// [29] Json(const JsonArray& array, *a); +// [29] Json(const JsonObject& object, *a); +// [29] Json(const JsonNumber& number, *a); +// [29] Json(bslmf::MovableRef array, *a); // [29] Json(bslmf::MovableRef object, *a); -// [29] Json(const char *string, *a); -// [29] Json(const bsl::string_view& string, *a); -// [29] Json(STRING&& string, *a); +// [29] Json(bslmf::MovableRef number, *a); +// [29] Json(int number, *a); +// [29] Json(unsigned int number, *a); +// [29] Json(long number, *a); +// [29] Json(unsigned long number, *a); +// [29] Json(long long number, *a); +// [29] Json(unsigned long long number, *a); +// [29] Json(float number, *a); +// [29] Json(double number, *a); +// [29] Json(bdldfp::Decimal64 number, *a); +// [29] Json(const char *string, *a); +// [29] Json(const bsl::string_view& string, *a); +// [29] Json(STRING&& string, *a); +// [46] Json(const Json_Initializer, bslma::Allocator *); // [28] ~Json(); // // MANIPULATORS // [36] Json& Json::operator=(const Json& rhs); // [37] Json& Json::operator=(bslmf::MovableRef rhs); -// [39] Json& Json::operator=(float rhs); -// [39] Json& Json::operator=(double rhs); -// [39] Json& Json::operator=(bdldfp::Decimal64 rhs); -// [39] Json& Json::operator=(int rhs); -// [39] Json& Json::operator=(unsigned int rhs); -// [39] Json& Json::operator=(bsls::Types::Int64 rhs); -// [39] Json& Json::operator=(bsls::Types::Uint64 rhs); +// [39] Json& Json::operator=(int rhs); +// [39] Json& Json::operator=(unsigned int rhs); +// [39] Json& Json::operator=(long rhs); +// [39] Json& Json::operator=(unsigned long rhs); +// [39] Json& Json::operator=(long long rhs); +// [39] Json& Json::operator=(unsigned long long rhs); +// [39] Json& Json::operator=(float rhs); +// [39] Json& Json::operator=(double rhs); +// [39] Json& Json::operator=(bdldfp::Decimal64 rhs); // [39] Json& Json::operator=(const JsonNumber& rhs); // [39] Json& Json::operator=(bslmf::MovableRef rhs); // [39] Json& Json::operator=(const char *rhs); @@ -290,16 +325,40 @@ using bsl::flush; // [39] Json::operator BloombergLP::bdljsn::JsonNumber &(); // [39] Json::operator BloombergLP::bdljsn::JsonObject &(); // [39] Json::operator bsl::string &(); +// [44] Json& Json::pushBack(const Json& json); +// [44] Json& Json::pushBack(const JsonArray& array); +// [44] Json& Json::pushBack(const JsonObject& object); +// [44] Json& Json::pushBack(const JsonNumber& number); +// [44] Json& Json::pushBack(bslmf::MovableRef json); +// [44] Json& Json::pushBack(bslmf::MovableRef array); +// [44] Json& Json::pushBack(bslmf::MovableRef object); +// [44] Json& Json::pushBack(bslmf::MovableRef number); +// [44] Json& Json::pushBack(const JsonNull& value); +// [44] Json& Json::pushBack(bool value); +// [44] Json& Json::pushBack(int value); +// [44] Json& Json::pushBack(unsigned int value); +// [44] Json& Json::pushBack(long value); +// [44] Json& Json::pushBack(unsigned long value); +// [44] Json& Json::pushBack(long long value); +// [44] Json& Json::pushBack(unsigned long long value); +// [44] Json& Json::pushBack(float value); +// [44] Json& Json::pushBack(double value); +// [44] Json& Json::pushBack(bdldfp::Decimal64 value); +// [44] Json& Json::pushBack(const char *string); +// [44] Json& Json::pushBack(const bsl::string_view& string); +// [44] Json& Json::pushBack(bsl::string&& string); +// [47] template RT visit(FORWARD_REF visitor); +// [47] template decltype(auto) visit (FORWARD_REF visitor); // // [35] void Json::swap(Json& other); // // ACCESSORS -// [40] bool Json::isArray() const; +// [40] bool Json::isArray() const; // [40] bool Json::isBoolean() const; -// [40] bool Json::isNull() const; -// [40] bool Json::isNumber() const; -// [40] bool Json::isObject() const; -// [40] bool Json::isString() const; +// [40] bool Json::isNull() const; +// [40] bool Json::isNumber() const; +// [40] bool Json::isObject() const; +// [40] bool Json::isString() const; // [30] const JsonArray& Json::theArray() const; // [30] const bool& Json::theBoolean() const; // [30] const JsonNull& Json::theNull() const; @@ -307,12 +366,18 @@ using bsl::flush; // [30] const JsonObject& Json::theObject() const; // [30] const bsl::string& Json::theString() const; // [30] JsonType::Enum Json::type() const; -// [40] int Json::asInt(int *r) const; -// [40] int Json::asInt64(Int64 *r) const; -// [40] int Json::asUint(unsigned int *r) const; -// [40] int Json::asUint64(Uint64 *r) const; -// [40] float Json::asFloat() const; -// [40] double Json::asDouble() const; +// [40] int Json::asShort (short *r) const; +// [40] int Json::asInt (int *r) const; +// [40] int Json::asLong (long *r) const; +// [40] int Json::asLonglong(long long *r) const; +// [40] int Json::asInt64 (Int64 *r) const; +// [40] int Json::asUshort (unsigned short *r) const; +// [40] int Json::asUint (unsigned int *r) const; +// [40] int Json::asUlong (unsigned long *r) const; +// [40] int Json::asUlonglong(unsigned long long *r) const; +// [40] int Json::asUint64 (Uint64 *r) const; +// [40] float Json::asFloat() const; +// [40] double Json::asDouble() const; // [40] Decimal64 Json::asDecimal64() const; // [40] int Json::asDecimal64Exact(Decimal64 *r) const; // [40] const Json& Json::operator[](const string_view& key) const; @@ -324,6 +389,8 @@ using bsl::flush; // [40] Json::operator const BloombergLP::bdljsn::JsonNumber &() const; // [40] Json::operator const BloombergLP::bdljsn::JsonObject &() const; // [40] Json::operator const bsl::basic_string &() const; +// [47] template RT visit(FORWARD_REF visitor) const; +// [47] template decltype(auto) visit (FORWARD_REF visitor) const; // // [40] bslma::Allocator* Json::allocator() const; // [31] ostream& Json::print(ostream& stream, int l, int spl) const; @@ -332,9 +399,84 @@ using bsl::flush; // [31] ostream& operator<<(ostream&, const Json&); // [32] bool operator==(const Json& lhs, rhs); // [32] bool operator!=(const Json& lhs, rhs); +// [43] operator==(const JsonArray& , const Json& ); +// [43] operator==(const JsonObject& , const Json& ); +// [43] operator==(const JsonNumber& , const Json& ); +// [43] operator==(const JsonNull& , const Json& ); +// [43] operator==(bool , const Json& ); +// [43] operator==(int , const Json& ); +// [43] operator==(unsigned int , const Json& ); +// [43] operator==(bsls::Types::Int64 , const Json& ); +// [43] operator==(bsls::Types::Uint64 , const Json& ); +// [43] operator==(float , const Json& ); +// [43] operator==(double , const Json& ); +// [43] operator==(bdldfp::Decimal64 , const Json& ); +// [43] operator==(const char *, const Json& ); +// [43] operator==(const bsl::string_view& , const Json& ); +// [43] operator==(const Json& , const JsonArray& ); +// [43] operator==(const Json& , const JsonObject& ); +// [43] operator==(const Json& , const JsonNumber& ); +// [43] operator==(const Json& , const JsonNull& ); +// [43] operator==(const Json& , bool ); +// [43] operator==(const Json& , int ); +// [43] operator==(const Json& , unsigned int ); +// [43] operator==(const Json& , bsls::Types::Int64 ); +// [43] operator==(const Json& , bsls::Types::Uint64 ); +// [43] operator==(const Json& , float ); +// [43] operator==(const Json& , double ); +// [43] operator==(const Json& , bdldfp::Decimal64 ); +// [43] operator==(const Json& , const char *); +// [43] operator==(const Json& , const bsl::string_view& ); +// [43] operator!=(const JsonArray& , const Json& ); +// [43] operator!=(const JsonObject& , const Json& ); +// [43] operator!=(const JsonNumber& , const Json& ); +// [43] operator!=(const JsonNull& , const Json& ); +// [43] operator!=(bool , const Json& ); +// [43] operator!=(int , const Json& ); +// [43] operator!=(unsigned int , const Json& ); +// [43] operator!=(bsls::Types::Int64 , const Json& ); +// [43] operator!=(bsls::Types::Uint64 , const Json& ); +// [43] operator!=(float , const Json& ); +// [43] operator!=(double , const Json& ); +// [43] operator!=(bdldfp::Decimal64 , const Json& ); +// [43] operator!=(const char *, const Json& ); +// [43] operator!=(const bsl::string_view& , const Json& ); +// [43] operator!=(const Json& , const JsonArray& ); +// [43] operator!=(const Json& , const JsonObject& ); +// [43] operator!=(const Json& , const JsonNumber& ); +// [43] operator!=(const Json& , const JsonNull& ); +// [43] operator!=(const Json& , bool ); +// [43] operator!=(const Json& , int ); +// [43] operator!=(const Json& , unsigned int ); +// [43] operator!=(const Json& , bsls::Types::Int64 ); +// [43] operator!=(const Json& , bsls::Types::Uint64 ); +// [43] operator!=(const Json& , float ); +// [43] operator!=(const Json& , double ); +// [43] operator!=(const Json& , bdldfp::Decimal64 ); +// [43] operator!=(const Json& , const char *); +// [43] operator!=(const Json& , const bsl::string_view& ); // // FREE FUNCTIONS // [35] void swap(Json& lhs, rhs); +// +// JSON_INITIALIZER CLASS METHODS +// +// [45] Json_Initializer(); +// [45] Json_Initializer(const JsonNull &); +// [45] Json_Initializer(bool b); +// [45] Json_Initializer(NUMBER num); +// [45] Json_Initializer(float f); +// [45] Json_Initializer(double d); +// [45] Json_Initializer(const char *p); +// [45] Json_Initializer(const bsl::string &s); +// [45] Json_Initializer(const std::string &s); +// [45] Json_Initializer(bsl::string_view sv); +// [45] Json_Initializer(std::initializer_list il); +// [45] Json_Initializer(const JsonObject &jObj); +// [45] Json_Initializer(const JsonArray &jArr); +// [45] Json_Initializer(const JsonNumber &jNum); +// [45] Json_Initializer(const Json &json); +// [45] const storage& get_storage() const; // ---------------------------------------------------------------------------- // ============================================================================ @@ -521,7 +663,7 @@ int ggg(Json *object, const char *spec, int verbose) stack.emplace_back(e_OBJECT_EXPECT_MEMBER, state.d_json_p); } break; default: { - ASSERT(0 == "illegal"); + ASSERT(false && "illegal"); } break; } } break; @@ -556,7 +698,7 @@ int ggg(Json *object, const char *spec, int verbose) &stack.back().d_json_p->theArray().back()); } break; default: { - ASSERT(0 == "illegal"); + ASSERT(false && "illegal"); } break; } } break; @@ -574,7 +716,7 @@ int ggg(Json *object, const char *spec, int verbose) stack.pop_back(); } break; default: { - ASSERT(0 == "illegal"); + ASSERT(false && "illegal"); } break; } } break; @@ -631,12 +773,12 @@ int ggg(Json *object, const char *spec, int verbose) .d_json_p->theObject()[stack.back().d_member]); } break; default: { - ASSERT(0 == "illegal"); + ASSERT(false && "illegal"); } break; } } break; default: { - ASSERT(0 == "illegal"); + ASSERT(false && "illegal"); } break; } } @@ -781,12 +923,12 @@ static const DefaultDataRow DEFAULT_DATA[] = }; enum { DEFAULT_NUM_DATA = sizeof DEFAULT_DATA / sizeof *DEFAULT_DATA }; -char KEY0[] = "zero"; -char KEY1[] = "one"; -char KEY2[] = "two"; -char KEY3[] = "three"; +const char KEY0[] = "zero"; +const char KEY1[] = "one"; +const char KEY2[] = "two"; +const char KEY3[] = "three"; -const char* KEYS[] = {KEY0, KEY1, KEY2, KEY3}; +const char *const KEYS[] = {KEY0, KEY1, KEY2, KEY3}; template class TemplatedJsonObjectInsertionTest { @@ -870,7 +1012,8 @@ class ReorderExpectedJsonObjectString { for (int i = 0; i < 4; ++i) { pos = input.find('\"', prev = pos); if (bsl::string::npos == pos) { - ASSERTV(i, input, 0 != "Quote character number i not found"); + ASSERTV(i, input, false && + "Quote character number i not found"); return ""; // RETURN } format[i] = input.substr(prev, pos - prev); @@ -878,22 +1021,21 @@ class ReorderExpectedJsonObjectString { const char delim = (i == 3 ? ']' : ','); pos = input.find(delim, prev = pos); if (bsl::string::npos == pos) { - ASSERTV(delim, i, input, 0 != "delimiter not found"); + ASSERTV(delim, i, input, false && "delimiter not found"); return ""; // RETURN } if (delim == ']') { ++pos; if (pos == input.size()) { - ASSERTV(i, - input, - 0 != "']' character was last in string"); + ASSERTV(i, input, false && + "\']\' character was last in string"); return ""; // RETURN } } const bsl::string::size_type secondQuote = input.find('\"', prev + 1); if (secondQuote == bsl::string::npos) { - ASSERTV(i, input, 0 != "second quote not found"); + ASSERTV(i, input, false && "second quote not found"); return ""; // RETURN } data[input.substr(prev + 1, secondQuote - (prev + 1))] = @@ -1089,8 +1231,8 @@ void extendedBreathingTest(BSLA_MAYBE_UNUSED bool verbose, // Intializer-List Assignment { JsonArray mX; - mX.assign({g("1"), g("s")}); - ASSERT(mX == ga("[1s]")); + ASSERT(&mX == &mX.assign({g("1"), g("s")})); + ASSERT( mX == ga("[1s]")); } #endif @@ -1099,8 +1241,8 @@ void extendedBreathingTest(BSLA_MAYBE_UNUSED bool verbose, Json range[3] = {g("n"), g("n"), g("n")}; JsonArray mX; - mX.assign(range, range + 3); - ASSERT(mX == ga("[nnn]")); + ASSERT(&mX == &mX.assign(range, range + 3)); + ASSERT( mX == ga("[nnn]")); } // Begin and End @@ -2412,6 +2554,38 @@ void extendedBreathingTest(BSLA_MAYBE_UNUSED bool verbose, ASSERT(m8Value == 1); ASSERT(m8.allocator() == &ta); + Json m7a(1L); + ASSERT(m7a.isNumber()); + long m7aValue; + rc = m7a.asLong(&m7aValue); + ASSERT(rc == 0); + ASSERT(m7aValue == 1L); + ASSERT(m7a.allocator() == bslma::Default::allocator()); + + Json m8a(1L, &ta); + ASSERT(m8a.isNumber()); + long m8aValue; + rc = m8a.asLong(&m8aValue); + ASSERT(rc == 0); + ASSERT(m8aValue == 1L); + ASSERT(m8a.allocator() == &ta); + + Json m7b(1LL); + ASSERT(m7b.isNumber()); + long long m7bValue; + rc = m7b.asLonglong(&m7bValue); + ASSERT(rc == 0); + ASSERT(m7bValue == 1LL); + ASSERT(m7b.allocator() == bslma::Default::allocator()); + + Json m8b(1LL, &ta); + ASSERT(m8b.isNumber()); + long long m8bValue; + rc = m8b.asLonglong(&m8bValue); + ASSERT(rc == 0); + ASSERT(m8bValue == 1LL); + ASSERT(m8b.allocator() == &ta); + Json m9(1u); unsigned int m9Value; rc = m9.asUint(&m9Value); @@ -2426,6 +2600,34 @@ void extendedBreathingTest(BSLA_MAYBE_UNUSED bool verbose, ASSERT(m10Value == 1u); ASSERT(m10.allocator() == &ta); + Json m9a(1ul); + unsigned long m9aValue; + rc = m9a.asUlong(&m9aValue); + ASSERT(rc == 0); + ASSERT(m9aValue == 1ul); + ASSERT(m9a.allocator() == bslma::Default::allocator()); + + Json m10a(1ul, &ta); + unsigned long m10aValue; + rc = m10a.asUlong(&m10aValue); + ASSERT(rc == 0); + ASSERT(m10aValue == 1ul); + ASSERT(m10a.allocator() == &ta); + + Json m9b(1ull); + unsigned long long m9bValue; + rc = m9b.asUlonglong(&m9bValue); + ASSERT(rc == 0); + ASSERT(m9bValue == 1ull); + ASSERT(m9b.allocator() == bslma::Default::allocator()); + + Json m10b(1ull, &ta); + unsigned long long m10bValue; + rc = m10b.asUlonglong(&m10bValue); + ASSERT(rc == 0); + ASSERT(m10bValue == 1ul); + ASSERT(m10b.allocator() == &ta); + Json m11(static_cast(1)); bsls::Types::Int64 m11Value; rc = m11.asInt64(&m11Value); @@ -3348,6 +3550,13 @@ template <> class JsonValueConstructorHelper { public: + /// Returns the value of specified type from the specified 'json', + /// suitable for comparison to the value used in construction. + static const JsonNull& extractFromJson(const Json& json, int , char ) + { + return json.theNull(); + } + /// Returns the `JsonType::Enum` of the specified type static JsonType::Enum getType() { @@ -3361,7 +3570,6 @@ class JsonValueConstructorHelper } }; - template <> class JsonValueConstructorHelper { @@ -3379,7 +3587,6 @@ class JsonValueConstructorHelper return JsonType::e_BOOLEAN; } - /// Returns a `const` reference to a value of the specified type static bool getValue() { @@ -3404,7 +3611,6 @@ class JsonValueConstructorHelper return JsonType::e_NUMBER; } - /// Returns a `const` reference to a value of the specified type static float getValue() { @@ -3429,7 +3635,6 @@ class JsonValueConstructorHelper return JsonType::e_NUMBER; } - /// Returns a `const` reference to a value of the specified type static double getValue() { @@ -3454,7 +3659,6 @@ class JsonValueConstructorHelper return JsonType::e_ARRAY; } - /// Returns a `const` reference to a value of the specified type static const JsonArray& getValue() { @@ -3546,7 +3750,7 @@ class JsonValueConstructorHelper }; template <> -class JsonValueConstructorHelper +class JsonValueConstructorHelper { public: /// Returns the value of specified type from the specified `json`, @@ -3557,8 +3761,67 @@ class JsonValueConstructorHelper int ALLOC_CONFIG, char CONFIG) { - bsls::Types::Int64 value; - ASSERTV(ALLOC_CONFIG, CONFIG, 0 == json.theNumber().asInt64(&value)); + long value; + ASSERTV(ALLOC_CONFIG, CONFIG, 0 == json.theNumber().asLong(&value)); + return value; + } + static JsonType::Enum getType() + // Returns the 'JsonType::Enum' of the specified type + { + return JsonType::e_NUMBER; + } + + static long getValue() + // Returns a 'const' reference to a value of the specified type + { + return -54321L; + } +}; + +template <> +class JsonValueConstructorHelper +{ + public: + static unsigned long extractFromJson(const Json& json, + long ALLOC_CONFIG, + char CONFIG) + // Returns the value of specified type from the specified 'json', + // suitable for comparison to the value used in construction. The + // specified 'ALLOC_CONFIG' and the specified 'CONFIG' will be used to + // report any failure to convert. + { + unsigned long value; + ASSERTV(ALLOC_CONFIG, CONFIG, 0 == json.theNumber().asUlong(&value)); + return value; + } + static JsonType::Enum getType() + // Returns the 'JsonType::Enum' of the specified type + { + return JsonType::e_NUMBER; + } + + static unsigned long getValue() + // Returns a 'const' reference to a value of the specified type + { + return 98765UL; + } +}; + +template <> +class JsonValueConstructorHelper +{ + public: + static long long extractFromJson(const Json& json, + int ALLOC_CONFIG, + char CONFIG) + // Returns the value of specified type from the specified 'json', + // suitable for comparison to the value used in construction. The + // specified 'ALLOC_CONFIG' and the specified 'CONFIG' will be used to + // report any failure to convert. + { + long long value; + ASSERTV(ALLOC_CONFIG, CONFIG, 0 == json.theNumber() + .asLonglong(&value)); return value; } /// Returns the `JsonType::Enum` of the specified type @@ -3575,7 +3838,7 @@ class JsonValueConstructorHelper }; template <> -class JsonValueConstructorHelper +class JsonValueConstructorHelper { public: /// Returns the value of specified type from the specified `json`, @@ -3586,8 +3849,9 @@ class JsonValueConstructorHelper int ALLOC_CONFIG, char CONFIG) { - bsls::Types::Uint64 value; - ASSERTV(ALLOC_CONFIG, CONFIG, 0 == json.theNumber().asUint64(&value)); + unsigned long long value; + ASSERTV(ALLOC_CONFIG, CONFIG, 0 == json.theNumber() + .asUlonglong(&value)); return value; } /// Returns the `JsonType::Enum` of the specified type @@ -3599,7 +3863,7 @@ class JsonValueConstructorHelper /// Returns a `const` reference to a value of the specified type static bsls::Types::Uint64 getValue() { - return 0x987654321LL; + return 0x987654321ULL; } }; @@ -3669,7 +3933,7 @@ class JsonValueConstructorHelper }; template <> -class JsonValueConstructorHelper +class JsonValueConstructorHelper { public: /// Returns the value of specified type from the specified `json`, @@ -3734,7 +3998,6 @@ class JsonValueConstructorHelper return JsonType::e_STRING; } - /// Returns a `const` reference to a value of the specified type static const bsl::string& getValue() { @@ -3812,44 +4075,3523 @@ class JsonValueEnumeration allocated = true; } break; default: { - BSLS_ASSERT_OPT(0 == "Bad JSON value enumerator config"); + BSLS_ASSERT_OPT(false && "Bad JSON value enumerator config"); allocated = false; } break; } - return allocated; - } -}; -// ============================================================================ -// MAIN PROGRAM -// ---------------------------------------------------------------------------- + return allocated; + } +}; + +template +void testJsonPushBack(char cfg, int numBlocksPush, bool verbose) + // Test the correctness of a 'Json::pushBack' of a (template argument) + // 'TYPE' onto an null 'Json' object. Use the specified 'cfg' in + // diagnostic messages, if any. The specified 'numBlocksPush' is the + // expected number of allocations by the 'pushBack' call. The specified + // 'verbose', if 'true', sets the 'TestAllocator' to log. The value pushed + // is obtained from 'JsonValueConstructorHelper::getValue()'. Also + // perform a negative test that an initial type other than array or null + // fails the defensive check. +{ + if (verbose) { + P(bsls::NameOf()); + } + + typedef JsonValueConstructorHelper Helper; + typedef bdljsn::Json Obj; + + const int ALLOC_CONFIG = -777; // Arbitrary diagnostic value. + const char CONFIG = cfg; + + bslma::TestAllocator ta("testJsonPushBack", verbose); + bslma::TestAllocatorMonitor tam(&ta); + + Obj obj(&ta); + ASSERTV(obj.isNull()); + + ASSERT(&obj == &obj.pushBack(Helper::getValue())); // TEST + + ASSERTV(obj.isArray()); + ASSERTV(obj.size(), 1 == obj.size()); + + Obj *objPtr = &obj[0]; + + ASSERTV(ALLOC_CONFIG, + CONFIG, + numBlocksPush, tam.numBlocksInUseChange(), + numBlocksPush == tam.numBlocksInUseChange()); + + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::getType(), + objPtr->type(), + Helper::getType() == objPtr->type()); + + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG), + Helper::getValue(), + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG) + == Helper::getValue()); + +#ifdef BDE_BUILD_TARGET_EXC + { + bsls::AssertTestHandlerGuard hG; + + Obj thingNull; thingNull .makeNull(); + Obj thingArray; thingArray .makeArray(); + + ASSERT_PASS(thingNull .pushBack(Helper::getValue())); + ASSERT_PASS(thingArray.pushBack(Helper::getValue())); + + Obj thingBoolean; thingBoolean.makeBoolean(); + Obj thingNumber; thingNumber .makeNumber(); + Obj thingString; thingString .makeString(bsl::string()); + Obj thingObject; thingObject .makeObject(); + + ASSERT_FAIL(thingBoolean.pushBack(Helper::getValue())); + ASSERT_FAIL(thingNumber .pushBack(Helper::getValue())); + ASSERT_FAIL(thingString .pushBack(Helper::getValue())); + ASSERT_FAIL(thingObject .pushBack(Helper::getValue())); + } +#endif + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE + { + using RetType = decltype(obj.pushBack(Helper::getValue())); + + static_assert(bsl::is_same::value, ""); + } +#endif +} + +template <> +void testJsonPushBack(char cfg, int numBlocksPush, bool verbose) + // Test the correctness of a 'Json::pushBack' onto an null + // 'Json' object. Use the specified 'cfg' in diagnostic messages, if any. + // The specified 'numBlocksPush' is the expected number of allocations by + // the 'pushBack' call. The specified 'verbose', if 'true', sets the + // 'TestAllocator' to log. The value pushed is composed manually using + // 'JsonValueConstructorHelper::getValue()' to get values for the + // constituent types. Also perform a negative test that initial type other + // than array or null fails the defensive check. +{ + if (verbose) { + P(bsls::NameOf()); + } + + typedef bdljsn::Json Obj; + typedef bdljsn::Json Value; + + const int ALLOC_CONFIG = -888; // Arbitrary diagnostic value. + const char CONFIG = cfg; + + bslma::TestAllocator ta("testJsonPushBack", verbose); + bslma::TestAllocatorMonitor tam(&ta); + + Value value; const Value& cValue = value; + value.makeArray(); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + + Obj obj(&ta); + ASSERTV(obj.isNull()); + + ASSERT(&obj == &obj.pushBack(cValue)); // TEST + + ASSERTV(obj.isArray()); + ASSERTV(obj.size(), 1 == obj.size()); + + ASSERTV(obj[0] == cValue); + + ASSERTV(ALLOC_CONFIG, + CONFIG, + numBlocksPush, tam.numBlocksInUseChange(), + numBlocksPush == tam.numBlocksInUseChange()); + +#ifdef BDE_BUILD_TARGET_EXC + { + bsls::AssertTestHandlerGuard hG; + + Obj thingNull; thingNull .makeNull(); + Obj thingArray; thingArray .makeArray(); + + ASSERT_PASS(thingNull .pushBack(cValue)); + ASSERT_PASS(thingArray.pushBack(cValue)); + + Obj thingBoolean; thingBoolean.makeBoolean(); + Obj thingNumber; thingNumber .makeNumber(); + Obj thingString; thingString .makeString(bsl::string()); + Obj thingObject; thingObject .makeObject(); + + ASSERT_FAIL(thingBoolean.pushBack(cValue)); + ASSERT_FAIL(thingNumber .pushBack(cValue)); + ASSERT_FAIL(thingString .pushBack(cValue)); + ASSERT_FAIL(thingObject .pushBack(cValue)); + } +#endif +#ifdef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE + { + using RetType = decltype(obj.pushBack(cValue)); + + static_assert(bsl::is_same::value, ""); + } +#endif +} + +template +void testJsonPushBackMoveRef(char cfg, bool verbose) + // Test the correctness of a 'Json::pushBack' of a moveable reference of + // (template argument) 'TYPE' onto an null 'Json' object. The value pushed + // is obtained from JsonValueConstructorHelper::getValue()'. Since + // each object pushed is (here) constructed using the same allocator as the + // 'JsonArray', each 'pushBack' takes one allocation so the array can hold + // the added object but none for the object itself. The specified 'cfg' is + // used in diagnostic messages, if any. The specified 'verbose', if + // 'true', sets the 'TestAllocator' to log. Also perform a negative test + // that initial type other than array or null fails the defensive check. +{ + typedef JsonValueConstructorHelper Helper; + typedef bdljsn::Json Obj; + + const int ALLOC_CONFIG = -42; // Arbitrary diagnostic value. + const char CONFIG = cfg; + + bslma::TestAllocator ta("testJsonPushBackMoveRef", verbose); + bslma::TestAllocatorMonitor tam(&ta); + + TYPE element(Helper::getValue(), &ta); // same allocator as 'obj'. + + tam.reset(); + + Obj obj(&ta); + ASSERTV(obj.isNull()); + + ASSERT(&obj == &obj.pushBack(bslmf::MovableRefUtil::move(element)));// TEST + + ASSERTV(obj.isArray()); + ASSERTV(obj.size(), 1 == obj.size()); + + Obj *objPtr = &obj[0]; + + ASSERTV(ALLOC_CONFIG, + tam.numBlocksInUseChange(), + CONFIG, + 1 == tam.numBlocksInUseChange()); + // One allocation to expand the array's data to hold the new + // element. Constructing that element requires no additional + // allocation it is moved from an object that uses the same + // allocator. + + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::getType(), + objPtr->type(), + Helper::getType() == objPtr->type()); + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG), + Helper::getValue(), + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG) + == Helper::getValue()); + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE + { + using RetType = decltype( + obj.pushBack(bslmf::MovableRefUtil::move(element))); + + static_assert(bsl::is_same::value, ""); + } +#endif +} + +template <> +void testJsonPushBackMoveRef(char cfg, bool verbose) + // Test the correctness of a 'Json::pushBack' of a moveable reference of + // (template argument) 'TYPE' onto an null 'Json' object. Since each + // object pushed is (here) constructed using the same allocator as the + // 'Json', each 'pushBack' takes one allocation so the array can hold the + // added object but none for the object itself. The specified 'cfg' is + // used in diagnostic messages, if any. The specified 'verbose', if + // 'true', sets the 'TestAllocator' to log. The value pushed is composed + // manually using 'JsonValueConstructorHelper::getValue()' to get + // values for the constituent types. Also perform a negative test that + // initial type other than array or null fails the defensive check. +{ + typedef bdljsn::Json Obj; + typedef bdljsn::Json Value; + + const int ALLOC_CONFIG = -42; // Arbitrary diagnostic value. + const char CONFIG = cfg; + + bslma::TestAllocator ta("testJsonPushBackMoveRef", verbose); + bslma::TestAllocatorMonitor tam(&ta); + + Value value(&ta); // same allocator as 'obj' below + value.makeArray(); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + value.pushBack(JsonValueConstructorHelper::getValue()); + + const Value savedValue(value); + + tam.reset(); + + Obj obj(&ta); + ASSERTV(obj.isNull()); + + ASSERTV(&obj == &obj.pushBack(bslmf::MovableRefUtil::move(value))); // TEST + + ASSERTV(obj.isArray()); + ASSERTV(obj.size(), 1 == obj.size()); + ASSERTV(savedValue, obj[0], + savedValue == obj[0]); + + ASSERTV(ALLOC_CONFIG, + tam.numBlocksInUseChange(), + CONFIG, + 1 == tam.numBlocksInUseChange()); + // One allocation to expand the array's data to hold the new + // element. Constructing that element requires no additional + // allocation it is moved from an object that uses the same + // allocator. + +#ifdef BDE_BUILD_TARGET_EXC + { + Value valueForNull (savedValue); + Value valueForArray (savedValue); + Value valueForBoolean(savedValue); + Value valueForNumber (savedValue); + Value valueForString (savedValue); + Value valueForObject (savedValue); + + Obj thingNull; thingNull .makeNull(); + Obj thingArray; thingArray .makeArray(); + Obj thingBoolean; thingBoolean.makeBoolean(); + Obj thingNumber; thingNumber .makeNumber(); + Obj thingString; thingString .makeString(bsl::string()); + Obj thingObject; thingObject .makeObject(); + + typedef bslmf::MovableRefUtil MRU; + + bsls::AssertTestHandlerGuard hG; + + ASSERT_PASS(thingNull .pushBack(MRU::move(valueForNull ))); + ASSERT_PASS(thingArray .pushBack(MRU::move(valueForArray ))); + + ASSERT_FAIL(thingBoolean.pushBack(MRU::move(valueForBoolean))); + ASSERT_FAIL(thingNumber .pushBack(MRU::move(valueForNumber ))); + ASSERT_FAIL(thingString .pushBack(MRU::move(valueForString ))); + ASSERT_FAIL(thingObject .pushBack(MRU::move(valueForObject ))); + } +#endif + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE + { + using RetType = decltype( + obj.pushBack(bslmf::MovableRefUtil::move(value))); + + static_assert(bsl::is_same::value, ""); + } +#endif +} + +template +void testArrayPushBack(char cfg, int numBlocksPush, bool verbose) + // Test the correctness of a 'JsonArray::pushBack' of a (template argument) + // 'TYPE' onto an empty 'JsonArray'. Use the specified 'cfg' in diagnostic + // messages, if any. The specified 'numBlocksPush' is the expected number + // of allocations by the 'pushBack' call. The specified 'verbose', if + // 'true', sets the 'TestAllocator' to log. The value pushed is obtained + // from 'JsonValueConstructorHelper::getValue()'. +{ + if (verbose) { + P(bsls::NameOf()); + } + + typedef JsonValueConstructorHelper Helper; + typedef bdljsn::Json Obj; + typedef bdljsn::JsonArray Array; + + const int ALLOC_CONFIG = -666; // Arbitrary diagnostic value. + const char CONFIG = cfg; + + bslma::TestAllocator ta("testArrayPushBack", verbose); + bslma::TestAllocatorMonitor tam(&ta); + + Array array(&ta); + ASSERT(&array == &array.pushBack(Helper::getValue())); // TEST + ASSERTV(array.size(), 1 == array.size()); + + Obj *objPtr = &array[0]; + + ASSERTV(ALLOC_CONFIG, + CONFIG, + numBlocksPush, tam.numBlocksInUseChange(), + numBlocksPush == tam.numBlocksInUseChange()); + + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::getType(), + objPtr->type(), + Helper::getType() == objPtr->type()); + + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG), + Helper::getValue(), + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG) + == Helper::getValue()); + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE + { + using RetType = decltype(array.pushBack(Helper::getValue())); + + static_assert(bsl::is_same::value, ""); + } +#endif +} + +template +void testArrayPushBackMoveRef(char cfg, bool verbose) + // Test the correctness of a 'JsonArray::pushBack' of a moveable reference + // of (template argument) 'TYPE' onto an empty 'JsonArray'. The value + // pushed is obtained from JsonValueConstructorHelper::getValue()'. + // Since each object pushed is (here) constructed using the same allocator + // as the 'JsonArray' each 'pushBack' takes one allocation so the array can + // hold the added object but none for the object itself. The specified + // 'cfg' is used in diagnostic messages, if any. The specified 'verbose', + // if 'true', sets the 'TestAllocator' to log. +{ + typedef JsonValueConstructorHelper Helper; + typedef bdljsn::Json Obj; + typedef bdljsn::JsonArray Array; + + const int ALLOC_CONFIG = -42; // Arbitrary diagnostic value. + const char CONFIG = cfg; + + bslma::TestAllocator ta("testArrayPushBackMoveRef", verbose); + bslma::TestAllocatorMonitor tam(&ta); + + Array array(&ta); + TYPE element(Helper::getValue(), &ta); // same allocator + + tam.reset(); + + ASSERT(&array == &array.pushBack(bslmf::MovableRefUtil::move(element))); + // TEST + ASSERTV(array.size(), 1 == array.size()); + + Obj *objPtr = &array[0]; + + ASSERTV(ALLOC_CONFIG, + tam.numBlocksInUseChange(), + CONFIG, + 1 == tam.numBlocksInUseChange()); + // One allocation to expand the array's data to hold the new + // element. Constructing that element requires no additional + // allocation it is moved from an object that uses the same + // allocator. + + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::getType(), + objPtr->type(), + Helper::getType() == objPtr->type()); + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG), + Helper::getValue(), + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG) + == Helper::getValue()); + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE + { + using RetType = decltype(array.pushBack(Helper::getValue())); + + static_assert(bsl::is_same::value, ""); + } +#endif +} + +template +void testEqualityOverloadsRef() + // Test equality comparison operators between a 'Json' and a 'const'- + // reference of a (template parameter) 'TYPE'. Comparisons are made with + // the 'Json' on the left and the 'TYPE' on the right, and vice versa. + // Each test of equality is mirrored by a test of inequality. + // Additionally, comparisons of 'TYPE' and 'JsonNull' are tested. +{ + const TYPE& notaJsonD= TYPE(); + const TYPE& notaJsonA = JsonValueConstructorHelper::getValue(); + + typedef bdljsn::Json Obj; + + const Obj& objD = Obj(notaJsonD); + const Obj& objA = Obj(notaJsonA); + + // operator== value, obj + ASSERT( notaJsonD == objD ); + ASSERT(!(notaJsonD == objA) ); + ASSERT(!(notaJsonA == objD) ); + ASSERT( notaJsonA == objA ); + + // operator!= value, obj + ASSERT(!(notaJsonD != objD) ); + ASSERT( notaJsonD != objA ); + ASSERT( notaJsonA != objD ); + ASSERT(!(notaJsonA != objA) ); + + // operator== obj, value + ASSERT( objD == notaJsonD ); + ASSERT(!(objD == notaJsonA)); + ASSERT(!(objA == notaJsonD)); + ASSERT( objA == notaJsonA ); + + // operator!= obj, value + ASSERT(!(objD != notaJsonD)); + ASSERT( objD != notaJsonA ); + ASSERT( objA != notaJsonD ); + ASSERT(!(objA != notaJsonA)); + + // Test 'const JsonNull&' + + typedef bdljsn::JsonNull Null; + + const Null& null = Null(); // "default" is the only value. + + // operator== null, obj + ASSERT(!(null == objA) ); + ASSERT(!(null == objD) ); + + // operator!= null, obj + ASSERT( null != objA ); + ASSERT( null != objD ); + + // operator== obj, value + ASSERT(!(objD == null )); + ASSERT(!(objA == null )); + + // operator!= obj, value + ASSERT( objD != null ); + ASSERT( objA != null ); +} + +template +void testEqualityOverloadsVal() + // Test equality comparison operators between a 'Json' and a value of + // (template parameter) 'TYPE'. Comparisons are made with the 'Json' on + // the left and the 'TYPE' on the right, and vice versa. Each test of + // equality is mirrored by a test of inequality. Additionally, comparisons + // of 'TYPE' and 'JsonNull' are tested. +{ + VALUE valueD = VALUE(0); + VALUE valueA = JsonValueConstructorHelper::getValue(); + + typedef bdljsn::Json Obj; + + const Obj& objD = Obj(valueD); + const Obj& objA = Obj(valueA); + + // operator== value, obj + ASSERT( valueD == objD ); + ASSERT(!(valueD == objA) ); + ASSERT(!(valueA == objD) ); + ASSERT( valueA == objA ); + + // operator!= value, obj + ASSERT(!(valueD != objD) ); + ASSERT( valueD != objA ); + ASSERT( valueA != objD ); + ASSERT(!(valueA != objA) ); + + // operator== obj, value + ASSERT( objD == valueD ); + ASSERT(!(objD == valueA)); + ASSERT(!(objA == valueD)); + ASSERT( objA == valueA ); + + // operator!= obj, value + ASSERT(!(objD != valueD)); + ASSERT( objD != valueA ); + ASSERT( objA != valueD ); + ASSERT(!(objA != valueA)); + + // Test 'const JsonNull&' + // + typedef bdljsn::JsonNull Null; + + const Null& null = Null(); // "default" is the only value. + + // operator== null, obj + ASSERT(!(null == objA) ); + ASSERT(!(null == objD) ); + + // operator!= null, obj + ASSERT( null != objA ); + ASSERT( null != objD ); + + // operator== obj, value + ASSERT(!(objD == null )); + ASSERT(!(objA == null )); + + // operator!= obj, value + ASSERT( objD != null ); + ASSERT( objA != null ); +} + +void testEqualityOverloadsStr() + // Test equality comparison operators between a 'Json' and the + // 'const char *' type. Comparisons are made with the 'Json' on the left + // and the 'TYPE' on the right, and vice versa. Each test of equality is + // mirrored by a test of inequality. The defensive check for the + // precondition -- the address is non-null- is tested. Additionally, + // comparisons of 'TYPE' and 'JsonNull' are tested. +{ + const char *strD = ""; + const char *strA = JsonValueConstructorHelper::getValue(); + + typedef bdljsn::Json Obj; + + const Obj& objD = Obj(strD); + const Obj& objA = Obj(strA); + + // operator== str, obj + ASSERT( strD == objD ); + ASSERT(!(strD == objA)); + ASSERT(!(strA == objD)); + ASSERT( strA == objA ); + + // operator!= str, obj + ASSERT(!(strD != objD)); + ASSERT( strD != objA ); + ASSERT( strA != objD ); + ASSERT(!(strA != objA)); + + // operator== obj, str + ASSERT( objD == strD ); + ASSERT(!(objD == strA)); + ASSERT(!(objA == strD)); + ASSERT( objA == strA ); + + // operator!= obj, str + ASSERT(!(objD != strD)); + ASSERT( objD != strA ); + ASSERT( objA != strD ); + ASSERT(!(objA != strA)); + +#ifdef BDE_BUILD_TARGET_EXC + bsls::AssertTestHandlerGuard hG; + + const char *const nullStr = 0; + const char *const notNullStr = "non-empty string literal"; + + ASSERT_FAIL( nullStr == objA); + ASSERT_PASS(notNullStr == objA); + + ASSERT_FAIL( nullStr != objA); + ASSERT_PASS(notNullStr != objA); + + ASSERT_FAIL( objA == nullStr); + ASSERT_PASS( objA == notNullStr); + + ASSERT_FAIL( objA != nullStr); + ASSERT_PASS( objA != notNullStr); +#endif +} + +template +void compileCheckEqualitySignature() + // Use the "function pointer' idiom to confirm that the equality (and + // inequality operators between 'Json' and the (template parameter) 'TYPE' + // have the expected signatures. 'Json' and 'TYPE' are compared in the + // positions of 'lhs' and 'rhs', and vice versa. Successful compilation of + // this function indicates correctness. +{ + typedef bdljsn::Json Obj; + + typedef bool (*operatorPtrA)(TYPE, const Obj&); + + operatorPtrA operatorEqA = operator==; (void) operatorEqA; + operatorPtrA operatorNeA = operator!=; (void) operatorNeA; + + typedef bool (*operatorPtrB)(const Obj& , TYPE ); + + operatorPtrB operatorEqB = operator==; (void) operatorEqB; + operatorPtrB operatorNeB = operator!=; (void) operatorNeB; +} + +template +struct ConvertibleTo { + operator TYPE() const + // Return a default-constructed object of type 'TYPE'. + { + return TYPE(); + } +}; + +// ============================================================================ +// HELPERS for 'visit' +// ---------------------------------------------------------------------------- + + // ============================ + // class TestJsonVisitorReturns + // ============================ + +template +class TestJsonVisitorReturns +{ + bsl::vector *d_output_p; + bool d_isVerbose; + + public: + // CREATORS + explicit TestJsonVisitorReturns(bsl::vector *output = 0, + bool isVerbose = false); + // Create a 'TestJsonVistorReturns' object having all overloads + // required for the manipulator overloads of 'Json::visit'. Optionally + // specify 'isVerbose' for the generation of trace statements to + // standard output. Optionally specify 'output', a vector to which the + // returned value is pushed. Note that 'output' is useful when the + // return type is 'void'. + + // ACCESSORS + RETURN_TYPE operator()(JsonObject *object ) const; + RETURN_TYPE operator()(JsonArray *array ) const; + RETURN_TYPE operator()(bsl::string *string ) const; + RETURN_TYPE operator()(JsonNumber *number ) const; + RETURN_TYPE operator()(bool *boolean) const; + RETURN_TYPE operator()(JsonNull *null ) const; + // Return 'RETURN_TYPE()'. The specified + // 'object/array/string/number/boolean/null' is ignored. Push the + // '-(type() + 1)' to 'output' if provided on construction. + + RETURN_TYPE operator()(const JsonObject& object ) const; + RETURN_TYPE operator()(const JsonArray& array ) const; + RETURN_TYPE operator()(const bsl::string& string ) const; + RETURN_TYPE operator()(const JsonNumber& number ) const; + RETURN_TYPE operator()(const bool& boolean) const; + RETURN_TYPE operator()(const JsonNull& null ) const; + // Return 'RETURN_TYPE()'. The specified + // 'object/array/string/number/boolean/null' is ignored. Push the + // 'type() + 1' to 'output' if provided on construction. +}; + + // ---------------------------- + // class TestJsonVisitorReturns + // ---------------------------- + +// CREATORS +template +TestJsonVisitorReturns::TestJsonVisitorReturns( + bsl::vector *output, + bool isVerbose) +: d_output_p(output) +, d_isVerbose(isVerbose) +{ +} + +// ACCESSOR +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(JsonObject *object) const +{ + BSLS_ASSERT(object); + + const int value = -(bdljsn::JsonType::e_OBJECT + 1); + + if (d_isVerbose) { P_(object) Q(OBJECT: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(JsonArray *array) const +{ + BSLS_ASSERT(array); + + const int value = -(bdljsn::JsonType::e_ARRAY + 1); + + if (d_isVerbose) { P_(array) Q(ARRAY: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(bsl::string *string) const +{ + BSLS_ASSERT(string); + + const int value = -(bdljsn::JsonType::e_STRING + 1); + + if (d_isVerbose) { P_(string) Q(STRING: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(JsonNumber *number) const +{ + BSLS_ASSERT(number); + + const int value = -(bdljsn::JsonType::e_NUMBER + 1); + + if (d_isVerbose) { P_(number) Q(NUMBER: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); + +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(bool *boolean) const +{ + BSLS_ASSERT(boolean); + + const int value = -(bdljsn::JsonType::e_BOOLEAN + 1); + + if (d_isVerbose) { P_(boolean) Q(BOOLEAN: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(JsonNull *null) const +{ + BSLS_ASSERT(null); + + const int value = -(bdljsn::JsonType::e_NULL + 1); + + if (d_isVerbose) { P_(null) Q(NULL: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(const JsonObject& object) const +{ + (void) object; + + const int value = bdljsn::JsonType::e_OBJECT + 1; + + if (d_isVerbose) { Q(OBJECT: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(const JsonArray& array) const +{ + (void) array; + + const int value = bdljsn::JsonType::e_ARRAY + 1; + + if (d_isVerbose) { Q(ARRAY: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(const bsl::string& string) + const +{ + (void) string; + + const int value = bdljsn::JsonType::e_STRING + 1; + + if (d_isVerbose) { Q(STRING: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(const JsonNumber& number) const +{ + (void) number; + + const int value = bdljsn::JsonType::e_NUMBER + 1; + + if (d_isVerbose) { Q(NUMBER: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(const bool& boolean) const +{ + (void) boolean; + + const int value = bdljsn::JsonType::e_BOOLEAN + 1; + + if (d_isVerbose) { Q(BOOLEAN: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + +template +RETURN_TYPE +TestJsonVisitorReturns::operator()(const JsonNull& null) const +{ + (void) null; + + const int value = bdljsn::JsonType::e_NULL + 1; + + if (d_isVerbose) { Q(NULL: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return RETURN_TYPE(); +} + + //--------------------------------------------------------------------- + // class TestJsonVisitorReturns + //--------------------------------------------------------------------- + +// ACCESSOR +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + JsonObject *object) const +{ + BSLS_ASSERT(object); + + const int value = -(bdljsn::JsonType::e_OBJECT + 1); + + if (d_isVerbose) { P_(object) Q(OBJECT: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + JsonArray *array) const +{ + BSLS_ASSERT(array); + + const int value = -(bdljsn::JsonType::e_ARRAY + 1); + + if (d_isVerbose) { P_(array) Q(ARRAY: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + bsl::string *string) const +{ + BSLS_ASSERT(string); + + const int value = -(bdljsn::JsonType::e_STRING + 1); + + if (d_isVerbose) { P_(string) Q(STRING: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + JsonNumber *number) const +{ + BSLS_ASSERT(number); + + const int value = -(bdljsn::JsonType::e_NUMBER + 1); + + if (d_isVerbose) { P_(number) Q(NUMBER: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); + +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + bool *boolean) const +{ + BSLS_ASSERT(boolean); + + const int value = -(bdljsn::JsonType::e_BOOLEAN + 1); + + if (d_isVerbose) { P_(boolean) Q(BOOLEAN: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + JsonNull *null) const +{ + BSLS_ASSERT(null); + + const int value = -(bdljsn::JsonType::e_NULL + 1); + + if (d_isVerbose) { P_(null) Q(NULL: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + const JsonObject& object) const +{ + (void) object; + + const int value = bdljsn::JsonType::e_OBJECT + 1; + + if (d_isVerbose) { Q(OBJECT: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + const JsonArray& array) const +{ + (void) array; + + const int value = bdljsn::JsonType::e_ARRAY + 1; + + if (d_isVerbose) { Q(ARRAY: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + const bsl::string& string) const +{ + (void) string; + + const int value = bdljsn::JsonType::e_STRING + 1; + + if (d_isVerbose) { Q(STRING: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + const JsonNumber& number) const +{ + (void) number; + + const int value = bdljsn::JsonType::e_NUMBER + 1; + + if (d_isVerbose) { Q(NUMBER: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + const bool& boolean) const +{ + (void) boolean; + + const int value = bdljsn::JsonType::e_BOOLEAN + 1; + + if (d_isVerbose) { Q(BOOLEAN: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + +template <> +bsltf::NonDefaultConstructibleTestType +TestJsonVisitorReturns::operator()( + const JsonNull& null) const +{ + (void) null; + + const int value = bdljsn::JsonType::e_NULL + 1; + + if (d_isVerbose) { Q(NULL: Returns); } + if (d_output_p) { d_output_p->push_back(value); } + + return bsltf::NonDefaultConstructibleTestType(0); +} + + // ============================== + // class TestJsonVisitorManipAvec + // ============================== + +class TestJsonVisitorManipAvec +{ + // This class defines 'operator()' overloads for the *manipulator* + // overloads of the 'Json' 'visitor' method. These 'operator()' overloads + // have the 'const'-qualification ("avec") that prevents modifying the + // 'visitor' argument. + + bsl::vector *d_output_p; + bool d_isVerbose; + + public: + // CREATORS + explicit TestJsonVisitorManipAvec(bsl::vector *output = 0, + bool isVerbose = false); + // Create a 'TestJsonVistorManipAces' object having all overloads + // required for the manipulator overloads of 'Json::visit'. Optionally + // specify 'isVerbose' for the generation of trace statements to + // standard output. Optionally specify 'output', a vector to which the + // returned value is pushed. Note that 'output' is useful when the + // return type is 'void'. + + // ACCESSORS + int operator()(JsonObject *object ) const; + int operator()(JsonArray *array ) const; + int operator()(bsl::string *string ) const; + int operator()(JsonNumber *number ) const; + int operator()(bool *boolean) const; + int operator()(JsonNull *null ) const; + // Return '-(type() + 1)'. The specified + // 'object/array/string/number/boolean/null' is ignored. +}; + + // ------------------------------ + // class TestJsonVisitorManipAvec + // ------------------------------ + +// CREATORS +TestJsonVisitorManipAvec::TestJsonVisitorManipAvec(bsl::vector *output, + bool isVerbose) +: d_output_p(output) +, d_isVerbose(isVerbose) +{ +} + +// ACCESSOR +int +TestJsonVisitorManipAvec::operator()(JsonObject *object) const +{ + BSLS_ASSERT(object); + + const int value = -(bdljsn::JsonType::e_OBJECT + 1); + + if (d_isVerbose) { P_(object) Q(OBJECT: ManipAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipAvec::operator()(JsonArray *array) const +{ + BSLS_ASSERT(array); + + const int value = -(bdljsn::JsonType::e_ARRAY + 1); + + if (d_isVerbose) { P_(array) Q(ARRAY: ManipAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipAvec::operator()(bsl::string *string) const +{ + BSLS_ASSERT(string); + + const int value = -(bdljsn::JsonType::e_STRING + 1); + + if (d_isVerbose) { P_(string) Q(STRING: ManipAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipAvec::operator()(JsonNumber *number) const +{ + BSLS_ASSERT(number); + + const int value = -(bdljsn::JsonType::e_NUMBER + 1); + + if (d_isVerbose) { P_(number) Q(NUMER: ManipAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipAvec::operator()(bool *boolean) const +{ + BSLS_ASSERT(boolean); + + const int value = -(bdljsn::JsonType::e_BOOLEAN + 1); + + if (d_isVerbose) { P_(boolean) Q(BOOLEAN: ManipAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipAvec::operator()(JsonNull *null) const +{ + BSLS_ASSERT(null); + + const int value = -(bdljsn::JsonType::e_NULL + 1); + + if (d_isVerbose) { P_(null) Q(NULL: ManipAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + + // ============================== + // class TestJsonVisitorAccesAvec + // ============================== + +class TestJsonVisitorAccesAvec +{ + // This class defines 'operator()' overloads for the *accessor* overloads + // of the 'Json' 'visitor' method. These 'operator()' overloads have the + // 'const'-qualification ("avec") that prevents modifying the 'visitor' + // argument. + + bsl::vector *d_output_p; + bool d_isVerbose; + + public: + // CREATORS + explicit TestJsonVisitorAccesAvec(bsl::vector *output = 0, + bool isVerbose = false) ; + // Create a 'TestJsonVistorManipAvec' object having all overloads + // required for the accessor overloads of 'Json::visit'. Optionally + // specify 'isVerbose' for the generation of trace statements to + // standard output. Optionally specify 'output', a vector to which the + // returned value is pushed. Note that 'output' is useful when the + // return type is 'void'. + + // ACCESSORS + int operator()(const JsonObject& object ) const; + int operator()(const JsonArray& array ) const; + int operator()(const bsl::string& string ) const; + int operator()(const JsonNumber& number ) const; + int operator()(const bool& boolean) const; + int operator()(const JsonNull& null ) const; + // Return 'type() + 1'. The specified + // 'object/array/string/number/boolean/null' is ignored. +}; + + // ------------------------------ + // class TestJsonVisitorAccesAvec + // ------------------------------ + +// CREATORS +TestJsonVisitorAccesAvec::TestJsonVisitorAccesAvec(bsl::vector *output, + bool isVerbose) +: d_output_p(output) +, d_isVerbose(isVerbose) +{ +} + +// ACCESSORS +int +TestJsonVisitorAccesAvec::operator()(const JsonObject& object) const +{ + (void) object; + + const int value = bdljsn::JsonType::e_OBJECT + 1; + + if (d_isVerbose) { Q(OBJECT: AcessAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesAvec::operator()(const JsonArray& array) const +{ + (void) array; + + const int value = bdljsn::JsonType::e_ARRAY + 1; + + if (d_isVerbose) { Q(ARRAY: AcessAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesAvec::operator()(const bsl::string& string) const +{ + (void) string; + + const int value = bdljsn::JsonType::e_STRING + 1; + + if (d_isVerbose) { Q(STRING: AcessAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesAvec::operator()(const JsonNumber& number) const +{ + (void) number; + + const int value = bdljsn::JsonType::e_NUMBER + 1; + + if (d_isVerbose) { Q(NUMBER: AcessAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesAvec::operator()(const bool& boolean) const +{ + (void) boolean; + + const int value = bdljsn::JsonType::e_BOOLEAN + 1; + + if (d_isVerbose) { Q(BOOLEAN: AcessAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesAvec::operator()(const JsonNull& null) const +{ + (void) null; + + const int value = bdljsn::JsonType::e_NULL + 1; + + if (d_isVerbose) { Q(NULL: AcessAvec); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + + // ============================== + // class TestJsonVisitorManipSans + // ============================== + +class TestJsonVisitorManipSans +{ + // This class defines 'operator()' overloads for the *manipulator* + // overloads of the 'Json' 'visitor' method. These 'operator()' overloads + // lack 'const'-qualification ("sans") that prevents modifying the + // 'visitor' argument. + + bsl::vector *d_output_p; + bool d_isVerbose; + + public: + // CREATORS + explicit TestJsonVisitorManipSans(bsl::vector *output = 0, + bool isVerbose = false); + // Create a 'TestJsonVistorManipSans' object having all overloads + // required for the manipulator overloads of 'Json::visit'. Optionally + // specify 'isVerbose' for the generation of trace statements to + // standard output. Optionally specify 'output', a vector to which the + // returned value is pushed. Note that 'output' is useful when the + // return type is 'void'. + + // ACCESSORS + int operator()(JsonObject *object ); + int operator()(JsonArray *array ); + int operator()(bsl::string *string ); + int operator()(JsonNumber *number ); + int operator()(bool *boolean); + int operator()(JsonNull *null ); + // Return '-(type() + 1)'. The specified + // 'object/array/string/number/boolean/null' is ignored. +}; + + // ------------------------------ + // class TestJsonVisitorManipSans + // ------------------------------ + +// CREATORS +TestJsonVisitorManipSans::TestJsonVisitorManipSans(bsl::vector *output, + bool isVerbose) +: d_output_p(output) +, d_isVerbose(isVerbose) +{ +} + +// ACCESSOR +int +TestJsonVisitorManipSans::operator()(JsonObject *object) +{ + BSLS_ASSERT(object); + + const int value = -(bdljsn::JsonType::e_OBJECT + 1); + + if (d_isVerbose) { P_(object) Q(OBJECT: ManipSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipSans::operator()(JsonArray *array) +{ + BSLS_ASSERT(array); + + const int value = -(bdljsn::JsonType::e_ARRAY + 1); + + if (d_isVerbose) { P_(array) Q(ARRAY: ManipSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipSans::operator()(bsl::string *string) +{ + BSLS_ASSERT(string); + + const int value = -(bdljsn::JsonType::e_STRING + 1); + + if (d_isVerbose) { P_(string) Q(STRING: ManipSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipSans::operator()(JsonNumber *number) +{ + BSLS_ASSERT(number); + + const int value = -(bdljsn::JsonType::e_NUMBER + 1); + + if (d_isVerbose) { P_(number) Q(NUMER: ManipSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipSans::operator()(bool *boolean) +{ + BSLS_ASSERT(boolean); + + const int value = -(bdljsn::JsonType::e_BOOLEAN + 1); + + if (d_isVerbose) { P_(boolean) Q(BOOLEAN: ManipSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorManipSans::operator()(JsonNull *null) +{ + BSLS_ASSERT(null); + + const int value = -(bdljsn::JsonType::e_NULL + 1); + + if (d_isVerbose) { P_(null) Q(NULL: ManipSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + + // ============================== + // class TestJsonVisitorAccesSans + // ============================== + +class TestJsonVisitorAccesSans +{ + // This class defines 'operator()' overloads for the *accessor* overloads + // of the 'Json' 'visitor' method. These 'operator()' overloads lack + // 'const'-qualification ("sans") that prevents modifying the 'visitor' + // argument. + + bsl::vector *d_output_p; + bool d_isVerbose; + + public: + // CREATORS + explicit TestJsonVisitorAccesSans(bsl::vector *output = 0, + bool isVerbose = false) ; + // Create a 'TestJsonVistorManipSans' object having all overloads + // required for the accessor overloads of 'Json::visit'. Optionally + // specify 'isVerbose' for the generation of trace statements to + // standard output. Optionally specify 'output', a vector to which the + // returned value is pushed. Note that 'output' is useful when the + // return type is 'void'. + + // ACCESSORS + int operator()(const JsonObject& object ); + int operator()(const JsonArray& array ); + int operator()(const bsl::string& string ); + int operator()(const JsonNumber& number ); + int operator()(const bool& boolean); + int operator()(const JsonNull& null ); + // Return 'type() + 1'. The specified + // 'object/array/string/number/boolean/null' is ignored. +}; + + // ------------------------------ + // class TestJsonVisitorAccesSans + // ------------------------------ + +// CREATORS +TestJsonVisitorAccesSans::TestJsonVisitorAccesSans(bsl::vector *output, + bool isVerbose) +: d_output_p(output) +, d_isVerbose(isVerbose) +{ +} + +// ACCESSORS +int +TestJsonVisitorAccesSans::operator()(const JsonObject& object) +{ + (void) object; + + const int value = bdljsn::JsonType::e_OBJECT + 1; + + if (d_isVerbose) { Q(OBJECT: AccesSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesSans::operator()(const JsonArray& array) +{ + (void) array; + + const int value = bdljsn::JsonType::e_ARRAY + 1; + + if (d_isVerbose) { Q(ARRAY: AccesSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesSans::operator()(const bsl::string& string) +{ + (void) string; + + const int value = bdljsn::JsonType::e_STRING + 1; + + if (d_isVerbose) { Q(STRING: AccesSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesSans::operator()(const JsonNumber& number) +{ + (void) number; + + const int value = bdljsn::JsonType::e_NUMBER + 1; + + if (d_isVerbose) { Q(NUMBER: AccesSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesSans::operator()(const bool& boolean) +{ + (void) boolean; + + const int value = bdljsn::JsonType::e_BOOLEAN + 1; + + if (d_isVerbose) { Q(BOOLEAN: AccesSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + +int +TestJsonVisitorAccesSans::operator()(const JsonNull& null) +{ + (void) null; + + const int value = bdljsn::JsonType::e_NULL + 1; + + if (d_isVerbose) { Q(NULL: AccesSans); } + if (d_output_p) { d_output_p->push_back(value); } + + return value; +} + + // ========================= + // class TestJsonVisitorNoop + // ========================= + +template +class TestJsonVisitorNoop +{ + public: + // ACCESSORS + template + RETURN_TYPE operator()( VALUE_TYPE *argument) const; + // Return 'RETURN_TYPE()'. The specified 'argument' is ignored. + + template + RETURN_TYPE operator()(const VALUE_TYPE& argument) const; + // Return 'RETURN_TYPE()'. The specified 'argument' is ignored. +}; + + // ------------------------- + // class TestJsonVisitorNoop + // ------------------------- + +template +template +RETURN_TYPE TestJsonVisitorNoop::operator()( + VALUE_TYPE *argument) const +{ + (void) argument; + return RETURN_TYPE(); +} + +template +template +RETURN_TYPE TestJsonVisitorNoop::operator()( + const VALUE_TYPE& argument) const +{ + (void) argument; + return RETURN_TYPE(); +} + + // ========================= + // class TestJsonVisitorDoop + // ========================= + +template +class TestJsonVisitorDoop : public TestJsonVisitorNoop +{ + public: + // CREATORS + //! TestJsonVisitorDoop(); + // Create a 'TestJsonVisitorDoop' object that defined all overloads + // required by both the accessor and manipulator overloads of + // 'Json::visit' where the two overloads: + //.. + // RETURN_TYPE operator()(JsonObject *object ) const; + // RETURN_TYPE operator()(const bsl::string& string ) const; + //.. + // have the same behavior as those of 'TestJsonVistor*' and all other + // overloads have the same behavior as those of 'TestJsonVisitorNoop'. + + // ACCESSORS + using TestJsonVisitorNoop::operator(); + + RETURN_TYPE operator()(JsonObject *object ) const; + // Return 'RETURN_TYPE(-(type() + 1))'. The specified 'object' is + // ignored. + + RETURN_TYPE operator()(const bsl::string& string ) const; + // Return 'RETURN_TYPE(type() + 1)'. The specified 'string' is + // ignored. +}; + + // ------------------------- + // class TestJsonVisitorDoop + // ------------------------- + +template +RETURN_TYPE +TestJsonVisitorDoop::operator()(JsonObject *object) const +{ + BSLS_ASSERT(object); + P_(object) Q(OBJECT); + return RETURN_TYPE(-1); +} + +template +RETURN_TYPE +TestJsonVisitorDoop::operator()(const bsl::string& string) const +{ + Q(STRING: const); (void) string; + return RETURN_TYPE(3); +} + + // ================================ + // class TestJsonVisitorSetStringOK + // ================================ + +class TestJsonVisitorSetStringOK: public TestJsonVisitorNoop { + + public: + // ACCESSORS + using TestJsonVisitorNoop::operator(); + + void operator()(bsl::string *string) const + // Test the specified 'string' to a valid UTF-8 sequence. + { + BSLS_ASSERT(string); + + string->assign("Hello world!!!!!"); + } +}; + + // ================================ + // class TestJsonVisitorSetStringNG + // ================================ + +class TestJsonVisitorSetStringNG: public TestJsonVisitorNoop { + + public: + // ACCESSORS + using TestJsonVisitorNoop::operator(); + + void operator()(bsl::string *string) const + // Set the specified 'string' to an invalid UTF-8 sequence copied from + // 'bdlde_utf8util.t.cpp'. + { + BSLS_ASSERT(string); + + string->assign("\xf0\x80\x80\x80"); + } +}; + + // =========================== + // class TestJsonVisitorSetGet + // =========================== + +class TestJsonVisitorSetGet +{ + public: + // ACCESSORS + void operator()(JsonObject *object ) const; + void operator()(JsonArray *array ) const; + void operator()(bsl::string *string ) const; + void operator()(JsonNumber *number ) const; + void operator()(bool *boolean) const; + void operator()(JsonNull *null ) const; + // Test whether or not the value of the specified + // 'object'/'array'/'string'/'number'/'boolean'/'null' is the default + // value for for the corresponding 'type()' of the 'Json' object and + // then set that value to the value obtained from + // 'JsonValueConstructorHelper'. + + void operator()(const JsonObject& object ) const; + void operator()(const JsonArray& array ) const; + void operator()(const bsl::string& string ) const; + void operator()(const JsonNumber& number ) const; + void operator()(const bool& boolean) const; + void operator()(const JsonNull& null ) const; + // Test whether or not the value of the specified + // 'object'/'array'/'string'/'number'/'boolean'/'null' has the same + // value as that obtained from 'JsonValueConstructorHelper' for the + // 'type()' of the 'Json' object. +}; + + // --------------------------- + // class TestJsonVisitorSetGet + // --------------------------- + +// ACCESSORS +void TestJsonVisitorSetGet::operator()(JsonObject *object) const +{ + BSLS_ASSERT(object); + ASSERTV(*object, JsonObject() == *object); + *object = JsonValueConstructorHelper::getValue(); +} + +void TestJsonVisitorSetGet::operator()(JsonArray *array) const +{ + BSLS_ASSERT(array); + ASSERTV(*array, JsonArray() == *array); + *array = JsonValueConstructorHelper::getValue(); +} + +void TestJsonVisitorSetGet::operator()(bsl::string *string) const +{ + BSLS_ASSERT(string); + ASSERTV(*string, bsl::string() == *string); + *string = JsonValueConstructorHelper::getValue(); +} + +void TestJsonVisitorSetGet::operator()(JsonNumber *number) const +{ + BSLS_ASSERT(number); + ASSERTV(*number, JsonNumber() == *number); + *number = JsonValueConstructorHelper::getValue(); +} + +void TestJsonVisitorSetGet::operator()(bool *boolean) const +{ + BSLS_ASSERT(boolean); + ASSERTV(*boolean, bool() == *boolean); + *boolean = JsonValueConstructorHelper::getValue(); +} + +void TestJsonVisitorSetGet::operator()(JsonNull *null) const +{ + BSLS_ASSERT(null); + ASSERTV(*null, JsonNull() == *null); + *null = JsonValueConstructorHelper::getValue(); +} + +void TestJsonVisitorSetGet::operator()(const JsonObject& object) const +{ + ASSERTV(object, JsonValueConstructorHelper::getValue(), + object == JsonValueConstructorHelper::getValue()); +} + +void TestJsonVisitorSetGet::operator()(const JsonArray& array) const +{ + ASSERTV(array, JsonValueConstructorHelper::getValue(), + array == JsonValueConstructorHelper::getValue()); +} + +void TestJsonVisitorSetGet::operator()(const bsl::string& string) const +{ + ASSERTV(string, JsonValueConstructorHelper::getValue(), + string == JsonValueConstructorHelper::getValue()); +} + +void TestJsonVisitorSetGet::operator()(const JsonNumber& number) const +{ + ASSERTV(number, JsonValueConstructorHelper::getValue(), + number == JsonValueConstructorHelper::getValue()); +} + +void TestJsonVisitorSetGet::operator()(const bool& boolean) const +{ + ASSERTV(boolean, JsonValueConstructorHelper::getValue(), + boolean == JsonValueConstructorHelper::getValue()); +} + +void TestJsonVisitorSetGet::operator()(const JsonNull& null) const +{ + ASSERTV(null, JsonValueConstructorHelper::getValue(), + null == JsonValueConstructorHelper::getValue()); +} + + // =================== + // struct TestTemplate + // =================== + +template +struct TestTemplate { + static void testCase45(bool isVerbose = false); + // Test 'visit' with a spectrum of 'return' types. Optionally specify + // 'isVerbose' to generate trace statements to standard output. +}; + + // ------------------- + // struct TestTemplate + // ------------------- + +template +void TestTemplate::testCase45(bool isVerbose) +{ + if (isVerbose) { + P(bsls::NameOf()); + } + + bsl::vector expected; const bsl::vector& Expected = expected; + + expected.push_back(-1); + expected.push_back(-2); + expected.push_back(-3); + expected.push_back(-4); + expected.push_back(-5); + expected.push_back(-6); + expected.push_back( 1); + expected.push_back( 2); + expected.push_back( 3); + expected.push_back( 4); + expected.push_back( 5); + expected.push_back( 6); + + ASSERTV(Expected.size(), 12 == Expected.size()); + +#define TEST(RETURN_TYPE, VISITOR) \ + { \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + mX.makeObject(); mX.visit(VISITOR); \ + mX.makeArray(); mX.visit(VISITOR); \ + mX.makeString(""); mX.visit(VISITOR); \ + mX.makeNumber(); mX.visit(VISITOR); \ + mX.makeBoolean(); mX.visit(VISITOR); \ + mX.makeNull(); mX.visit(VISITOR); \ + \ + mX.makeObject(); X.visit(VISITOR); \ + mX.makeArray(); X.visit(VISITOR); \ + mX.makeString(""); X.visit(VISITOR); \ + mX.makeNumber(); X.visit(VISITOR); \ + mX.makeBoolean(); X.visit(VISITOR); \ + mX.makeNull(); X.visit(VISITOR); \ + } + + bsl::vector output; + TestJsonVisitorReturns tjvReturns(&output); + + TEST(RETURN_TYPE, tjvReturns) // TEST + + ASSERTV(output.size(), 12 == output.size()); + ASSERTV(Expected == output); + + if (isVerbose) { + for (int i = 0 ; i < 12; ++i) { + P_(i) P_(Expected[i]); P(output[i]) + } + } + + output.resize(0); + ASSERTV(output.size(), 0 == output.size()); + + TEST(RETURN_TYPE, bslmf::MovableRefUtil::move(tjvReturns)); // TEST + + ASSERTV(output.size(), 12 == output.size()); + ASSERTV(Expected == output); + + if (isVerbose) { + for (int i = 0 ; i < 12; ++i) { + P_(i) P_(Expected[i]); P(output[i]) + } + } + + output.resize(0); + ASSERTV(output.size(), 0 == output.size()); +#undef TEST + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +#define TEST(VISITOR) \ + { \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + mX.makeObject(); mX.visit(VISITOR); \ + mX.makeArray(); mX.visit(VISITOR); \ + mX.makeString(""); mX.visit(VISITOR); \ + mX.makeNumber(); mX.visit(VISITOR); \ + mX.makeBoolean(); mX.visit(VISITOR); \ + mX.makeNull(); mX.visit(VISITOR); \ + \ + mX.makeObject(); X.visit(VISITOR); \ + mX.makeArray(); X.visit(VISITOR); \ + mX.makeString(""); X.visit(VISITOR); \ + mX.makeNumber(); X.visit(VISITOR); \ + mX.makeBoolean(); X.visit(VISITOR); \ + mX.makeNull(); X.visit(VISITOR); \ + } + + TestJsonVisitorReturns tjvReturnsDeduced(&output); + + TEST(tjvReturnsDeduced) // TEST + + ASSERTV(output.size(), 12 == output.size()); + ASSERTV(Expected == output); + + if (isVerbose) { + for (int i = 0 ; i < 12; ++i) { + P_(i) P_(Expected[i]); P(output[i]) + } + } + + output.resize(0); + ASSERTV(output.size(), 0 == output.size()); + + TEST(bslmf::MovableRefUtil::move(tjvReturnsDeduced)); // TEST + + ASSERTV(output.size(), 12 == output.size()); + ASSERTV(Expected == output); + + if (isVerbose) { + for (int i = 0 ; i < 12; ++i) { + P_(i) P_(Expected[i]); P(output[i]) + } + } + + output.resize(0); + ASSERTV(output.size(), 0 == output.size()); +#undef TEST +#endif // BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +} + +// ============================================================================ +// USAGE EXAMPLES +// ---------------------------------------------------------------------------- + +bdljsn::Json example1(bslma::Default::globalAllocator()); +bdljsn::Json example2(bslma::Default::globalAllocator()); + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY + +// BDE_VERIFY pragma: -FD01 'operator()' overloads do not have contracts + +namespace Example3 { +// +///Example 3: Using the 'visit' Method to Traverse a 'Json' Object +///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// The 'Json' class provides the (overloaded) 'visit' method that invokes a +// user-supplied "visitor" functor according to the current 'type()' of the +// 'Json' object. +// +// For example, suppose one needs to survey the structure of the 'Json' object +// created in {Example 1} (and again in {Example 2}) and, in doing so, compile +// a tally of each of each of the 'Json' sub-objects and their their types +// (i.e., object, array, string, ...). +// +// First, we define a compliant visitor class, 'TallyByTypeVisitor': +//.. + // ================== + // TallyByTypeVisitor + // ================== + + class TallyByTypeVisitor { + + int d_tally[6]; + + public: + // CREATORS + explicit TallyByTypeVisitor(); + // Create a 'TallyByTypeVisitor' object that when passed to the + // 'Json::visit' method will increment the specified 'tally' array + // according to 'type()' and visit subordinate 'Json' objects, if + // any. The behavior is undefined unless 'tally' has at least + // 6 elements. + + // ACCESSORS + void operator()(const JsonObject& object); + // Increment the element corresponding to object in the tally array + // supplied at construction and visit the value of each member of + // the specified 'object'. + + void operator()(const JsonArray& array); + // Increment the element corresponding to array in the tally array + // supplied at construction and visit each element of the specified + // 'array'. + + void operator()(const bsl::string& string ); + void operator()(const JsonNumber& number ); + void operator()(const bool& boolean); + void operator()(const JsonNull& null ); + // Increment the element corresponding to + // string/number/boolean/null in the tally array supplied at + // construction. Ignore the specified + // 'string'/'number'/'boolean'/'null'. + + const int *tally() const; + // Return the address of an array of 6 elements containing the + // tally by type. The array is ordered according to + // 'JsonType::Enum'. + }; +//.. +// Notice that we have no need to change the value of the examined 'Json' +// objects so we use a set of 'operator()' overloads compatible with the +// 'visit' accessor method. Accordingly, we careful below to use this visitor +// only with 'const'-qualified 'Json' objects +// +// Then, we define the constructor and the six 'operator()' overloads. In each +// overload by type we increment the appropriate element in the user-supplied +// array of integers. +//.. + // ------------------ + // TallyByTypeVisitor + // ------------------ + + // CREATORS + TallyByTypeVisitor::TallyByTypeVisitor() + { + d_tally[bdljsn::JsonType::e_OBJECT ] = + d_tally[bdljsn::JsonType::e_ARRAY ] = + d_tally[bdljsn::JsonType::e_STRING ] = + d_tally[bdljsn::JsonType::e_NUMBER ] = + d_tally[bdljsn::JsonType::e_BOOLEAN] = + d_tally[bdljsn::JsonType::e_NULL ] = 0; + } + + // ACCESSORS + void TallyByTypeVisitor::operator()(const bsl::string& string) + { + (void) string; + ++d_tally[bdljsn::JsonType::e_STRING]; + } + + void TallyByTypeVisitor::operator()(const JsonNumber& number) + { + (void) number; + ++d_tally[bdljsn::JsonType::e_NUMBER]; + } + + void TallyByTypeVisitor::operator()(const bool& boolean) + { + (void) boolean; + ++d_tally[bdljsn::JsonType::e_BOOLEAN]; + } + + void TallyByTypeVisitor::operator()(const JsonNull& null) + { + (void) null; + ++d_tally[bdljsn::JsonType::e_NULL]; + } +//.. +// Next, we define the visitor overload for array types. After incrementing +// the tally array we pass this same visitor object to the 'Json' object in the +// array so those objects are included in the tally. +//.. + void TallyByTypeVisitor::operator()(const JsonArray& array) + { + ++d_tally[bdljsn::JsonType::e_ARRAY]; + + typedef JsonArray::ConstIterator ConstItr; + + for (ConstItr cur = array.cbegin(), + end = array.cend(); + end != cur; ++cur) { + const Json constElement = *cur; + constElement.visit(*this); + } + } + + const int *TallyByTypeVisitor::tally() const + { + return d_tally; + } +//.. +// Notice that 'element' is 'const'-qualified so the accessor 'visit' method is +// invoked. +// +// Then, we implement the visitor overload for the object type. Examination +// of the object produces a sequence of name-value pairs where the 'second' +// part is a 'Json' object that we must visit. +//.. + void TallyByTypeVisitor::operator()(const JsonObject& object) + { + ++d_tally[bdljsn::JsonType::e_OBJECT]; + + typedef JsonObject::ConstIterator ConstItr; + + for (ConstItr cur = object.cbegin(), + end = object.cend(); + end != cur; ++cur) { + const bsl::pair member = *cur; + const Json& json = member.second; + json.visit(*this); + } + } +//.. +// Again, notice that this visitor is used as an argument to a +// 'const'-qualified 'Json' object. +// +// Finally, we make a survey of the 'Json' object created in {Example 1} (and +// duplicated in {Example 2}). From visual inspection of the source JSON +// document we expect 10 'Json' objects distributed thus: +// +//: o Object +//: 1 top-level of the document is an object +//: 2 '{"boolean": false }', interior object +//: +//: o Array +//: 1 '[2.76, true]' +//: +//: o String +//: 1 '"text"' +//: +//: o Number +//: 1 '2.76' +//: 2 '3.14' +//: +//: o Boolean +//: 1 'false', from the internal object +//: 2 'true', from the array +//: 3 'true', from top-level object +//: +//: o Null +//: 1 null +// +// Use of our visitor functor on 'example1' confirms these observations: +//.. + int main() + { + TallyByTypeVisitor visitor; + + const Json& constExample1 = example1; + + constExample1.visit(&visitor); + + const int *const tally = visitor.tally(); + + ASSERT(2 == tally[bdljsn::JsonType::e_OBJECT ]); + ASSERT(1 == tally[bdljsn::JsonType::e_ARRAY ]); + ASSERT(1 == tally[bdljsn::JsonType::e_STRING ]); + ASSERT(2 == tally[bdljsn::JsonType::e_NUMBER ]); + ASSERT(3 == tally[bdljsn::JsonType::e_BOOLEAN]); + ASSERT(1 == tally[bdljsn::JsonType::e_NULL ]); + + return 0; + } +//.. + +} // close namespace Example3 + +// BDE_VERIFY pragma: +FD01 'operator()' overloads do not have contracts + +#endif // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY + +// ============================================================================ +// MAIN PROGRAM +// ---------------------------------------------------------------------------- + +using namespace BloombergLP; + +typedef bsls::Types:: Int64 Int64; +typedef bsls::Types::Uint64 Uint64; + +int main(int argc, char *argv[]) +{ + using namespace BloombergLP; + + const int test = argc > 1 ? atoi(argv[1]) : 0; + + BSLA_MAYBE_UNUSED const bool verbose = argc > 2; + BSLA_MAYBE_UNUSED const bool veryVerbose = argc > 3; + BSLA_MAYBE_UNUSED const bool veryVeryVerbose = argc > 4; + BSLA_MAYBE_UNUSED const bool veryVeryVeryVerbose = argc > 5; + + bsl::cout << "TEST " << __FILE__ << " CASE " << test << bsl::endl; + + // CONCERN: `BSLS_REVIEW` failures should lead to test failures. + bsls::ReviewFailureHandlerGuard reviewGuard(&bsls::Review::failByAbort); + + // CONCERN: In no case does memory come from the global allocator. + + bslma::TestAllocator globalAllocator("global", veryVeryVeryVerbose); + bslma::Default::setGlobalAllocator(&globalAllocator); + + switch (test) { case 0: + case 47: { + // -------------------------------------------------------------------- + // TEST: 'visit' + // + // Concerns: + //: 1 Each 'visit' overload dispatches to the intended 'operator()' + //: overload of the supplied 'visitor'. + //: + //: 2 The accessor overloads of 'visit' allow the 'visitor' to access + //: the 'Json' value. The manipulator overloads of 'visit' allow the + //: 'visitor' to access *and* change the value of the 'Json' object. + //: + //: o Caveat: When 'isNull()' the value cannot be changed because the + //: null value is a singleton. + //: + //: 3 The return type of the 'visitor' can be 'void', can be any + //: primitive type, and can even be "exotic" types having "odd" + //: behaviors. + //: + //: 4 Each 'visit' overload behaves identically whether the 'visitor' + //: is supplied as an lvalue or an rvalue. + //: + //: 5 Each 'visit' overload behaves identically whether the return type + //: of the is specified explicitly or, for C++14 or later, that + //: return type is deduced. + //: + //: 6 The supplied visitor object can have overloads for both the + //: accessor and manipulator overloads of 'visit' without interfering + //: with each other. + //: + //: 7 The 'visitor' can be passed as a 'const' reference -- assuming + //: its overloads are also 'const' qualified -- or, for C++11 or + //: later, by address or by a non-'const' reference. + //: + //: 8 QoI: The expected behavior is observed when a + //: manipulator-compliant 'visitor' is passed to an accessor 'visit' + //: overload: All calls are dispatched to the 'operator()' that + //: accepts a 'bool' (since all pointers can be converted to a + //: 'bool'). + //: + //: 9 QoI: Asserted precondition violations are detected when enabled. + //: + //: o The 'visitor' overload having the signature + //: 'operator()(bsl::string *string)' must preserve the UTF-8 + //: correctness of the JSON string. + // + // Plan: + //: 1 Each test will be exercised in the following variations: + // + //: 1 Once in which the 'visitor' is passed as an lvalue and again + //: with the visitor passed as an rvalue. + //: + //: 2 Similarly, each test is repeated with a the return type + //: explicitly specified and (for C++14 and later) with that type + //: deduced. + //: + //: 2 Define four test classes: + //: o 'TestJsonVisitorManipAvec' + //: o 'TestJsonVisitorManipSans' + //: o 'TestJsonVisitorAccesAvec' + //: o 'TestJsonVisitorAccesSans' + //: + //: o Each test class return a distinctive value for each overload + //: and confirm that the intended overload was called for the + //: current 'type()' of the 'Json' object. + // + //: o The two test classes with 'Manip' in their name have overloads + //: that accept pointers to the 'Json' value whereas the the two + //: test classes with 'Acces' in their name have overloads that + //: accept 'const'-references to those values. + //: + //: o The two test classes with 'Avec' in their name have overloads + //: that are 'const' qualified whereas the two test classes that + //: have 'Sans' in their name have overloads that are not + //: 'const' qualified. + //: + //: 3 'BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE' is used to exercise + //: each 'visit' overload with a while assortment of 'return' types + //: -- including 'void'. Since 'void' disallows the use of a return + //: value to confirm correct dispatch, 'TestJsonVisitorReturns' + //: uses an output vector instead. Also, that test visitor has + //: overloads for both the manipulator and visitor overloads of + //: 'visit' thereby showing that the implementation always calls the + //: correct visitor overload. + //: + //: 4 Use the 'TestJsonVisitorSetGet' class to confirm that for each + //: 'Json' object type: + //: + //: 1 The manipulator 'visit' sees the expected value (the default) + //: and can change that value (to a value obtained from the + //: 'JsonValueConstructorHelper'. + //: + //: 2 The accessor 'visit' overload sees that changed value. + //: + //: 5 Confirm that 'TestJsonVisitorNoop', a helper class needed + //: for the negative test, works as expected. + //: + //: 6 QoI: Precondition violations are detected when enabled. + //: + //: 1 Use helper classes 'TestJsonVisitorSetStringOK' and + //: 'TestJsonVisitorSetStringNG' in concert wit the + //: 'BSLS_ASSERTTEST_*' macros. + // + // Testing: + // template RT visit(FORWARD_REF visitor); + // template RT visit(FORWARD_REF visitor) const; + // template decltype(auto) visit (FORWARD_REF visitor); + // template decltype(auto) visit (FORWARD_REF visitor) const; + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "TEST: 'visit'" << endl + << "=============" << endl; + + if (veryVerbose) cout << + "Test dispatch to correct overload per 'const'-ness combos." + << endl; + { +#define TEST(RT, vMANIP, vACCES) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + RT ret; \ + \ + mX.makeObject(); ret = mX.visit(vMANIP); ASSERTV(ret, -1 == ret); \ + mX.makeArray(); ret = mX.visit(vMANIP); ASSERTV(ret, -2 == ret); \ + mX.makeString(""); ret = mX.visit(vMANIP); ASSERTV(ret, -3 == ret); \ + mX.makeNumber(); ret = mX.visit(vMANIP); ASSERTV(ret, -4 == ret); \ + mX.makeBoolean(); ret = mX.visit(vMANIP); ASSERTV(ret, -5 == ret); \ + mX.makeNull(); ret = mX.visit(vMANIP); ASSERTV(ret, -6 == ret); \ + \ + mX.makeObject(); ret = X.visit(vACCES); ASSERTV(ret, 1 == ret); \ + mX.makeArray(); ret = X.visit(vACCES); ASSERTV(ret, 2 == ret); \ + mX.makeString(""); ret = X.visit(vACCES); ASSERTV(ret, 3 == ret); \ + mX.makeNumber(); ret = X.visit(vACCES); ASSERTV(ret, 4 == ret); \ + mX.makeBoolean(); ret = X.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeNull(); ret = X.visit(vACCES); ASSERTV(ret, 6 == ret); \ +} + TestJsonVisitorManipAvec tjvManipAvec; + TestJsonVisitorAccesAvec tjvAccesAvec; + + TEST(int, tjvManipAvec, + tjvAccesAvec); + TEST(int, bslmf::MovableRefUtil::move(tjvManipAvec), + bslmf::MovableRefUtil::move(tjvAccesAvec)); + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY + TestJsonVisitorManipSans tjvManipSans; + TestJsonVisitorAccesSans tjvAccesSans; + + TEST(int, tjvManipSans, + tjvAccesSans); + TEST(int, bslmf::MovableRefUtil::move( tjvManipSans), + bslmf::MovableRefUtil::move( tjvAccesSans)); + + TEST(int, tjvManipSans, + tjvAccesSans); + TEST(int, bslmf::MovableRefUtil::move(&tjvManipSans), + bslmf::MovableRefUtil::move(&tjvAccesSans)); +#endif +#undef TEST + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +#define TEST(RT, vMANIP, vACCES) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + RT ret; \ + \ + mX.makeObject(); ret = mX.visit(vMANIP); ASSERTV(ret, -1 == ret); \ + mX.makeArray(); ret = mX.visit(vMANIP); ASSERTV(ret, -2 == ret); \ + mX.makeString(""); ret = mX.visit(vMANIP); ASSERTV(ret, -3 == ret); \ + mX.makeNumber(); ret = mX.visit(vMANIP); ASSERTV(ret, -4 == ret); \ + mX.makeBoolean(); ret = mX.visit(vMANIP); ASSERTV(ret, -5 == ret); \ + mX.makeNull(); ret = mX.visit(vMANIP); ASSERTV(ret, -6 == ret); \ + \ + mX.makeObject(); ret = X.visit(vACCES); ASSERTV(ret, 1 == ret); \ + mX.makeArray(); ret = X.visit(vACCES); ASSERTV(ret, 2 == ret); \ + mX.makeString(""); ret = X.visit(vACCES); ASSERTV(ret, 3 == ret); \ + mX.makeNumber(); ret = X.visit(vACCES); ASSERTV(ret, 4 == ret); \ + mX.makeBoolean(); ret = X.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeNull(); ret = X.visit(vACCES); ASSERTV(ret, 6 == ret); \ +} + TestJsonVisitorManipAvec tjvManipAvecDeduce; + TestJsonVisitorAccesAvec tjvAccesAvecDeduce; + + TEST(int, tjvManipAvecDeduce, + tjvAccesAvecDeduce); + TEST(int, bslmf::MovableRefUtil::move(tjvManipAvecDeduce), + bslmf::MovableRefUtil::move(tjvAccesAvecDeduce)); +#undef TEST +#endif + } + + if (veryVerbose) cout << "Test assorted 'return' types." << endl; + { + BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE( + TestTemplate, + testCase45, + void, // Be sure 'void' is tested. + BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_ALL); + } + + if (veryVerbose) cout << "Test setting and getting values" << endl; + { +#define TEST(VISITOR) \ + { \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + mX.makeObject(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeArray(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeString(""); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeNumber(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeBoolean(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeNull(); mX.visit(VISITOR); X.visit(VISITOR); \ + } + TestJsonVisitorSetGet tjvSetGet; + + TEST( tjvSetGet ); + TEST(bslmf::MovableRefUtil::move(tjvSetGet)); +#undef TEST + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +#define TEST(VISITOR) \ + { \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + mX.makeObject(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeArray(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeString(""); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeNumber(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeBoolean(); mX.visit(VISITOR); X.visit(VISITOR); \ + mX.makeNull(); mX.visit(VISITOR); X.visit(VISITOR); \ + } \ + + TestJsonVisitorSetGet tjvSetGetDeduce; + TEST( tjvSetGetDeduce ); + TEST(bslmf::MovableRefUtil::move(tjvSetGetDeduce)); + +#undef TEST +#endif + } + + if (veryVerbose) cout << "Test mismatch scenario." << endl; + { + // The "mismatch" scenario -- using a visitor intended for a + // 'const' object on a non-'const' object is deemed undefined + // behavior but well understood and is here tested. Failure here + // would be indicative of a significant implementation change. + +#define TEST_MISMATCH(RT, vACCES) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; \ + \ + int ret; \ + \ + mX.makeObject(); ret = mX.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeArray(); ret = mX.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeString(""); ret = mX.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeNumber(); ret = mX.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeBoolean(); ret = mX.visit(vACCES); ASSERTV(ret, 5 == ret); \ + mX.makeNull(); ret = mX.visit(vACCES); ASSERTV(ret, 5 == ret); \ +} + TestJsonVisitorAccesAvec tjvAccesAvec; + + TEST_MISMATCH(int, tjvAccesAvec) ; + TEST_MISMATCH(int, bslmf::MovableRefUtil::move(tjvAccesAvec)); + } + + if (veryVerbose) cout << "Test '*Noop' helper class" << endl; + { +#define TEST(RT, VISITOR) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + RT ret; \ + \ + mX.makeObject(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeArray(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString(""); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNumber(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + \ + mX.makeObject(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeArray(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString(""); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNumber(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ +} + TestJsonVisitorNoop tjvNoop; + TEST(double, tjvNoop ); + TEST(double, bslmf::MovableRefUtil::move(tjvNoop)); + +#undef TEST + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +#define TEST(RT, VISITOR) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + RT ret; \ + \ + mX.makeObject(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeArray(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString("");ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNumber(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + \ + mX.makeObject(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeArray(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString("");ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNumber(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ +} + TestJsonVisitorNoop tjvNoopDeduce; + TEST(double, tjvNoopDeduce ); + TEST(double, bslmf::MovableRefUtil::move(tjvNoopDeduce)); + +#undef TEST +#endif + } + + if (veryVerbose) cout << "Test '*Doop' helper class" << endl; + { +#define TEST(RT, VISITOR) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + RT ret; \ + \ + mX.makeObject(); ret = mX.visit(VISITOR); ASSERTV(ret, -1 == ret); \ + /* ^^ */ \ + mX.makeArray(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString(""); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNumber(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + \ + mX.makeObject(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeArray(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString(""); ret = X.visit(VISITOR); ASSERTV(ret, 3 == ret); \ + /* ^ */ \ + mX.makeNumber(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ +} + TestJsonVisitorDoop tjvDoop; + + TEST(short, tjvDoop ); + TEST(short, bslmf::MovableRefUtil::move(tjvDoop)); +#undef TEST + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY +#define TEST(RT, VISITOR) \ +{ \ + typedef bdljsn::Json Obj; \ + Obj mX; const Obj& X = mX; \ + \ + RT ret; \ + \ + mX.makeObject(); ret = mX.visit(VISITOR); ASSERTV(ret, -1 == ret); \ + /* ^^ */ \ + mX.makeArray(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString(""); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNumber(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = mX.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + \ + mX.makeObject(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeArray(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeString(""); ret = X.visit(VISITOR); ASSERTV(ret, 3 == ret); \ + /* ^ */ \ + mX.makeNumber(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeBoolean(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ + mX.makeNull(); ret = X.visit(VISITOR); ASSERTV(ret, 0 == ret); \ +} + TestJsonVisitorDoop tjvDoopDeduce; + + TEST(short, tjvDoopDeduce ); + TEST(short, bslmf::MovableRefUtil::move(tjvDoopDeduce)); +#undef TEST +#endif + } + + if (veryVerbose) cout << "Negative Testing." << endl; + { + bsls::AssertTestHandlerGuard hG; + + typedef bdljsn::Json Obj; + Obj obj; + + obj.makeString(""); + + TestJsonVisitorSetStringOK tjvSetStringOK; + TestJsonVisitorSetStringNG tjvSetStringNG; + + ASSERT_PASS(obj.visit(tjvSetStringOK)); + ASSERT_FAIL(obj.visit(tjvSetStringNG)); + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP14_BASELINE_LIBRARY + TestJsonVisitorSetStringOK tjvSetStringOKdeduce; + TestJsonVisitorSetStringNG tjvSetStringNGdeduce; + + ASSERT_PASS(obj.visit(tjvSetStringOKdeduce)); + ASSERT_FAIL(obj.visit(tjvSetStringNGdeduce)); +#endif + } + } break; + case 46: { + // -------------------------------------------------------------------- + // TESTING CONSUMPTION OF JSON_INITIALIZER + // + // Concerns: + //: 1 Each constructor captures the (single) parameter and stores it + //: into the variant. + //: + // Plan: + //: 1 Construct a series of 'Json_Initializer' objects, and confirm + //: that the type and the value for each one is as expected. + // + // Testing: + // Json(const Json_Initializer, bslma::Allocator *); + // JsonArray::pushBack(Json_Initializer init); + // JsonObject::insert(string_view, Json_Initializer init); + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "TESTING CONSUMPTION OF JSON_INITIALIZER" << endl + << "=======================================" << endl; + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + { + Json j1 = { + "Hello, World!", + 42, + JsonArray { "some", "elements", "in", "an", "array" }, + JsonObject { {"key1", "value1"}, {"key2", 42 } } + }; + + ASSERT(j1.isArray()); + const JsonArray &j1Arr = j1.theArray(); + + ASSERT(4 == j1Arr.size()); + ASSERT(j1Arr[0].isString()); + ASSERT(j1Arr[1].isNumber()); + ASSERT(j1Arr[2].isArray()); + ASSERT(j1Arr[3].isObject()); + ASSERT(2 == j1Arr[3].theObject().size()); + + Json j2({ + "Hello, World!", + 42, + JsonArray { "some", "elements", "in", "an", "array" }, + JsonObject { {"key1", "value1"}, {"key2", 42 } } + }); + + ASSERT(j2.isArray()); + const JsonArray &j2Arr = j2.theArray(); + ASSERT(4 == j2Arr.size()); + ASSERT(j2Arr[0].isString()); + ASSERT(j2Arr[1].isNumber()); + ASSERT(j2Arr[2].isArray()); + ASSERT(j2Arr[3].isObject()); + ASSERT(2 == j2Arr[3].theObject().size()); + + } + + { + JsonObject jo ({{"play", "Waiting for Godot"}, + {"chars", { "Didi", "Gogo", "Pozzo", "Lucky"}}}); + + ASSERT(2 == jo.size()); + ASSERT(jo["play"].isString()); + ASSERT(jo["chars"].isArray()); + + jo.insert("KEY", 123); + ASSERT(3 == jo.size()); + ASSERT(jo["KEY"].isNumber()); + } + +#if defined(BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY) + // In general, there are several ways to initialize an object: + //.. + // Foo x(a, b, c); -- a sequence of parameters inside parameters + // Foo x{a, b, c}; -- a sequence of parameters inside braces + // Foo x = y; + // Foo x = {a, b, c}; + //.. + // + // 'Json'/'JsonObject'/'JsonArray' do not support all of these methods, + // but I have put them all here in the test driver for completenes. + // When they are not supported, then the tests are commented out, + // ideally with a comment about why they are not supported. + { + using namespace std::literals::string_view_literals; + + bsl::string s = "ABC"; + Json j0; + Json_Initializer ji("ABC"); + + { + Json j1 (2); + ASSERT(j1.isNumber()); + ASSERT(2 == j1); + + Json j2 (true); + ASSERT(j2.isBoolean()); + ASSERT(j2.theBoolean()); + + Json j3 (2.4); + ASSERT(j3.isNumber()); + ASSERT(2.4 == j3.theNumber().asDouble()); + + Json j4 ("abc"); + ASSERT(j4.isString()); + ASSERT("abc" == j4.theString()); + + Json j5 ("def"sv); + ASSERT(j5.isString()); + ASSERT("def" == j5.theString()); + + // Json j6 ( "abc", 1L, false ); // Json does not have a sequence ctor + // Json j7 ( "abc", {1UL}); // Json does not have a sequence ctor + // Json j8 ( "abc", Json {1UL}); // Json does not have a sequence ctor + + Json j9 (s); + ASSERT(j9.isString()); + ASSERT(s == j9.theString()); + } + + { + Json j1 {2}; + ASSERT(j1.isArray()); + ASSERT(1 == j1.theArray().size()); + ASSERT(j1.theArray()[0].isNumber()); + ASSERT(2 == j1.theArray()[0]); + + Json j2 {true}; + ASSERT(j2.isArray()); + ASSERT(1 == j2.theArray().size()); + ASSERT(j2.theArray()[0].isBoolean()); + ASSERT(j2.theArray()[0].theBoolean()); + + Json j3 {2.4}; + ASSERT(j3.isArray()); + ASSERT(1 == j3.theArray().size()); + ASSERT(j3.theArray()[0].isNumber()); + ASSERT(2.4 == j3.theArray()[0].asDouble()); + + Json j4 {"abc"}; + ASSERT(j4.isArray()); + ASSERT(1 == j4.theArray().size()); + ASSERT(j4.theArray()[0].isString()); + ASSERT("abc" == j4.theArray()[0].theString()); + + Json j5 {"def"sv}; + ASSERT(j5.isArray()); + ASSERT(1 == j5.theArray().size()); + ASSERT(j5.theArray()[0].isString()); + ASSERT("def" == j5.theArray()[0].theString()); + + Json j6 {"abc", 1L, false}; + ASSERT(j6.isArray()); + ASSERT(3 == j6.theArray().size()); + ASSERT(j6.theArray()[0].isString()); + ASSERT(j6.theArray()[1].isNumber()); + ASSERT(j6.theArray()[2].isBoolean()); + ASSERT("abc" == j6.theArray()[0].theString()); + ASSERT(1L == j6.theArray()[1]); + ASSERT(! j6.theArray()[2].theBoolean()); + + Json j7 {"abc", {1UL}}; + ASSERT(j7.isArray()); + ASSERT(2 == j7.theArray().size()); + ASSERT(j7.theArray()[0].isString()); + ASSERT(j7.theArray()[1].isArray()); + ASSERT("abc" == j7.theArray()[0].theString()); + ASSERT(1UL == j7.theArray()[1].theArray()[0]); + + Json j8 {"abc", Json {1UL}}; + ASSERT(j8.isArray()); + ASSERT(2 == j8.theArray().size()); + ASSERT(j8.theArray()[0].isString()); + ASSERT(j8.theArray()[1].isArray()); + ASSERT("abc" == j8.theArray()[0].theString()); + ASSERT(1UL == j8.theArray()[1].theArray()[0]); + + Json j9 {s}; + ASSERT(j9.isArray()); + ASSERT(1 == j9.theArray().size()); + ASSERT(j9.theArray()[0].isString()); + ASSERT(s == j9.theArray()[0].theString()); + } + + { + // All of these constructors fail because the JSON constructors + // are 'explicit'. + // Json j1 = 2; + // Json j2 = true; + // Json j3 = 2.4; + // Json j4 = "abc"; + // Json j5 = "abc"sv; + // Json j6 = {"def", 1L, false }; + // Json j7 = {"abc", {1UL}}; + // Json j8 = {"abc", Json {1UL}}); + // Json j8 = s; + } + + { + // Nathan says we don't need to support this case. + // JsonArray j1 (2, 3.5, false, "ABC"sv); + + JsonArray j2 {2, 3.5, false, "ABC"sv}; + ASSERT(4 == j2.size()); + ASSERT(j2[0].isNumber()); + ASSERT(j2[1].isNumber()); + ASSERT(j2[2].isBoolean()); + ASSERT(j2[3].isString()); + ASSERT(2 == j2[0]); + ASSERT(3.5 == j2[1].asDouble()); + ASSERT(! j2[2].theBoolean()); + ASSERT("ABC" == j2[3].theString()); + + JsonArray j3 = {2, 3.5, false, "DEF"sv}; + ASSERT(4 == j3.size()); + ASSERT(j3[1].isNumber()); + ASSERT(j3[0].isNumber()); + ASSERT(j3[1].isNumber()); + ASSERT(j3[2].isBoolean()); + ASSERT(j3[3].isString()); + ASSERT(2 == j3[0]); + ASSERT(3.5 == j3[1].asDouble()); + ASSERT(! j3[2].theBoolean()); + ASSERT("DEF" == j3[3].theString()); + + JsonArray j4 ({2, 3.5, false, "XYZ"sv}); + ASSERT(4 == j4.size()); + ASSERT(j4[2].isBoolean()); + ASSERT(j4[0].isNumber()); + ASSERT(j4[1].isNumber()); + ASSERT(j4[2].isBoolean()); + ASSERT(j4[3].isString()); + ASSERT(2 == j4[0]); + ASSERT(3.5 == j4[1].asDouble()); + ASSERT(! j4[2].theBoolean()); + ASSERT("XYZ" == j4[3].theString()); + + JsonArray j5; + ASSERT(0 == j5.size()); + + // JsonArray j6 (false); + + JsonArray j7 {false}; + ASSERT(1 == j7.size()); + ASSERT(j7[0].isBoolean()); + ASSERT(!j7[0].theBoolean()); + + // This requires two conversions: 'Json_Initializer', then 'Json'. + // JsonArray j8 = false; + } + + { + // Nathan says we don't need to support this case. But we do anyway. + JsonObject j1 ("ABC"sv, 2.34); + ASSERT(1 == j1.size()); + ASSERT(j1.contains("ABC"sv)); + ASSERT(2.34 == j1["ABC"sv].asDouble()); + + JsonObject j2 {"DEF"sv, 2.34}; + ASSERT(1 == j2.size()); + ASSERT(j2.contains("DEF"sv)); + ASSERT(2.34 == j2["DEF"sv].asDouble()); + + JsonObject j3 = {"GHI"sv, 2.34}; + ASSERT(1 == j3.size()); + ASSERT(j3.contains("GHI"sv)); + ASSERT(2.34 == j3["GHI"sv].asDouble()); + + // Nathan says we don't need to support this case. But we do anyway. + JsonObject j4 ({"ABC"sv, 2.34}); + ASSERT(1 == j4.size()); + ASSERT(j4.contains("ABC"sv)); + ASSERT(2.34 == j4["ABC"sv].asDouble()); + + JsonObject j5 ({{"ABC"sv, 2.34}, {"DEF"sv, 5.67}}); + ASSERT(2 == j5.size()); + ASSERT(j5.contains("ABC"sv)); + ASSERT(2.34 == j5["ABC"sv].asDouble()); + ASSERT(j5.contains("DEF"sv)); + ASSERT(5.67 == j5["DEF"sv].asDouble()); + + JsonObject j6 = {{"PDQ"sv, 2.34}, {"XYZ"sv, 5.67}}; + ASSERT(2 == j6.size()); + ASSERT(j6.contains("PDQ"sv)); + ASSERT(2.34 == j6["PDQ"sv].asDouble()); + ASSERT(j6.contains("XYZ"sv)); + ASSERT(5.67 == j6["XYZ"sv].asDouble()); + } + + { + // Nathan says we don't need to support this case. + // Json j1 (2, 3.5, false, "ABC"sv); // would use the sequence ctor + + // Nathan says we don't need to support this case - but we do anyway. + Json j2 {2, 3.5, false, "ABC"sv}; + ASSERT(j2.isArray()); ASSERT(4 == j2.theArray().size()); + + // Nathan says we don't need to support this case - but we do anyway. + Json j3 = {2, 3.5, false, "ABC"sv}; + ASSERT(j3.isArray()); ASSERT(4 == j3.theArray().size()); + + // Nathan says we don't need to support this case - but we do anyway. + Json j4 ({2, 3.5, false, "ABC"sv}); + ASSERT(j4.isArray()); ASSERT(4 == j4.theArray().size()); + + Json j5 (JsonArray{2, 3.5, false, "ABC"sv}); + ASSERT(j5.isArray()); ASSERT(4 == j5.theArray().size()); + + Json j6 = JsonArray{2, 3.5, false, "ABC"sv}; + ASSERT(j6.isArray()); ASSERT(4 == j6.theArray().size()); + + Json j7 (JsonObject{"ABC"sv, 2.34}); + ASSERT(j7.isObject()); ASSERT(1 == j7.theObject().size()); + + Json j8 = JsonObject{"ABC"sv, 2.34}; + ASSERT(j8.isObject()); ASSERT(1 == j8.theObject().size()); + + // Json j9 ({ConvertibleTo{}}); // Ambiguous + // Json jA ({1, ConvertibleTo{}}); // two conversions + // Json jB[] = { ConvertibleTo{} }; // two conversions + } + + { + JsonArray jArr; + + Json j1 (j0); // this calls the copy ctor + Json j2 ({ {1L}, j0 }); + Json j3 ({ {j0}, 2.3 }); + Json j4 ({ {j0}, j0 }); + Json j5 ({ {{j0}}, {j0}}); + Json j6 (ConvertibleTo{}); + Json j7 (s); + Json j8 (bsl::string("DEF")); // uses Json(STRING_TYPE &&) + Json j9 (std::move(s)); // uses Json(STRING_TYPE &&) + Json jA (jArr); // uses Json(const JsonArray &) + Json jB (JsonArray{}); // uses Json( JsonArray &&) + Json jC (std::move(jArr)); // uses Json( JsonArray &&) + } + + { + JsonArray jArr; + + Json j1 {j0}; // this calls the copy ctor + Json j2 { Json {1L}, j0 }; + Json j3 { Json {j0}, 2.3 }; + Json j4 { Json {j0}, j0 }; + Json j5 { JsonArray { Json {j0}}, Json {j0}}; + Json j6 {ConvertibleTo{}}; + Json j7 {s}; + Json j8 {bsl::string("DEF")}; // uses Json(STRING_TYPE &&) + Json j9 {std::move(s)}; // uses Json(STRING_TYPE &&) + Json jA {jArr}; // uses Json(const JsonArray &) + Json jB {JsonArray{}}; // uses Json( JsonArray &&) + Json jC {std::move(jArr)}; // uses Json( JsonArray &&) + } + + { + JsonArray jArr; + + Json j1 = j0; // this calls the copy ctor + Json j2 = Json { Json {1L}, j0 }; + Json j3 = Json { Json {j0}, 2.3 }; + Json j4 = Json { Json {j0}, j0 }; + Json j5 = Json { JsonArray { Json {j0}}, Json {j0}}; + // Json j6 = ConvertibleTo{}; // Two Conversions: int, then Json + // Json j7 = s; // Two Conversions: string_view, then Json + Json j8 = bsl::string("DEF"); // uses Json(STRING_TYPE &&) + Json j9 = std::move(s); // uses Json(STRING_TYPE &&) + Json jA = jArr; // uses Json(const JsonArray &) + Json jB = JsonArray{}; // uses Json( JsonArray &&) + Json jC = std::move(jArr); // uses Json( JsonArray &&) + } + } +#endif + + { + JsonArray j1; + + ASSERT(0 == j1.size()); + j1.pushBack("ABC"); + ASSERT(1 == j1.size()); + ASSERT(j1[0].isString()); + ASSERT("ABC" == j1[0].theString()); + j1.pushBack(JsonArray {1, false}); + ASSERT(2 == j1.size()); + } + +#endif + } break; + case 45: { + // -------------------------------------------------------------------- + // TESTING JSON_INITIALIZER + // + // Concerns: + //: 1 Each constructor captures the (single) parameter and stores it + //: into the variant. + //: + // Plan: + //: 1 Construct a series of 'Json_Initializer' objects, and confirm + //: that the type and the value for each one is as expected. + // + // Testing: + // Json_Initializer(); + // Json_Initializer(const JsonNull &); + // Json_Initializer(bool b); + // Json_Initializer(NUMBER num); + // Json_Initializer(float f); + // Json_Initializer(double d); + // Json_Initializer(const char *p); + // Json_Initializer(const bsl::string &s); + // Json_Initializer(const std::string &s); + // Json_Initializer(bsl::string_view sv); + // Json_Initializer(std::initializer_list il); + // Json_Initializer(const JsonObject &jObj); + // Json_Initializer(const JsonArray &jArr); + // Json_Initializer(const JsonNumber &jNum); + // Json_Initializer(const Json &json); + // const storage& get_storage() const; + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "TESTING JSON_INITIALIZER" << endl + << "========================" << endl; + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS + bsl::string abcString = "ABC"; + JsonNull jNull; + JsonObject jObj; + JsonArray jArr; + JsonNumber jNum; + Json json; + + Json_Initializer j0; + ASSERT(j0.get_storage().is()); + ASSERT(JsonNull() == j0.get_storage().the()); + + Json_Initializer j1(jNull); + ASSERT(j1.get_storage().is()); + ASSERT(JsonNull() == j1.get_storage().the()); + + Json_Initializer j2[] = { 2, 2L, 2LL }; + for (const auto& j: j2) { + ASSERT(j.get_storage().is()); + ASSERT(2 == j.get_storage().the()); + } + + Json_Initializer j3[] = { 3U, 3UL, 3ULL }; + for (const auto& j: j3) { + ASSERT(j.get_storage().is()); + ASSERT(3 == j.get_storage().the()); + } + + Json_Initializer j4(4.25f); + ASSERT(j4.get_storage().is()); + ASSERT(4.25 == j4.get_storage().the()); + + Json_Initializer j5(4.25); + ASSERT(j5.get_storage().is()); + ASSERT(4.25 == j5.get_storage().the()); + + Json_Initializer j6[] = { "ABC", abcString, bsl::string_view("ABC") }; + for (const auto& j: j6) { + ASSERT(j.get_storage().is()); + ASSERT("ABC" == j.get_storage().the()); + } + + Json_Initializer j7 = { false, "DEF", 42 }; + ASSERT(j7.get_storage().is>()); + + Json_Initializer j8(jObj); + ASSERT(j8.get_storage().is()); + ASSERT(&jObj == j8.get_storage().the()); + + Json_Initializer j9(jArr); + ASSERT(j9.get_storage().is()); + ASSERT(&jArr == j9.get_storage().the()); + + Json_Initializer j10(jNum); + ASSERT(j10.get_storage().is()); + ASSERT(&jNum == j10.get_storage().the()); + + Json_Initializer j11(json); + ASSERT(j11.get_storage().is()); + ASSERT(&json == j11.get_storage().the()); +#endif + } break; + case 44: { + // -------------------------------------------------------------------- + // PROMOTE-FROM-NULL MANIPULATORS + // The 'Json' class has 'pushBack' methods that will automatically + // "promote" a 'Json' object from the null type to the array type. + // + // Concerns: + //: 1 Invocation of any of the 22 'pushBack' overloads on a 'Json' + //: object changes the type of that object to array and completes the + //: operation. + //: + //: 2 The result of each 'Json::pushBack' method invocation is + //: identical to invoking the corresponding method on a 'JsonArray'. + //: + //: 3 QoI: Asserted precondition violations are detected when enabled. + //: Note that 'Json::pushBack(const char * )' has an extra defensive + //: check for the null pointer. + // + // Plan: + //: 1 Defined two function templates, 'testJsonPushBack' and + //: 'testJsonPushBackMoveRef', modeled on 'testArrayPushBack' and + //: 'testArrayPushBackMoveRef' that were used in TC 23 to test the + //: 'JsonArray::pushBack' overloads. The arguments to these + //: functions here match those appearing in TC 23. + //: + //: 1 The new functions have an addition tests to confirm the change + //: type of the initially null 'Json' to the array type. + //: + //: 2 The new functions also test the defensive checks on initial + //: 'Json' type (null or array). + //: + //: 3 Note that these functions also use the + //: 'JsonValueConstructorHelper' class template as a source of + //: input values for the 'pushBack' methods; however, since that + //: helper class does not allow 'Json' itself as a type, + //: specializations 'testJsonPushBack' and + //: 'testJsonPushBackMoveRef' are defined. + //: + //: 2 Perform a separate negative test for the null pointer check in + //: 'Json::pushBack(const char * )'. + // + // Testing: + // Json& Json::pushBack(const Json& json); + // Json& Json::pushBack(const JsonArray& array); + // Json& Json::pushBack(const JsonObject& object); + // Json& Json::pushBack(const JsonNumber& number); + // Json& Json::pushBack(bslmf::MovableRef json); + // Json& Json::pushBack(bslmf::MovableRef array); + // Json& Json::pushBack(bslmf::MovableRef object); + // Json& Json::pushBack(bslmf::MovableRef number); + // Json& Json::pushBack(const JsonNull& value); + // Json& Json::pushBack(bool value); + // Json& Json::pushBack(int value); + // Json& Json::pushBack(unsigned int value); + // Json& Json::pushBack(long value); + // Json& Json::pushBack(unsigned long value); + // Json& Json::pushBack(long long value); + // Json& Json::pushBack(unsigned long long value); + // Json& Json::pushBack(float value); + // Json& Json::pushBack(double value); + // Json& Json::pushBack(bdldfp::Decimal64 value); + // Json& Json::pushBack(const char *string); + // Json& Json::pushBack(const bsl::string_view& string); + // Json& Json::pushBack(bsl::string&& string); + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "PROMOTE-FROM-NULL MANIPULATORS" << endl + << "==============================" << endl; + + if (verbose) cout << "Test 'Json::pushBack' Overloads" << endl; + { + testJsonPushBack ('y', 2, verbose); + testJsonPushBackMoveRef ('z', verbose); + + testJsonPushBack ('a', 2, verbose); + testJsonPushBack ('b', 3, verbose); + testJsonPushBack ('c', 2, verbose); + + testJsonPushBackMoveRef ('d', verbose); + testJsonPushBackMoveRef ('e', verbose); + testJsonPushBackMoveRef ('f', verbose); + + testJsonPushBack ('g', 1, verbose); + + testJsonPushBack ('h', 1, verbose); + testJsonPushBack ('i', 1, verbose); + testJsonPushBack ('j', 1, verbose); + testJsonPushBack ('k', 1, verbose); + testJsonPushBack ('l', 1, verbose); + testJsonPushBack ('m', 1, verbose); + testJsonPushBack ('n', 1, verbose); + testJsonPushBack ('o', 1, verbose); + testJsonPushBack ('p', 1, verbose); + testJsonPushBack ('q', 1, verbose); + + testJsonPushBack ('r', 1, verbose); + testJsonPushBack ('s', 1, verbose); + testArrayPushBackMoveRef ('t', verbose); + } -using namespace BloombergLP; + if (verbose) cout << "Negative test: 'Json::pushBack(const char *)'" + << endl; + { + bsls::AssertTestHandlerGuard hG; -typedef bsls::Types::Int64 Int64; -typedef bsls::Types::Uint64 Uint64; + const char *const nullStr = 0; + const char *const notNullStr = "non-empty string literal"; -int main(int argc, char *argv[]) -{ - using namespace BloombergLP; + typedef bdljsn::Json Obj; - const int test = argc > 1 ? atoi(argv[1]) : 0; + Obj objExpectFail; + Obj objExpectPass; - BSLA_MAYBE_UNUSED const bool verbose = argc > 2; - BSLA_MAYBE_UNUSED const bool veryVerbose = argc > 3; - BSLA_MAYBE_UNUSED const bool veryVeryVerbose = argc > 4; - BSLA_MAYBE_UNUSED const bool veryVeryVeryVerbose = argc > 5; + ASSERT_FAIL(objExpectFail.pushBack( nullStr)); + ASSERT_PASS(objExpectPass.pushBack(notNullStr)); + } - bsl::cout << "TEST " << __FILE__ << " CASE " << test << bsl::endl; + } break; + case 43: { + // -------------------------------------------------------------------- + // CONVENIENCE OVERLOADS + // + // Concerns: + //: 1 Each 'pushBack' overload adds to 'JsonArray' an element having + //: the expected value and JSON type. + //: + //: 2 Each 'pushBack' overload that takes a 'bslmf::MovableRef' + //: argument dispatches to an overload that can perform a "move" + //: operation. + //: + //: 3 The types that accepted used in a 'Json' value constructor must + //: be equality comparable to a 'Json' object. There are 16 types in + //: that set. Note that 'bslmf::MovableRef' types appearing in the + //: value constructor are *not* included in this set as we don't use + //: such types in equality comparison. Also note that there are 4 + //: overloads for each of these types: equality/inequality, the value + //: type as 'lhs'/'rhs' -- thus 56 in total. + //: + //: 1 Does each of these overloads have the standard signature. + //: + //: 2 Does each of these overloads validly compare their arguments + //: for equality/inequality? + // + // Plan: + //: 1 Use the 'JsonValueConstructorHelper' class template -- used in + //: TC 28 (JSON VALUE CTOR) and elsewhere -- as a source of test + //: value/type pairs. Then: + //: o Pass the value to the 'pushBack' method of a 'JsonArray'. + //: o Confirm that an ('Json') element has been added to the array. + //: o Compare the type and value of the added element to those of the + //: 'pushBack' argument. + //: + //: 2 For overloads that take a 'bslmf::MovableRef', construct the + //: element to be added using the same 'bslma::TestAllocator' that + //: supplies memory to the array (thereby making the 'pushBack' + //: eligible for a move). Use the 'TestAllocator' features to + //: demonstrate that pushing does not trigger any allocation for + //: creating the added element. + //: + //: 3 Confirm the signature of the equality/inequality operators using + //: the function-address idiom as encapsulated in the + //: 'compileCheckEqualitySignature' function template. + //: + //: 4 Confirm that each of overloaded equality/inequality operators + //: returns the expected value for several combinations of known + //: input: the default value of the value type and the value returned + //: from 'JsonValueConstructorHelper::getValue()' for the value type. + //: + //: o Most of these tests are encapsulated in function templates + //: 'testEqualityOverloadsRef' and 'testEqualityOverloadsVal', + //: each having different initialization patterns. + //: + //: o 'testEqualityOverloadsStr' handles the special case of + //: setting the default value of a 'const char *' to '""'. + //: + //: o The tests of 'JsonNull' follow a significantly different + //: pattern than the others since it has no value that is not + //: the default and it compares unequal to anything but it self. + //: Tests for 'JsonNull' are integrated with the tests described + //: above where it is compared for inequality with a wide variety + //: of other 'Json' objects. + // + // Testing: + // JsonArray::pushBack(const JsonArray& array); + // JsonArray::pushBack(const JsonObject& object); + // JsonArray::pushBack(const JsonNumber& number); + // JsonArray::pushBack(bslmf::MovableRef array); + // JsonArray::pushBack(bslmf::MovableRef object); + // JsonArray::pushBack(bslmf::MovableRef number); + // JsonArray::pushBack(const JsonNull& value); + // JsonArray::pushBack(bool value); + // JsonArray::pushBack(int value); + // JsonArray::pushBack(unsigned int value); + // JsonArray::pushBack(long value); + // JsonArray::pushBack(unsigned long value); + // JsonArray::pushBack(long long value); + // JsonArray::pushBack(unsigned long long value); + // JsonArray::pushBack(float value); + // JsonArray::pushBack(double value); + // JsonArray::pushBack(bdldfp::Decimal64 value); + // JsonArray::pushBack(const char *string); + // JsonArray::pushBack(const bsl::string_view& string); + // JsonArray::pushBack(bsl::string&& string); + // operator==(const JsonArray& , const Json& ); + // operator==(const JsonObject& , const Json& ); + // operator==(const JsonNumber& , const Json& ); + // operator==(const JsonNull& , const Json& ); + // operator==(bool , const Json& ); + // operator==(int , const Json& ); + // operator==(unsigned int , const Json& ); + // operator==(bsls::Types::Int64 , const Json& ); + // operator==(bsls::Types::Uint64 , const Json& ); + // operator==(float , const Json& ); + // operator==(double , const Json& ); + // operator==(bdldfp::Decimal64 , const Json& ); + // operator==(const char *, const Json& ); + // operator==(const bsl::string_view& , const Json& ); + // operator==(const Json& , const JsonArray& ); + // operator==(const Json& , const JsonObject& ); + // operator==(const Json& , const JsonNumber& ); + // operator==(const Json& , const JsonNull& ); + // operator==(const Json& , bool ); + // operator==(const Json& , int ); + // operator==(const Json& , unsigned int ); + // operator==(const Json& , bsls::Types::Int64 ); + // operator==(const Json& , bsls::Types::Uint64 ); + // operator==(const Json& , float ); + // operator==(const Json& , double ); + // operator==(const Json& , bdldfp::Decimal64 ); + // operator==(const Json& , const char *); + // operator==(const Json& , const bsl::string_view& ); + // operator!=(const JsonArray& , const Json& ); + // operator!=(const JsonObject& , const Json& ); + // operator!=(const JsonNumber& , const Json& ); + // operator!=(const JsonNull& , const Json& ); + // operator!=(bool , const Json& ); + // operator!=(int , const Json& ); + // operator!=(unsigned int , const Json& ); + // operator!=(bsls::Types::Int64 , const Json& ); + // operator!=(bsls::Types::Uint64 , const Json& ); + // operator!=(float , const Json& ); + // operator!=(double , const Json& ); + // operator!=(bdldfp::Decimal64 , const Json& ); + // operator!=(const char *, const Json& ); + // operator!=(const bsl::string_view& , const Json& ); + // operator!=(const Json& , const JsonArray& ); + // operator!=(const Json& , const JsonObject& ); + // operator!=(const Json& , const JsonNumber& ); + // operator!=(const Json& , const JsonNull& ); + // operator!=(const Json& , bool ); + // operator!=(const Json& , int ); + // operator!=(const Json& , unsigned int ); + // operator!=(const Json& , bsls::Types::Int64 ); + // operator!=(const Json& , bsls::Types::Uint64 ); + // operator!=(const Json& , float ); + // operator!=(const Json& , double ); + // operator!=(const Json& , bdldfp::Decimal64 ); + // operator!=(const Json& , const char *); + // operator!=(const Json& , const bsl::string_view& ); + // -------------------------------------------------------------------- - // CONCERN: `BSLS_REVIEW` failures should lead to test failures. - bsls::ReviewFailureHandlerGuard reviewGuard(&bsls::Review::failByAbort); + if (verbose) cout << endl + << "CONVENIENCE OVERLOADS" << endl + << "=====================" << endl; - // CONCERN: In no case does memory come from the global allocator. + if (verbose) cout << "Test 'JsonArray::pushBack' Overloads" << endl; + { + testArrayPushBack ('a', 2, verbose); + testArrayPushBack ('b', 3, verbose); + testArrayPushBack ('c', 2, verbose); + + testArrayPushBackMoveRef ('d', verbose); + testArrayPushBackMoveRef ('e', verbose); + testArrayPushBackMoveRef ('f', verbose); + + testArrayPushBack ('g', 1, verbose); + + testArrayPushBack ('h', 1, verbose); + testArrayPushBack ('i', 1, verbose); + testArrayPushBack ('j', 1, verbose); + testArrayPushBack ('k', 1, verbose); + testArrayPushBack ('l', 1, verbose); + testArrayPushBack ('m', 1, verbose); + testArrayPushBack ('n', 1, verbose); + testArrayPushBack ('o', 1, verbose); + testArrayPushBack ('p', 1, verbose); + testArrayPushBack ('q', 1, verbose); + + testArrayPushBack ('r', 1, verbose); + testArrayPushBack ('s', 1, verbose); + + testArrayPushBackMoveRef ('t', verbose); + } - bslma::TestAllocator globalAllocator("global", veryVeryVeryVerbose); - bslma::Default::setGlobalAllocator(&globalAllocator); + if (verbose) cout << "\nAssign the address of each " + "'operator==' (and 'operator!=') to a variable" + << endl; + { + compileCheckEqualitySignature(); // 01 + compileCheckEqualitySignature(); // 02 + compileCheckEqualitySignature(); // 03 + compileCheckEqualitySignature(); // 04 + compileCheckEqualitySignature(); // 05 + compileCheckEqualitySignature(); // 06 + compileCheckEqualitySignature(); // 07 + compileCheckEqualitySignature(); // 08 + compileCheckEqualitySignature(); // 09 + compileCheckEqualitySignature(); // 10 + compileCheckEqualitySignature(); // 11 + compileCheckEqualitySignature(); // 12 + compileCheckEqualitySignature(); // 13 + compileCheckEqualitySignature(); // 14 + compileCheckEqualitySignature(); // 15 + compileCheckEqualitySignature(); // 16 + } - switch (test) { case 0: + if (verbose) cout << endl + << "Test 'Json::operator==' Overloads" << endl; + { + testEqualityOverloadsRef(); // 01 + testEqualityOverloadsRef(); // 02 + testEqualityOverloadsRef(); // 03 + + // 'JsonNull' tests distributed among the others in this series. + // 04 + + testEqualityOverloadsVal(); // 05 + testEqualityOverloadsVal(); // 06 + testEqualityOverloadsVal(); // 07 + testEqualityOverloadsVal(); // 08 + testEqualityOverloadsVal(); // 09 + testEqualityOverloadsVal(); // 10 + testEqualityOverloadsVal(); // 11 + testEqualityOverloadsVal(); // 12 + testEqualityOverloadsVal(); // 13 + testEqualityOverloadsVal(); // 14 + + testEqualityOverloadsStr (); // 'const char *' + // 16 + + testEqualityOverloadsRef(); // 16 + } + + } break; case 42: { // -------------------------------------------------------------------- // DRQS 171793948 @@ -3922,6 +7664,8 @@ int main(int argc, char *argv[]) ASSERTV(obj.size(), 4 == obj.size()); } +// BDE_VERIFY pragma: -IND01 + const std::string s0 = "s0"; std::string s1 = "s1"; const bsl::string s2 = "s2"; @@ -3929,6 +7673,8 @@ int main(int argc, char *argv[]) const bsl::string_view s4 = "s4"; bsl::string_view s5 = "s5"; +// BDE_VERIFY pragma: +IND01 + bdljsn::JsonObject obj; obj.insert(s0,s0); ASSERTV(obj.size(), 1== obj.size()); obj.clear(); @@ -4093,7 +7839,8 @@ int main(int argc, char *argv[]) #if defined(BSLS_COMPILERFEATURES_SUPPORT_USER_DEFINED_LITERALS) && \ defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) - bdljsn::Json example1, example2; + + if (veryVerbose) cout << "Example 1" << endl; { ///Usage @@ -4151,6 +7898,8 @@ int main(int argc, char *argv[]) } example1 = json; } + + if (veryVerbose) cout << "Example 2" << endl; { ///Example 2: More Efficiently Creating a `bdljsn::Json` ///----------------------------------------------------- @@ -4185,6 +7934,11 @@ int main(int argc, char *argv[]) example2 = json; } ASSERTV(example1, example2, example1 == example2); + + if (veryVerbose) cout << "Example 3" << endl; + { + Example3::main(); + } #endif } break; case 40: { @@ -4234,18 +7988,24 @@ int main(int argc, char *argv[]) // allocator. // // Testing: - // bool Json::isArray() const; + // bool Json::isArray() const; // bool Json::isBoolean() const; - // bool Json::isNull() const; - // bool Json::isNumber() const; - // bool Json::isObject() const; - // bool Json::isString() const; - // int Json::asInt(int *r) const; - // int Json::asInt64(Int64 *r) const; - // int Json::asUint(unsigned int *r) const; - // int Json::asUint64(Uint64 *r) const; - // float Json::asFloat() const; - // double Json::asDouble() const; + // bool Json::isNull() const; + // bool Json::isNumber() const; + // bool Json::isObject() const; + // bool Json::isString() const; + // int Json::asShort (short *r) const; + // int Json::asInt (int *r) const; + // int Json::asLong (long *r) const; + // int Json::asLonglong(long long *r) const; + // int Json::asInt64 (Int64 *r) const; + // int Json::asUshort (unsigned short *r) const; + // int Json::asUint (unsigned int *r) const; + // int Json::asUlong (unsigned long *r) const; + // int Json::asUlonglong(unsigned long long *r) const; + // int Json::asUint64 (Uint64 *r) const; + // float Json::asFloat() const; + // double Json::asDouble() const; // Decimal64 Json::asDecimal64() const; // int Json::asDecimal64Exact(Decimal64 *r) const; // const Json& Json::operator[](const string_view& key) const; @@ -4316,21 +8076,25 @@ int main(int argc, char *argv[]) JsonNumber(1, &xa), JsonNumber(0, &xa), JsonNumber(-1, &xa), + JsonNumber(bsl::numeric_limits::max(), &xa), JsonNumber(bsl::numeric_limits::max(), &xa), JsonNumber(bsl::numeric_limits::max(), &xa), JsonNumber(bsl::numeric_limits::max(), &xa), JsonNumber(bsl::numeric_limits::max(), &xa), + JsonNumber(bsl::numeric_limits::min(), &xa), JsonNumber(bsl::numeric_limits::min(), &xa), JsonNumber(bsl::numeric_limits::min(), &xa), JsonNumber(bsl::numeric_limits::max() + 1ll, &xa), + JsonNumber(bsl::numeric_limits::max() + 1ll, &xa), JsonNumber(bsl::numeric_limits::max() + 1ll, &xa), JsonNumber(bsl::numeric_limits::max() + 1ull, &xa), + JsonNumber(bsl::numeric_limits::min() - 1ll, &xa), JsonNumber(bsl::numeric_limits::min() - 1ll, &xa), JsonNumber( static_cast( @@ -4352,7 +8116,8 @@ int main(int argc, char *argv[]) for (long unsigned int i = 0; i < (sizeof(NUMBERS)/sizeof(*NUMBERS)); - ++i) { + ++i) + { const JsonNumber& N = NUMBERS[i]; const Obj X(N, &xa); @@ -4361,6 +8126,16 @@ int main(int argc, char *argv[]) int jsRC; int nRC; + if (veryVerbose) cout << + "int Json::asShort(short *r) const;\n"; + { + short jsValue; + short nValue; + jsRC = X.asShort(&jsValue); + nRC = N.asShort(&nValue); + ASSERTV(N, X, jsRC, nRC, jsRC == nRC); + ASSERTV(N, X, jsValue, nValue, jsValue == nValue); + } if (veryVerbose) cout << "int Json::asInt(int *r) const;\n"; { int jsValue; @@ -4370,6 +8145,25 @@ int main(int argc, char *argv[]) ASSERTV(N, X, jsRC, nRC, jsRC == nRC); ASSERTV(N, X, jsValue, nValue, jsValue == nValue); } + if (veryVerbose) cout << "int Json::asLong(int *r) const;\n"; + { + long jsValue; + long nValue; + jsRC = X.asLong(&jsValue); + nRC = N.asLong(&nValue); + ASSERTV(N, X, jsRC, nRC, jsRC == nRC); + ASSERTV(N, X, jsValue, nValue, jsValue == nValue); + } + if (veryVerbose) cout << + "int Json::asLonglong(int *r) const;\n"; + { + long long jsValue; + long long nValue; + jsRC = X.asLonglong(&jsValue); + nRC = N.asLonglong(&nValue); + ASSERTV(N, X, jsRC, nRC, jsRC == nRC); + ASSERTV(N, X, jsValue, nValue, jsValue == nValue); + } if (veryVerbose) cout << "int Json::asInt64(Int64 *r) const;\n"; { @@ -4380,6 +8174,35 @@ int main(int argc, char *argv[]) ASSERTV(N, X, jsRC, nRC, jsRC == nRC); ASSERTV(N, X, jsValue, nValue, jsValue == nValue); } + if (veryVerbose) cout << "int Json::asUlong(int *r) const;\n"; + { + unsigned long jsValue; + unsigned long nValue; + jsRC = X.asUlong(&jsValue); + nRC = N.asUlong(&nValue); + ASSERTV(N, X, jsRC, nRC, jsRC == nRC); + ASSERTV(N, X, jsValue, nValue, jsValue == nValue); + } + if (veryVerbose) cout << + "int Json::asUlonglong(int *r) const;\n"; + { + unsigned long long jsValue; + unsigned long long nValue; + jsRC = X.asUlonglong(&jsValue); + nRC = N.asUlonglong(&nValue); + ASSERTV(N, X, jsRC, nRC, jsRC == nRC); + ASSERTV(N, X, jsValue, nValue, jsValue == nValue); + } + if (veryVerbose) + cout << "int Json::asUshort(unsigned short *r) const;\n"; + { + unsigned short jsValue; + unsigned short nValue; + jsRC = X.asUshort(&jsValue); + nRC = N.asUshort(&nValue); + ASSERTV(N, X, jsRC, nRC, jsRC == nRC); + ASSERTV(N, X, jsValue, nValue, jsValue == nValue); + } if (veryVerbose) cout << "int Json::asUint(unsigned int *r) const;\n"; { @@ -4660,13 +8483,15 @@ int main(int argc, char *argv[]) // 12Verify that the default allocator did not allocate. // // Testing: - // Json& Json::operator=(float rhs); - // Json& Json::operator=(double rhs); - // Json& Json::operator=(bdldfp::Decimal64 rhs); - // Json& Json::operator=(int rhs); - // Json& Json::operator=(unsigned int rhs); - // Json& Json::operator=(bsls::Types::Int64 rhs); - // Json& Json::operator=(bsls::Types::Uint64 rhs); + // Json& Json::operator=(int rhs); + // Json& Json::operator=(unsigned int rhs); + // Json& Json::operator=(long rhs); + // Json& Json::operator=(unsigned long rhs); + // Json& Json::operator=(long long rhs); + // Json& Json::operator=(unsigned long long rhs); + // Json& Json::operator=(float rhs); + // Json& Json::operator=(double rhs); + // Json& Json::operator=(bdldfp::Decimal64 rhs); // Json& Json::operator=(const JsonNumber& rhs); // Json& Json::operator=(bslmf::MovableRef rhs); // Json& Json::operator=(const char *rhs); @@ -4802,15 +8627,51 @@ int main(int argc, char *argv[]) ASSERTV(sa.numBytesTotal(), 0 == sa.numBytesTotal()); ASSERTV(da.numBytesTotal(), 0 == da.numBytesTotal()); } - // Json& Json::operator=(bsls::Types::Int64 rhs); + // Json& Json::operator=(long rhs); { bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); bslma::TestAllocator xa("scratch", veryVeryVeryVerbose); Obj mX(&sa); const Obj& X = mX; - const bsls::Types::Int64 v = 1; - const JsonNumber expected(v, &xa); + const long v = 1L; + const JsonNumber expected(v, &xa); + + Json& result = (mX = v); + + ASSERTV(&X, &result, &X == &result); + ASSERTV(X.type(), JsonType::e_NUMBER == X.type()); + ASSERTV(X.theNumber(), expected == X.theNumber()); + ASSERTV(sa.numBytesTotal(), 0 == sa.numBytesTotal()); + ASSERTV(da.numBytesTotal(), 0 == da.numBytesTotal()); + } + // Json& Json::operator=(unsigned long rhs); + { + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + bslma::TestAllocator xa("scratch", veryVeryVeryVerbose); + + Obj mX(&sa); const Obj& X = mX; + + const unsigned long v = 1UL; + const JsonNumber expected(v, &xa); + + Json& result = (mX = v); + + ASSERTV(&X, &result, &X == &result); + ASSERTV(X.type(), JsonType::e_NUMBER == X.type()); + ASSERTV(X.theNumber(), expected == X.theNumber()); + ASSERTV(sa.numBytesTotal(), 0 == sa.numBytesTotal()); + ASSERTV(da.numBytesTotal(), 0 == da.numBytesTotal()); + } + // Json& Json::operator=(long long rhs); + { + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + bslma::TestAllocator xa("scratch", veryVeryVeryVerbose); + + Obj mX(&sa); const Obj& X = mX; + + const long long v = 1LL; + const JsonNumber expected(v, &xa); Json& result = (mX = v); @@ -4820,15 +8681,15 @@ int main(int argc, char *argv[]) ASSERTV(sa.numBytesTotal(), 0 == sa.numBytesTotal()); ASSERTV(da.numBytesTotal(), 0 == da.numBytesTotal()); } - // Json& Json::operator=(bsls::Types::Uint64 rhs); + // Json& Json::operator=(unsigned long long rhs); { bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); bslma::TestAllocator xa("scratch", veryVeryVeryVerbose); Obj mX(&sa); const Obj& X = mX; - const bsls::Types::Uint64 v = 1; - const JsonNumber expected(v, &xa); + const unsigned long long v = 1ULL; + const JsonNumber expected(v, &xa); Json& result = (mX = v); @@ -5091,7 +8952,11 @@ int main(int argc, char *argv[]) Obj mX(&sa); const Obj& X = mX; +#if BSLS_PLATFORM_CMP_IBM + const JsonNull v = { }; +#else const JsonNull v; +#endif const JsonNull expected(v); Json& result = (mX = v); @@ -5374,17 +9239,68 @@ int main(int argc, char *argv[]) } // Json& Json::operator[](const string_view& key); { - bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); - bsl::string_view key = "key"; + bslma::TestAllocator sa("supplied", true); + bslma::TestAllocatorMonitor sam(&sa); + bsl::string_view key = "key"; Obj mX(&sa); const Obj& X = mX; + mX.makeObject().insert(key, 1); + ASSERTV(sam.isTotalUp()); + ASSERTV(sam.numBlocksInUseChange(), + sam.numBlocksInUseChange() == 2); + + sam.reset(); - bslma::TestAllocatorMonitor sam(&sa); Json& result = mX[key]; const Json& expected = X.theObject()[key]; ASSERTV(&result == &expected); ASSERTV(sam.isTotalSame()); + + { + Obj mY(&sa); const Obj& Y = mY; + ASSERTV(Y.isNull()); + ASSERTV(sam.numBlocksInUseChange(), + sam.isTotalSame()); + + sam.reset(); + + Json& result = (mY[key] = 2) ; // TEST + + ASSERTV(Y.isObject()); + ASSERTV(sam.numBlocksInUseChange(), + sam.numBlocksInUseChange() == 2); + + const Json& expected = Y.theObject()[key]; + ASSERTV(&result == &expected); + ASSERTV(expected.type(), expected.isNumber()); + ASSERTV(expected, expected == 2); + + sa.setVerbose(false); + +#ifdef BDE_BUILD_TARGET_EXC + // Negative test + { + bsls::AssertTestHandlerGuard hG; + + Obj thingNull; thingNull .makeNull(); + Obj thingObject; thingObject .makeObject(); + + ASSERT_PASS(thingNull [KEY0] = 0); + ASSERT_PASS(thingObject[KEY1] = 1); + + Obj thingBoolean; thingBoolean.makeBoolean(); + Obj thingNumber; thingNumber .makeNumber(); + Obj thingString; thingString .makeString(bsl::string()); + Obj thingArray; thingArray .makeArray(); + + ASSERT_FAIL(thingBoolean[KEY0] = 0); + ASSERT_FAIL(thingNumber [KEY1] = 1); + ASSERT_FAIL(thingString [KEY2] = 2); + ASSERT_FAIL(thingArray [KEY3] = 3); + } +#endif + } } // Json& Json::operator[](size_t index); { @@ -5766,7 +9682,6 @@ int main(int argc, char *argv[]) X, (F == X) == (VALUE_CONFIG1 == VALUE_CONFIG2)); - BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(s2) { if (veryVeryVerbose) { T_ T_ Q(ExceptionTestBody) } @@ -5801,7 +9716,6 @@ int main(int argc, char *argv[]) F.allocator(), &s1 == F.allocator()); - #ifdef BDE_BUILD_TARGET_EXC if (!MEMDST2 && MEMSRC1) { ASSERTV(VALUE_CONFIG1, @@ -6120,7 +10034,6 @@ int main(int argc, char *argv[]) &mX, mR == &mX); - #ifdef BDE_BUILD_TARGET_EXC if (!MEM2 && MEM1) { ASSERTV(VALUE_CONFIG1, @@ -6798,7 +10711,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(VALUE_CONFIG, @@ -6860,7 +10773,7 @@ int main(int argc, char *argv[]) // No allocator. } else { - BSLS_ASSERT_OPT(0 == "JSON is no known type"); + BSLS_ASSERT_OPT(false && "JSON is no known type"); } ASSERTV(VALUE_CONFIG, CONFIG, &oa == X.allocator()); ASSERTV(VALUE_CONFIG, CONFIG, &scratch == Z.allocator()); @@ -7147,7 +11060,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(Z, objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config"); + BSLS_ASSERT_OPT(false && "Bad allocator config"); } break; } ASSERTV(VALUE_CONFIG, @@ -7196,7 +11109,7 @@ int main(int argc, char *argv[]) // No allocator. } else { - BSLS_ASSERT_OPT(0 == "JSON is no known type"); + BSLS_ASSERT_OPT(false && "JSON is no known type"); } // Also invoke the object's `allocator` accessor, as well @@ -7568,12 +11481,10 @@ int main(int argc, char *argv[]) scratchArray.pushBack(Json(scratchNumber)); scratchArray.pushBack(Json(scratchString)); - JsonObject scratchObject; scratchObject["number"] = Json(scratchNumber); scratchObject["string"] = Json(scratchString); - bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); bslma::TestAllocator da("default", veryVeryVeryVerbose); @@ -7750,24 +11661,26 @@ int main(int argc, char *argv[]) // 2. Testing with injected exceptions (TODO). // // Testing: - // Json(const JsonArray &array, *a); - // Json(bslmf::MovableRef array, *a); - // Json(bool boolean, *a); - // Json(const JsonNull& null, *a); - // Json(float number, *a); - // Json(double number, *a); - // Json(bdldfp::Decimal64 number, *a); - // Json(int number, *a); - // Json(unsigned int number, *a); - // Json(bsls::Types::Int64 number, *a); - // Json(bsls::Types::Uint64 number, *a); - // Json(const JsonNumber& number, *a); - // Json(bslmf::MovableRef number, *a); - // Json(const JsonObject& object, *a); + // Json(bool boolean, *a); + // Json(const JsonNull& null, *a); + // Json(const JsonArray& array, *a); + // Json(const JsonObject& object, *a); + // Json(const JsonNumber& number, *a); + // Json(bslmf::MovableRef array, *a); // Json(bslmf::MovableRef object, *a); - // Json(const char *string, *a); - // Json(const bsl::string_view& string, *a); - // Json(STRING&& string, *a); + // Json(bslmf::MovableRef number, *a); + // Json(int number, *a); + // Json(unsigned int number, *a); + // Json(long number, *a); + // Json(unsigned long number, *a); + // Json(long long number, *a); + // Json(unsigned long long number, *a); + // Json(float number, *a); + // Json(double number, *a); + // Json(bdldfp::Decimal64 number, *a); + // Json(const char *string, *a); + // Json(const bsl::string_view& string, *a); + // Json(STRING&& string, *a); // -------------------------------------------------------------------- typedef bdljsn::Json Obj; @@ -7776,7 +11689,7 @@ int main(int argc, char *argv[]) << "===============" << endl; for (int allocCfg = 0; allocCfg < 3; ++allocCfg) { - for (char cfg = 'a'; cfg <= 's'; ++cfg) { + for (char cfg = 'a'; cfg <= 'u'; ++cfg) { const int ALLOC_CONFIG = allocCfg; const char CONFIG = cfg; @@ -7805,7 +11718,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -7853,7 +11766,7 @@ int main(int argc, char *argv[]) &sa); ASSERTV(CONFIG, tam.isTotalSame()); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -7884,7 +11797,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -7918,7 +11831,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -7942,7 +11855,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -7976,7 +11889,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8010,7 +11923,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8044,7 +11957,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8078,7 +11991,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8112,7 +12025,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8146,7 +12059,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8180,8 +12093,11 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } + ASSERTV(ALLOC_CONFIG, + CONFIG, + 1 == objAllocatorPtr->numBlocksTotal()); ASSERTV(ALLOC_CONFIG, CONFIG, Helper::getType(), @@ -8238,7 +12154,7 @@ int main(int argc, char *argv[]) &sa); ASSERTV(CONFIG, tam.isTotalSame()); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8279,7 +12195,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8327,7 +12243,7 @@ int main(int argc, char *argv[]) &sa); ASSERTV(CONFIG, tam.isTotalSame()); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8358,7 +12274,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8389,7 +12305,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8420,7 +12336,7 @@ int main(int argc, char *argv[]) objAllocatorPtr = &sa; objPtr = new (fa) Obj(Helper::getValue(), &sa); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } ASSERTV(ALLOC_CONFIG, CONFIG, @@ -8467,8 +12383,76 @@ int main(int argc, char *argv[]) &sa); ASSERTV(CONFIG, tam.isTotalSame()); } else { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); + } + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::getType(), + objPtr->type(), + Helper::getType() == objPtr->type()); + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG), + Helper::getValue(), + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG) + == Helper::getValue()); + } break; + case 't': { // Json(long number, *a); + typedef int TheType; + typedef JsonValueConstructorHelper Helper; + if (0 == ALLOC_CONFIG) { + objAllocatorPtr = &da; + objPtr = new (fa) Obj(Helper::getValue()); + } else if (1 == ALLOC_CONFIG) { + objAllocatorPtr = &da; + objPtr = new (fa) Obj(Helper::getValue(), 0); + } else if (2 == ALLOC_CONFIG) { + objAllocatorPtr = &sa; + objPtr = new (fa) Obj(Helper::getValue(), &sa); + } else { + BSLS_ASSERT_OPT(false && "Bad allocator config."); + } + ASSERTV(ALLOC_CONFIG, + CONFIG, + 0 == objAllocatorPtr->numBlocksTotal()); + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::getType(), + objPtr->type(), + Helper::getType() == objPtr->type()); + ASSERTV(ALLOC_CONFIG, + CONFIG, + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG), + Helper::getValue(), + Helper::extractFromJson(*objPtr, + ALLOC_CONFIG, + CONFIG) + == Helper::getValue()); + } break; + case 'u': { // Json(unsigned long number, *a); + typedef unsigned int TheType; + typedef JsonValueConstructorHelper Helper; + if (0 == ALLOC_CONFIG) { + objAllocatorPtr = &da; + objPtr = new (fa) Obj(Helper::getValue()); + } else if (1 == ALLOC_CONFIG) { + objAllocatorPtr = &da; + objPtr = new (fa) Obj(Helper::getValue(), 0); + } else if (2 == ALLOC_CONFIG) { + objAllocatorPtr = &sa; + objPtr = new (fa) Obj(Helper::getValue(), &sa); + } else { + BSLS_ASSERT_OPT(false && "Bad allocator config."); } + ASSERTV(ALLOC_CONFIG, + CONFIG, + 0 == objAllocatorPtr->numBlocksTotal()); ASSERTV(ALLOC_CONFIG, CONFIG, Helper::getType(), @@ -8486,7 +12470,7 @@ int main(int argc, char *argv[]) == Helper::getValue()); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad constructor config."); + BSLS_ASSERT_OPT(false && "Bad constructor config."); } break; } @@ -8659,7 +12643,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } @@ -9018,13 +13002,14 @@ int main(int argc, char *argv[]) // // Testing: // JsonObject(initializer_list members, *a); + // JsonObject(initializer_list members, *a); // JsonObject::operator=(initializer_list members); // Iterator JsonObject::begin(); // Iterator JsonObject::end(); // pair JsonObject::insert(const Member& m); // pair JsonObject::insert(MovableRef m); - // void JsonObject::insert(INPUT_ITER first, INPUT_ITER last); - // void JsonObject::insert(initializer_list members); + // JsonObject& JsonObject::insert(INPUT_ITER first, INPUT_ITER last); + // JsonObject& JsonObject::insert(initializer_list members); // pair JsonObject::insert(string_view key, V&& v); // void JsonObject::clear(); // Iterator JsonObject::erase(Iterator position); @@ -9119,6 +13104,127 @@ int main(int argc, char *argv[]) ASSERTV(da.numBlocksTotal(), 0 == da.numBlocksTotal()); ASSERTV(sa.numBlocksTotal(), 0 < sa.numBlocksTotal()); } + + { + bslma::TestAllocator sa2("supplied2", veryVeryVeryVerbose); + + { // don't pass the allocator to the constructor + bslma::TestAllocator da2("default2", + veryVeryVeryVerbose); + bslma::DefaultAllocatorGuard dag(&da2); + + Obj j1 {{"ABC", 23}, + {"DEF", 2.4}, + {"GHI", JsonArray{2, 3.5, false, "ABC"}}}; + + ASSERT(3 == j1.size()); + ASSERT( j1.contains("ABC")); + ASSERT( j1.contains("DEF")); + ASSERT( j1.contains("GHI")); + ASSERT(!j1.contains("XYZ")); + + ASSERT(23 == j1["ABC"]); + ASSERT(2.4 == j1["DEF"].asDouble()); + ASSERT(j1["GHI"].isArray()); + ASSERT(4 == j1["GHI"].theArray().size()); + + ASSERTV(da2.numBlocksInUse(), 0 < da2.numBlocksInUse()); + ASSERTV(sa2.numBlocksTotal(), 0 == sa2.numBlocksTotal()); + } + + ASSERTV(sa2.numBlocksInUse(), 0 == sa2.numBlocksInUse()); + + { // pass the allocator to the constructor + bslma::TestAllocator da2("default2", + veryVeryVeryVerbose); + bslma::DefaultAllocatorGuard dag(&da2); + + Obj j1 ({{"ABC", 23}, + {"DEF", 2.4}, + {"GHI", JsonArray{2, 3.5, false, "ABC"}}}, + &sa2); + + ASSERT(3 == j1.size()); + ASSERT( j1.contains("ABC")); + ASSERT( j1.contains("DEF")); + ASSERT( j1.contains("GHI")); + ASSERT(!j1.contains("XYZ")); + + ASSERT(23 == j1["ABC"]); + ASSERT(2.4 == j1["DEF"].asDouble()); + ASSERT(j1["GHI"].isArray()); + ASSERT(4 == j1["GHI"].theArray().size()); + + // the ephemeral JSONArray in the initializer_list uses the + // default allocator, but then that is deallocated and the + // supplied allocator is used + ASSERTV(da2.numBlocksInUse(), 0 == da2.numBlocksInUse()); + ASSERTV(da2.numBlocksTotal(), 0 < da2.numBlocksTotal()); + ASSERTV(sa2.numBlocksTotal(), 0 < sa2.numBlocksTotal()); + } + + ASSERTV(sa2.numBlocksInUse(), 0 == sa2.numBlocksInUse()); + + { // use a naked set of braces rather than saying 'JsonArray' + bslma::TestAllocator da2("default2", + veryVeryVeryVerbose); + bslma::DefaultAllocatorGuard dag(&da2); + + Obj j1 {{"ABC", 23}, + {"DEF", 2.4}, + {"GHI", {2, 3.5, false, "ABC"}}}; + + ASSERT(3 == j1.size()); + ASSERT( j1.contains("ABC")); + ASSERT( j1.contains("DEF")); + ASSERT( j1.contains("GHI")); + ASSERT(!j1.contains("XYZ")); + + ASSERT(23 == j1["ABC"]); + ASSERT(2.4 == j1["DEF"].asDouble()); + ASSERT(j1["GHI"].isArray()); + ASSERT(4 == j1["GHI"].theArray().size()); + + ASSERTV(da2.numBlocksInUse(), 0 < da2.numBlocksInUse()); + ASSERTV(sa2.numBlocksInUse(), 0 == sa2.numBlocksInUse()); + } + + ASSERTV(sa2.numBlocksInUse(), 0 == sa2.numBlocksInUse()); + + { // use a naked set of braces rather than saying 'JsonArray' + bslma::TestAllocator da2("default2", + veryVeryVeryVerbose); + bslma::DefaultAllocatorGuard dag(&da2); + + Obj j1 ({{"ABC", 23}, + {"DEF", 2.4}, + {"GHI", {2, 3.5, false, "ABC"}}}, + &sa2); + + ASSERT(3 == j1.size()); + ASSERT( j1.contains("ABC")); + ASSERT( j1.contains("DEF")); + ASSERT( j1.contains("GHI")); + ASSERT(!j1.contains("XYZ")); + + ASSERT(23 == j1["ABC"]); + ASSERT(2.4 == j1["DEF"].asDouble()); + ASSERT(j1["GHI"].isArray()); + ASSERT(4 == j1["GHI"].theArray().size()); + + // the ephemeral JSONArray in the initializer_list uses the + // default allocator, but then that is deallocated and the + // supplied allocator is used + ASSERTV(da2.numBlocksInUse(), 0 == da2.numBlocksInUse()); + ASSERTV(da2.numBlocksTotal(), 0 < da2.numBlocksTotal()); + ASSERTV(sa2.numBlocksTotal(), 0 < sa2.numBlocksTotal()); + } + + ASSERTV(sa2.numBlocksInUse(), 0 == sa2.numBlocksInUse()); + // We didn't use the previous default allocator at all + ASSERTV(da.numBlocksTotal(), 0 == da.numBlocksTotal()); + } + #endif for (char cfg = 'a'; cfg <= 'e'; ++cfg) { @@ -9164,7 +13270,7 @@ int main(int argc, char *argv[]) mX.find(KEY)->second = GOOD; } break; default: { - BSLS_ASSERT_OPT(0 == "Bad test case config."); + BSLS_ASSERT_OPT(false && "Bad test case config."); } break; } ASSERTV(CONFIG, K, X, K == X); @@ -9206,7 +13312,7 @@ int main(int argc, char *argv[]) ASSERT(!r2); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad test case config."); + BSLS_ASSERT_OPT(false && "Bad test case config."); } break; } ASSERTV(CONFIG, K, X, K == X); @@ -9254,14 +13360,14 @@ int main(int argc, char *argv[]) } break; case 'l': { if (veryVerbose) cout << "void insert(Iter f, Iter l);\n"; - mX.insert(&GOOD_MEMBER, &GOOD_MEMBER + 1); + ASSERT(&mX == &mX.insert(&GOOD_MEMBER, &GOOD_MEMBER + 1)); } break; case 'm': { #ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS if (veryVerbose) cout << "void insert(initializer_list m);"; - mX.insert({{KEY, GOOD}}); + ASSERT(&mX == &mX.insert({{KEY, GOOD}})); #else if (veryVerbose) cout << "init list insertion elided.\n"; elided = true; @@ -9279,7 +13385,7 @@ int main(int argc, char *argv[]) ASSERT(false == r2.second); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad test case config."); + BSLS_ASSERT_OPT(false && "Bad test case config."); } break; } ASSERTV(CONFIG, K, X, elided, (K == X) || elided); @@ -9648,7 +13754,6 @@ int main(int argc, char *argv[]) ASSERTV(LINE1, LINE2, F, X, (F == X) == (LINE1 == LINE2)); - BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(s2) { if (veryVeryVerbose) { T_ T_ Q(ExceptionTestBody) } @@ -9663,7 +13768,6 @@ int main(int argc, char *argv[]) ASSERTV(LINE1, LINE2, &s1, F.allocator(), &s1 == F.allocator()); - #ifdef BDE_BUILD_TARGET_EXC if ('N' == MEMDST2 && 'Y' == MEMSRC1) { ASSERTV(LINE1, LINE2, 0 < EXCEPTION_COUNT); @@ -10645,7 +14749,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, 2*sizeof(Obj) == fa.numBytesInUse()); @@ -10964,7 +15068,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(Z, objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config"); + BSLS_ASSERT_OPT(false && "Bad allocator config"); } break; } ASSERTV(LINE, CONFIG, sizeof(Obj) == fa.numBytesInUse()); @@ -11908,7 +16012,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } @@ -12209,7 +16313,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } @@ -12604,7 +16708,7 @@ int main(int argc, char *argv[]) case 'c': { #ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS if (veryVerbose) cout << "assign(initializer_list l);\n"; - mX.assign({GOOD}); + ASSERT(&mX == &mX.assign({GOOD})); #else if (veryVerbose) cout << "init list assign member elided.\n"; continue; @@ -12613,7 +16717,7 @@ int main(int argc, char *argv[]) case 'd': { if (veryVerbose) cout << "assign(INPUT_ITERATOR first, INPUT_ITERATOR last);\n"; - mX.assign(&GOOD, &GOOD + 1); + ASSERT(&mX == &mX.assign(&GOOD, &GOOD + 1)); } break; case 'e': { if (veryVerbose) cout << "Iterator begin();\n"; @@ -12632,7 +16736,7 @@ int main(int argc, char *argv[]) mX.back() = GOOD; } break; default: { - BSLS_ASSERT_OPT(0 == "Bad test case config."); + BSLS_ASSERT_OPT(false && "Bad test case config."); } break; } ASSERTV(CONFIG, K, X, K == X); @@ -12673,7 +16777,7 @@ int main(int argc, char *argv[]) mX.popBack(); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad test case config."); + BSLS_ASSERT_OPT(false && "Bad test case config."); } break; } ASSERTV(CONFIG, K, X, K == X); @@ -12735,7 +16839,7 @@ int main(int argc, char *argv[]) ASSERT(r == mX.begin()); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad test case config."); + BSLS_ASSERT_OPT(false && "Bad test case config."); } break; } ASSERTV(CONFIG, K, X, K == X); @@ -13061,7 +17165,6 @@ int main(int argc, char *argv[]) ASSERTV(LINE1, LINE2, F, X, (F == X) == (LINE1 == LINE2)); - BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(s2) { if (veryVeryVerbose) { T_ T_ Q(ExceptionTestBody) } @@ -13076,7 +17179,6 @@ int main(int argc, char *argv[]) ASSERTV(LINE1, LINE2, &s1, F.allocator(), &s1 == F.allocator()); - #ifdef BDE_BUILD_TARGET_EXC if ('N' == MEMDST2 && 'Y' == MEMSRC1) { ASSERTV(LINE1, LINE2, 0 < EXCEPTION_COUNT); @@ -13374,7 +17476,6 @@ int main(int argc, char *argv[]) ASSERTV(LINE1, LINE2, Z, X, Z == X); ASSERTV(LINE1, LINE2, mR, &mX, mR == &mX); - #ifdef BDE_BUILD_TARGET_EXC if ('N' == MEMDST2 && 'Y' == MEMSRC1) { ASSERTV(LINE1, LINE2, 0 < EXCEPTION_COUNT); @@ -14021,7 +18122,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, 2*sizeof(Obj) == fa.numBytesInUse()); @@ -14339,7 +18440,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(Z, objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config"); + BSLS_ASSERT_OPT(false && "Bad allocator config"); } break; } ASSERTV(LINE, CONFIG, sizeof(Obj) == fa.numBytesInUse()); @@ -15342,7 +19443,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } @@ -15639,7 +19740,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } @@ -15655,7 +19756,7 @@ int main(int argc, char *argv[]) // Verify no allocation from the object/non-object allocators - ASSERTV(CONFIG, oa.numBlocksTotal(), 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, oa.numBlocksTotal(), 0 == oa.numBlocksTotal()); ASSERTV(CONFIG, noa.numBlocksTotal(), 0 == noa.numBlocksTotal()); // --------------------------------- @@ -15839,21 +19940,20 @@ int main(int argc, char *argv[]) bdljsn::JsonObject::Member("a", Json()), bdljsn::JsonObject::Member("b", Json(1))}; - object.insert(ARRAY, ARRAY + 2); - object.insert("bool", true); - object.insert("int", 1); - object.insert("double", 3.1415); + ASSERT(&object == &object.insert(ARRAY, ARRAY + 2)); + object.insert("bool", true); + object.insert("int", 1); + object.insert("double", 3.1415); object.insert("cstring", "JsonObject"); - object.insert("string", bsl::string("bsl::string")); - + object.insert("string", bsl::string("bsl::string")); - ASSERT(bdljsn::Json(true) == object["bool"]); - ASSERT(bdljsn::Json(1) == object["int"]); - ASSERT(bdljsn::Json(3.1415) == object["double"]); - ASSERT(bdljsn::Json("JsonObject") == object["cstring"]); + ASSERT(bdljsn::Json(true) == object["bool"]); + ASSERT(bdljsn::Json(1) == object["int"]); + ASSERT(bdljsn::Json(3.1415) == object["double"]); + ASSERT(bdljsn::Json("JsonObject") == object["cstring"]); ASSERT(bdljsn::Json("bsl::string") == object["string"]); - ASSERT(bdljsn::Json() == object["a"]); - ASSERT(bdljsn::Json(1) == object["b"]); + ASSERT(bdljsn::Json() == object["a"]); + ASSERT(bdljsn::Json(1) == object["b"]); if (veryVerbose) cout << endl diff --git a/groups/bdl/bdljsn/bdljsn_jsonnull.cpp b/groups/bdl/bdljsn/bdljsn_jsonnull.cpp index 3aeeddbc6a..928ad38454 100644 --- a/groups/bdl/bdljsn/bdljsn_jsonnull.cpp +++ b/groups/bdl/bdljsn/bdljsn_jsonnull.cpp @@ -26,6 +26,15 @@ bsl::ostream& JsonNull::print(bsl::ostream& stream, return stream; } +#ifndef BSLS_COMPILERFEATURES_SUPPORT_INLINE_VARIABLES + + // --------------- + // object jsonNull + // --------------- + +const JsonNull jsonNull = { }; +#endif + } // close package namespace } // close enterprise namespace diff --git a/groups/bdl/bdljsn/bdljsn_jsonnull.h b/groups/bdl/bdljsn/bdljsn_jsonnull.h index a85c6f274f..3ec19a2188 100644 --- a/groups/bdl/bdljsn/bdljsn_jsonnull.h +++ b/groups/bdl/bdljsn/bdljsn_jsonnull.h @@ -63,6 +63,8 @@ BSLS_IDENT("$Id: $") #include // 'hashAppend(HASH_ALG, int)' +#include + #include namespace BloombergLP { @@ -81,7 +83,7 @@ class JsonNull { /// Create a "null" JSON respresentation. The created object is equal /// to all other `JsonNull` objects. - JsonNull(); +//! JsonNull() = default; // ACCESSORS @@ -133,6 +135,17 @@ void hashAppend(HASHALG& hashAlgorithm, const JsonNull& object); /// operation is a no-op. void swap(JsonNull& a, JsonNull& b); + // =============== + // object jsonNull + // =============== + +#ifdef BSLS_COMPILERFEATURES_SUPPORT_INLINE_VARIABLES +inline constexpr JsonNull jsonNull = { }; +#else +extern const JsonNull jsonNull; +#endif + // An object having the (singular) value of the 'JsonNull' type. + // ============================================================================ // INLINE DEFINITIONS // ============================================================================ @@ -142,10 +155,6 @@ void swap(JsonNull& a, JsonNull& b); // -------------- // CREATORS -inline -JsonNull::JsonNull() -{ -} } // close package namespace diff --git a/groups/bdl/bdljsn/bdljsn_jsonnull.t.cpp b/groups/bdl/bdljsn/bdljsn_jsonnull.t.cpp index 4dca93c15a..8f4ea94266 100644 --- a/groups/bdl/bdljsn/bdljsn_jsonnull.t.cpp +++ b/groups/bdl/bdljsn/bdljsn_jsonnull.t.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include // `bsl::realloc`, `bsl::free` @@ -62,8 +63,9 @@ using namespace bsl; // [ 1] void swap(bdljsn::JsonNull& a, b); // [ 3] void hashAppend(HASHALG& hashAlgorithm, const JsonNull& object); // ---------------------------------------------------------------------------- -// [ 4] USAGE EXAMPLE +// [ 5] USAGE EXAMPLE // [ 2] CONCERN: All accessor methods are declared `const`. +// [ 4] CONCERN: 'bdljsn::jsonNull' exists and has the expected value. // ============================================================================ // STANDARD BDE ASSERT TEST FUNCTION @@ -188,7 +190,7 @@ int main(int argc, char *argv[]) bslma::Default::setGlobalAllocator(&globalAllocator); switch (test) { case 0: - case 4: { + case 5: { // -------------------------------------------------------------------- // USAGE EXAMPLE // Extracted from component header file. @@ -244,6 +246,40 @@ int main(int argc, char *argv[]) ASSERT(a == b); // ``` + } break; + case 4: { + // -------------------------------------------------------------------- + // TESTING 'bdljsn::jsonNull'' + // + // Concerns: + //: 1 The 'bdljsn::jsonNull' object exists at namespace scope, + //: has the intended value, and is 'const'. + // + // Plan: + //: 1 An ad hoc sequence of tests. + // + // Testing + // CONCERN: 'bdljsn::jsonNull' exists and has the expected value. + // -------------------------------------------------------------------- + + if (verbose) { + bsl::cout << bsl::endl + << "TESTING 'bdljsn::jsonNull'" << bsl::endl + << "==========================" << bsl::endl; + } + + ASSERTV(bdljsn::jsonNull == Obj()); // The object exists at + // namespace scope and has the + // intended value. + + bsl::ostringstream os; + + ASSERT((&os == &bdljsn::jsonNull.print(os, 0, 0))); + // 'print' is 'const'-qualified, thus the + // object too must be 'const'-qualified. + + ASSERTV(os.str(), "null\n" == os.str()); + } break; case 3: { // -------------------------------------------------------------------- @@ -416,7 +452,11 @@ int main(int argc, char *argv[]) if (veryVeryVerbose) { T_ T_ Q(EXPECTED) cout << EXP; } - const Obj X; // Same value on each iteration. +#if BSLS_PLATFORM_CMP_IBM + const Obj X = { }; // Same value on each iteration. +#else + const Obj X; // Same value on each iteration. +#endif for (int pass = 1; pass <= 2; ++pass) { diff --git a/groups/bdl/bdljsn/bdljsn_jsonnumber.h b/groups/bdl/bdljsn/bdljsn_jsonnumber.h index aef46c937c..4326e00834 100644 --- a/groups/bdl/bdljsn/bdljsn_jsonnumber.h +++ b/groups/bdl/bdljsn/bdljsn_jsonnumber.h @@ -245,13 +245,13 @@ BSLS_IDENT("$Id: $") // bsl::cout << "Integral: "; // ``` // If integral, we check if the value is a usable range. Let us assume that -// `bslsl::Type::Int64` is as large a number as we can accept. +// `long long` is as large a number as we can accept. // // Then, we convert the JSON number to that type and check for overflow and // underflow: // ``` -// bsls::Types::Int64 value; -// int rc = obj.asInt64(&value); +// long long value; +// int rc = obj.asLonglong(&value); // switch (rc) { // case 0: { // bsl::cout << value << " : OK to USE" << bsl::endl; @@ -431,9 +431,13 @@ class JsonNumber { const allocator_type& allocator = allocator_type()); explicit JsonNumber(unsigned int value, const allocator_type& allocator = allocator_type()); - explicit JsonNumber(bsls::Types::Int64 value, + explicit JsonNumber(long value, const allocator_type& allocator = allocator_type()); - explicit JsonNumber(bsls::Types::Uint64 value, + explicit JsonNumber(unsigned long value, + const allocator_type& allocator = allocator_type()); + explicit JsonNumber(long long value, + const allocator_type& allocator = allocator_type()); + explicit JsonNumber(unsigned long long value, const allocator_type& allocator = allocator_type()); /// Create a `JsonNumber` having the specified `value`. Optionally @@ -467,8 +471,8 @@ class JsonNumber { /// `original` becomes unspecified but valid, and no exceptions will be /// thrown; otherwise `original` is unchanged (and an exception may be /// thrown). - JsonNumber(bslmf::MovableRef original, - const allocator_type& allocator); + JsonNumber(bslmf::MovableRef original, + const allocator_type& allocator); //! ~JsonNumber() = default; // Destroy this object. @@ -491,8 +495,10 @@ class JsonNumber { /// non-`const` reference to this object. JsonNumber& operator=(int rhs); JsonNumber& operator=(unsigned int rhs); - JsonNumber& operator=(bsls::Types::Int64 rhs); - JsonNumber& operator=(bsls::Types::Uint64 rhs); + JsonNumber& operator=(long rhs); + JsonNumber& operator=(unsigned long rhs); + JsonNumber& operator=(long long rhs); + JsonNumber& operator=(unsigned long long rhs); /// Assign to this object the value of the specified `rhs`, and return a /// non-`const` reference to this object. The behavior is undefined if @@ -550,10 +556,16 @@ class JsonNumber { /// an error status value (unlike similar floating point conversions) /// because typically it is an error if a conversion to an integer results /// in an in-exact value. - int asInt(int *result) const; - int asInt64 (bsls::Types::Int64 *result) const; - int asUint (unsigned int *result) const; - int asUint64(bsls::Types::Uint64 *result) const; + int asShort (short *result) const; + int asInt (int *result) const; + int asLong (long *result) const; + int asLonglong (long long *result) const; + int asInt64 (bsls::Types::Int64 *result) const; + int asUshort (unsigned short *result) const; + int asUint (unsigned int *result) const; + int asUlong (unsigned long *result) const; + int asUlonglong(unsigned long long *result) const; + int asUint64 (bsls::Types::Uint64 *result) const; /// Return the closest floating point representation to this number. If /// this number is outside the representable range, return `+INF` or `-INF` @@ -703,18 +715,32 @@ inline JsonNumber::JsonNumber(int value, const allocator_type& allocator) : d_value(allocator) { - NumberUtil::stringify(&d_value, static_cast(value)); + NumberUtil::stringify(&d_value, static_cast(value)); } inline JsonNumber::JsonNumber(unsigned int value, const allocator_type& allocator) : d_value(allocator) { - NumberUtil::stringify(&d_value, static_cast(value)); + NumberUtil::stringify(&d_value, static_cast(value)); +} + +inline +JsonNumber::JsonNumber(long value, const allocator_type& allocator) +: d_value(allocator) +{ + NumberUtil::stringify(&d_value, static_cast(value)); +} + +inline +JsonNumber::JsonNumber(unsigned long value, const allocator_type& allocator) +: d_value(allocator) +{ + NumberUtil::stringify(&d_value, static_cast(value)); } inline -JsonNumber::JsonNumber(bsls::Types::Int64 value, +JsonNumber::JsonNumber(long long value, const allocator_type& allocator) : d_value(allocator) { @@ -722,7 +748,7 @@ JsonNumber::JsonNumber(bsls::Types::Int64 value, } inline -JsonNumber::JsonNumber(bsls::Types::Uint64 value, +JsonNumber::JsonNumber(unsigned long long value, const allocator_type& allocator) : d_value(allocator) { @@ -818,26 +844,40 @@ JsonNumber& JsonNumber::operator=(bslmf::MovableRef rhs) inline JsonNumber& JsonNumber::operator=(int rhs) { - NumberUtil::stringify(&d_value, static_cast(rhs)); + NumberUtil::stringify(&d_value, static_cast(rhs)); return *this; } inline JsonNumber& JsonNumber::operator=(unsigned int rhs) { - NumberUtil::stringify(&d_value, static_cast(rhs)); + NumberUtil::stringify(&d_value, static_cast(rhs)); return *this; } inline -JsonNumber& JsonNumber::operator=(bsls::Types::Int64 rhs) +JsonNumber& JsonNumber::operator=(long rhs) +{ + NumberUtil::stringify(&d_value, static_cast(rhs)); + return *this; +} + +inline +JsonNumber& JsonNumber::operator=(unsigned long rhs) +{ + NumberUtil::stringify(&d_value, static_cast(rhs)); + return *this; +} + +inline +JsonNumber& JsonNumber::operator=(long long rhs) { NumberUtil::stringify(&d_value, rhs); return *this; } inline -JsonNumber& JsonNumber::operator=(bsls::Types::Uint64 rhs) +JsonNumber& JsonNumber::operator=(unsigned long long rhs) { NumberUtil::stringify(&d_value, rhs); return *this; @@ -905,6 +945,12 @@ const bsl::string& JsonNumber::value() const // BDE_VERIFY pragma: push // BDE_VERIFY pragma: -FABC01 // not in alphabetic order +inline +int JsonNumber::asShort(short *result) const +{ + return NumberUtil::asShort(result, d_value); +} + inline int JsonNumber::asInt(int *result) const { @@ -912,11 +958,29 @@ int JsonNumber::asInt(int *result) const } inline -int JsonNumber::asInt64(bsls::Types::Int64 *result) const +int JsonNumber::asLong(long *result) const +{ + return NumberUtil::asLong(result, d_value); +} + +inline +int JsonNumber::asLonglong(long long *result) const +{ + return NumberUtil::asLonglong(result, d_value); +} + +inline +int JsonNumber::asInt64(bsls::Types::Int64 *result) const { return NumberUtil::asInt64(result, d_value); } +inline +int JsonNumber::asUshort(unsigned short *result) const +{ + return NumberUtil::asUshort(result, d_value); +} + inline int JsonNumber::asUint(unsigned int *result) const { @@ -924,7 +988,19 @@ int JsonNumber::asUint(unsigned int *result) const } inline -int JsonNumber::asUint64(bsls::Types::Uint64 *result) const +int JsonNumber::asUlong(unsigned long *result) const +{ + return NumberUtil::asUlong(result, d_value); +} + +inline +int JsonNumber::asUlonglong(unsigned long long *result) const +{ + return NumberUtil::asUint64(result, d_value); +} + +inline +int JsonNumber::asUint64(bsls::Types::Uint64 *result) const { return NumberUtil::asUint64(result, d_value); } diff --git a/groups/bdl/bdljsn/bdljsn_jsonnumber.t.cpp b/groups/bdl/bdljsn/bdljsn_jsonnumber.t.cpp index 16cdcf1546..74db52fc3a 100644 --- a/groups/bdl/bdljsn/bdljsn_jsonnumber.t.cpp +++ b/groups/bdl/bdljsn/bdljsn_jsonnumber.t.cpp @@ -1,6 +1,8 @@ // bdljsn_jsonnumber.t.cpp -*-C++-*- #include +#include + #include #include @@ -13,6 +15,7 @@ #include // `operator!=` #include #include +#include // 'operator!=' #include #include @@ -20,6 +23,7 @@ #include #include #include +#include // 'bsls::Types::Int64' #include #include // `bsl::size_t` @@ -96,13 +100,15 @@ using bsl::endl; // [ 2] JsonNumber(const bsl::string_view& text, *bA= 0); // [ 7] JsonNumber(MovableRef text); // [ 7] JsonNumber(MovableRef text, *bA = 0); -// [11] JsonNumber(int value, *bA =0); -// [11] JsonNumber(unsigned int value, *bA = 0); -// [11] JsonNumber(bsls::Types::Int64 value, *bA = 0); -// [11] JsonNumber(bsls::Types::Uint64 value, *bA = 0); -// [11] JsonNumber(float value, *bA = 0); -// [11] JsonNumber(double value, *bA = 0); -// [11] JsonNumber(bdldfp::Decimal64 value, *bA = 0); +// [11] JsonNumber(int value, *bA =0); +// [11] JsonNumber(unsigned int value, *bA = 0); +// [11] JsonNumber(long value, *bA = 0); +// [11] JsonNumber(unsigned long value, *bA = 0); +// [11] JsonNumber(long long value, *bA = 0); +// [11] JsonNumber(unsigned long long value, *bA = 0); +// [11] JsonNumber(float value, *bA = 0); +// [11] JsonNumber(double value, *bA = 0); +// [11] JsonNumber(bdldfp::Decimal64 value, *bA = 0); // [ 5] JsonNumber(const JsonNumber& original, *bA = 0); // [ 6] JsonNumber(MovableRef original); // [ 6] JsonNumber(MovableRef original, *bA); @@ -111,13 +117,15 @@ using bsl::endl; // MANIPULATORS // [ 9] JsonNumber& operator=(const JsonNumber& rhs); // [10] JsonNumber& operator=(MovableRef rhs); -// [11] JsonNumber& operator=(int rhs); -// [11] JsonNumber& operator=(unsigned int rhs); -// [11] JsonNumber& operator=(bsls::Types::Int64 rhs); -// [11] JsonNumber& operator=(bsls::Types::Uint64 rhs); -// [11] JsonNumber& operator=(float rhs); -// [11] JsonNumber& operator=(double rhs); -// [11] JsonNumber& operator=(bdldfp::Decimal64 rhs); +// [11] JsonNumber& operator=(int rhs); +// [11] JsonNumber& operator=(unsigned int rhs); +// [11] JsonNumber& operator=(long rhs); +// [11] JsonNumber& operator=(unsigned long rhs); +// [11] JsonNumber& operator=(long long rhs); +// [11] JsonNumber& operator=(unsigned long long rhs); +// [11] JsonNumber& operator=(float rhs); +// [11] JsonNumber& operator=(double rhs); +// [11] JsonNumber& operator=(bdldfp::Decimal64 rhs); // // [ 8] void swap(JsonNumber& other); // @@ -126,10 +134,16 @@ using bsl::endl; // [13] bool isIntegral() const; // [ 2] const bsl::string& value() const; // -// [11] int asInt (int *result) const; -// [11] int asInt64 (bsls::Types::Int64 *result) const; -// [11] int asUint (unsigned int *result) const; -// [11] int asUint64(bsls::Types::Uint64 *result) const; +// [11] int asShort (short *result) const; +// [11] int asInt (int *result) const; +// [11] int asLong (long *result) const; +// [11] int asLonglong (long long *result) const; +// [11] int asInt64 (Int64 *result) const; +// [11] int asUint (unsigned int *result) const; +// [11] int asUshort (unsigned short *result) const; +// [11] int asUlong (unsigned long *result) const; +// [11] int asUlonglong(unsigned long long *result) const; +// [11] int asUint64 (Uint64 *result) const; // [11] float asFloat() const; // [11] double asDouble() const; // [11] bdldfp::Decimal64 asDecimal64() const; @@ -155,7 +169,8 @@ using bsl::endl; // ---------------------------------------------------------------------------- // [ 1] BREATHING TEST // [17] USAGE EXAMPLE -// [ ] CONCERN: `BSLS_REVIEW` failures should lead to test failures +// [ ] CONCERN: Unexpected 'BSLS_REVIEW' failures should lead to test failures +// [ ] CONCERN: Only expected 'bsls' log messages occur. // ============================================================================ // STANDARD BDE ASSERT TEST FUNCTION @@ -227,9 +242,9 @@ void aSsErT(bool condition, const char *message, int line) // GLOBAL TYPEDEFS FOR TESTING // ---------------------------------------------------------------------------- -typedef bdljsn::JsonNumber Obj; -typedef bdljsn::NumberUtil NU; -typedef bdldfp::Decimal64 Deci64; +typedef bdljsn::JsonNumber Obj; +typedef bdljsn::NumberUtil NU; +typedef bdldfp::Decimal64 Deci64; typedef bsls::Types::Int64 Int64; typedef bsls::Types::Uint64 Uint64; @@ -240,7 +255,7 @@ typedef bsl::string_view SV; // TYPE TRAITS // ---------------------------------------------------------------------------- -BSLMF_ASSERT(bslmf::IsBitwiseMoveable::value); +BSLMF_ASSERT(bslmf::IsBitwiseMoveable ::value); BSLMF_ASSERT(bslma::UsesBslmaAllocator::value); // ============================================================================ @@ -285,6 +300,85 @@ const DefaultDataRow DEFAULT_DATA[] = }; enum { DEFAULT_NUM_DATA = sizeof DEFAULT_DATA / sizeof *DEFAULT_DATA }; +// ============================================================================ +// HELPERS FOR REVIEW & LOG MESSAGE HANDLING +// ---------------------------------------------------------------------------- + +static bool containsCaseless(const bsl::string_view& string, + const bsl::string_view& subString) + // Return 'true' if the specified 'subString' is present in the specified + // 'string' disregarding case of alphabet characters '[a-zA-Z]', otherwise + // return 'false'. +{ + if (subString.empty()) { + return true; // RETURN + } + + typedef bdlb::StringViewUtil SVU; + const bsl::string_view rsv = SVU::strstrCaseless(string, subString); + + return !rsv.empty(); +} + +// ============================================================================ +// EXPECTED 'BSLS_REVIEW' TEST HANDLERS +// ---------------------------------------------------------------------------- + +// These handlers are needed only temporarily until we determine how to fix the +// broken contract of 'bdlb::NumericParseUtil::parseDouble()' that says under- +// and overflow is not allowed yet the function supports it. + +bool isBdlbNumericParseUtilReview(const bsls::ReviewViolation& reviewViolation) + // Return 'true' if the specified 'reviewViolation' has been raised by the + // 'bdlb_numericparseutil' component or no source file names are supported + // by the build, otherwise return 'false'. +{ + const char *fn = reviewViolation.fileName(); + const bool fileOk = ('\0' == fn[0] // empty or has the component name + || containsCaseless(fn, "bdlb_numericparseutil")); + return fileOk; +} + +bool isUnderflowReview(const bsls::ReviewViolation& reviewViolation) + // Return 'true' if the specified 'reviewViolation' is an underflow message + // from the 'bdlb_numericparseutil' component (or no source file names are + // supported by the build), otherwise return 'false'. +{ + + return containsCaseless(reviewViolation.comment(), "underflow") + && isBdlbNumericParseUtilReview(reviewViolation); +} + +bool isOverflowReview(const bsls::ReviewViolation& reviewViolation) + // Return 'true' if the specified 'reviewViolation' is an overflow message + // from the 'bdlb_numericparseutil' component (or no source file names are + // supported by the build), otherwise return 'false'. +{ + + return containsCaseless(reviewViolation.comment(), "overflow") + && isBdlbNumericParseUtilReview(reviewViolation); +} + +void expectUnderflow(const bsls::ReviewViolation& reviewViolation) + // If the specified 'reviewViolation' is an expected underflow-related + // message from 'parseDouble' do nothing, otherwise call + // 'bsls::Review::failByAbort()'. +{ + if (!isUnderflowReview(reviewViolation)) { + bsls::Review::failByAbort(reviewViolation); + } +} + +void expectOverflow(const bsls::ReviewViolation& reviewViolation) + // If the specified 'reviewViolation' is an expected overflow-related + // message from 'parseDouble' do nothing, otherwise call + // 'bsls::Review::failByAbort()'. +{ + if (!isOverflowReview(reviewViolation)) { + bsls::Review::failByAbort(reviewViolation); + } +} + // ============================================================================ // CLASSES FOR TESTING // ---------------------------------------------------------------------------- @@ -449,7 +543,7 @@ void testCase11I(int argc, objPtr = new (fa) Obj(VALUE, objAllocatorPtr); // TEST } break; default: { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } @@ -480,7 +574,7 @@ void testCase11I(int argc, // Skip test } break; default : { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } ASSERTV(CONFIG, noa.numBlocksTotal(), @@ -715,7 +809,7 @@ void testCase11F(int argc, objPtr = new (fa) Obj(VALUE, objAllocatorPtr); // TEST } break; default: { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } @@ -746,7 +840,7 @@ void testCase11F(int argc, // Skip test } break; default : { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } ASSERTV(CONFIG, noa.numBlocksTotal(), @@ -1022,13 +1116,13 @@ int main(int argc, char *argv[]) bsl::cout << "Integral: "; // ``` // If integral, we check if the value is a usable range. Let us assume that -// `bslsl::Type::Int64` is as large a number as we can accept. +// `long long` is as large a number as we can accept. // // Then, we convert the JSON number to that type and check for overflow and // underflow: // ``` - bsls::Types::Int64 value; - int rc = obj.asInt64(&value); + long long value; + int rc = obj.asLonglong(&value); switch (rc) { case 0: { bsl::cout << value << " : OK to USE" << bsl::endl; @@ -1040,7 +1134,7 @@ int main(int argc, char *argv[]) bsl::cout << obj.value() << ": NG too small" << bsl::endl; } break; case bdljsn::JsonNumber::k_NOT_INTEGRAL: { - ASSERT(0 == "reached"); + ASSERT(false && "reached"); } break; } // ``` @@ -1061,7 +1155,7 @@ int main(int argc, char *argv[]) bsl::cout << value << ": inexact: USE approximation"; } break; case bdljsn::JsonNumber::k_NOT_INTEGRAL: { - ASSERT(0 == "reached"); + ASSERT(false && "reached"); } break; } @@ -1376,7 +1470,7 @@ int main(int argc, char *argv[]) Deci64(0) == result); } break; default: { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } @@ -1921,24 +2015,34 @@ int main(int argc, char *argv[]) // established in P-1 and P-2. // // Testing: - // JsonNumber(int value, *bA =0); - // JsonNumber(unsigned int value, *bA = 0); - // JsonNumber(bsls::Types::Int64 value, *bA = 0); - // JsonNumber(bsls::Types::Uint64 value, *bA = 0); - // JsonNumber(float value, *bA = 0); - // JsonNumber(double value, *bA = 0); - // JsonNumber(bdldfp::Decimal64 value, *bA = 0); - // JsonNumber& operator=(int rhs); - // JsonNumber& operator=(unsigned int rhs); - // JsonNumber& operator=(bsls::Types::Int64 rhs); - // JsonNumber& operator=(bsls::Types::Uint64 rhs); - // JsonNumber& operator=(float rhs); - // JsonNumber& operator=(double rhs); - // JsonNumber& operator=(bdldfp::Decimal64 rhs); - // int asInt (int *result) const; - // int asInt64 (bsls::Types::Int64 *result) const; - // int asUint (unsigned int *result) const; - // int asUint64(bsls::Types::Uint64 *result) const; + // JsonNumber(int value, *bA =0); + // JsonNumber(unsigned int value, *bA = 0); + // JsonNumber(long value, *bA = 0); + // JsonNumber(unsigned long value, *bA = 0); + // JsonNumber(long long value, *bA = 0); + // JsonNumber(unsigned long long value, *bA = 0); + // JsonNumber(float value, *bA = 0); + // JsonNumber(double value, *bA = 0); + // JsonNumber(bdldfp::Decimal64 value, *bA = 0); + // JsonNumber& operator=(int rhs); + // JsonNumber& operator=(unsigned int rhs); + // JsonNumber& operator=(long rhs); + // JsonNumber& operator=(unsigned long rhs); + // JsonNumber& operator=(long long rhs); + // JsonNumber& operator=(unsigned long long rhs); + // JsonNumber& operator=(float rhs); + // JsonNumber& operator=(double rhs); + // JsonNumber& operator=(bdldfp::Decimal64 rhs); + // int asShort (short *result) const; + // int asInt (int *result) const; + // int asLong (long *result) const; + // int asLonglong (long long *result) const; + // int asInt64 (Int64 *result) const; + // int asUshort (unsigned short *result) const; + // int asUint (unsigned int *result) const; + // int asUlong (unsigned long *result) const; + // int asUlonglong(unsigned long long *result) const; + // int asUint64 (Uint64 *result) const; // float asFloat() const; // double asDouble() const; // bdldfp::Decimal64 asDecimal64() const; @@ -1953,14 +2057,21 @@ int main(int argc, char *argv[]) << "=================================================" << endl; } - testCase11I< int>(argc, argv, &Obj::asInt); - testCase11I(argc, argv, &Obj::asInt64); - testCase11I< unsigned int>(argc, argv, &Obj::asUint); - testCase11I(argc, argv, &Obj::asUint64); + testCase11I< short>(argc, argv, &Obj::asShort); + testCase11I< int>(argc, argv, &Obj::asInt); + testCase11I< long>(argc, argv, &Obj::asLong); + testCase11I< long long>(argc, argv, &Obj::asLonglong); + testCase11I< Int64>(argc, argv, &Obj::asInt64); - testCase11F< float>(argc, argv, &Obj::asFloat); - testCase11F< double>(argc, argv, &Obj::asDouble); - testCase11F< bdldfp::Decimal64>(argc, argv, &Obj::asDecimal64); + testCase11I< unsigned short>(argc, argv, &Obj::asUshort); + testCase11I< unsigned int>(argc, argv, &Obj::asUint); + testCase11I< unsigned long>(argc, argv, &Obj::asUlong); + testCase11I(argc, argv, &Obj::asUlonglong); + testCase11I< Uint64>(argc, argv, &Obj::asUint64); + + testCase11F< float>(argc, argv, &Obj::asFloat); + testCase11F< double>(argc, argv, &Obj::asDouble); + testCase11F< bdldfp::Decimal64>(argc, argv, &Obj::asDecimal64); } break; case 10: { @@ -3166,7 +3277,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, 2*sizeof(Obj) == fa.numBytesInUse()); @@ -3494,7 +3605,7 @@ int main(int argc, char *argv[]) objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, 2*sizeof(Obj) == fa.numBytesInUse()); @@ -3792,7 +3903,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(Z, objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "Bad allocator config."); + BSLS_ASSERT_OPT(false && "Bad allocator config."); } break; } ASSERTV(LINE, CONFIG, sizeof(Obj) == fa.numBytesInUse()); @@ -4433,7 +4544,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } @@ -4458,7 +4569,14 @@ int main(int argc, char *argv[]) #endif ASSERTV(CONFIG, &oa, ALLOC_OF(X), &oa == X.get_allocator()); +#ifdef BSLS_PLATFORM_CMP_CLANG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif ASSERTV(CONFIG, &oa, ALLOC_OF(X), &oa == X.allocator()); +#ifdef BSLS_PLATFORM_CMP_CLANG +#pragma GCC diagnostic pop +#endif #ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC #ifdef BSLS_PLATFORM_CMP_CLANG @@ -4542,7 +4660,7 @@ int main(int argc, char *argv[]) objPtr = new (fa) Obj(SV(TEXT), objAllocatorPtr); } break; default: { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } @@ -4567,7 +4685,14 @@ int main(int argc, char *argv[]) #endif ASSERTV(CONFIG, &oa, ALLOC_OF(X), &oa == X.get_allocator()); +#ifdef BSLS_PLATFORM_CMP_CLANG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif ASSERTV(CONFIG, &oa, ALLOC_OF(X), &oa == X.allocator()); +#ifdef BSLS_PLATFORM_CMP_CLANG +#pragma GCC diagnostic pop +#endif #ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC #ifdef BSLS_PLATFORM_CMP_CLANG @@ -4585,7 +4710,7 @@ int main(int argc, char *argv[]) 0 == oa.numBlocksTotal()); } break; default : { - BSLS_ASSERT_OPT(0 == "reachable"); + BSLS_ASSERT_OPT(false && "reachable"); } break; } ASSERTV(CONFIG, noa.numBlocksTotal(), @@ -4644,15 +4769,15 @@ int main(int argc, char *argv[]) ASSERT( 0. == X.asDouble()); ASSERT(Deci64(0) == X.asDecimal64()); - int xInt; - unsigned xUint; - Int64 xInt64; - Uint64 xUint64; + int xInt; + unsigned xUint; + long long xInt64; + unsigned long long xUint64; ASSERT(0 == X.asInt(&xInt)); ASSERT(0 == X.asUint(&xUint)); - ASSERT(0 == X.asInt64(&xInt64)); - ASSERT(0 == X.asUint64(&xUint64)); + ASSERT(0 == X.asLonglong(&xInt64)); + ASSERT(0 == X.asUlonglong(&xUint64)); ASSERT(0 == xInt); ASSERT(0 == xUint); @@ -4669,15 +4794,15 @@ int main(int argc, char *argv[]) ASSERT( 0. == Y.asDouble()); ASSERT(Deci64(0) == Y.asDecimal64()); - int yInt; - unsigned yUint; - Int64 yInt64; - Uint64 yUint64; + int yInt; + unsigned yUint; + long long yInt64; + unsigned long long yUint64; ASSERT(0 == Y.asInt(&yInt)); ASSERT(0 == Y.asUint(&yUint)); - ASSERT(0 == Y.asInt64(&yInt64)); - ASSERT(0 == Y.asUint64(&yUint64)); + ASSERT(0 == Y.asLonglong(&yInt64)); + ASSERT(0 == Y.asUlonglong(&yUint64)); ASSERT(0 == yInt); ASSERT(0 == yUint); @@ -4689,15 +4814,15 @@ int main(int argc, char *argv[]) ASSERT( 0. == Z.asDouble()); ASSERT(Deci64(0) == Z.asDecimal64()); - int zInt; - unsigned zUint; - Int64 zInt64; - Uint64 zUint64; + int zInt; + unsigned zUint; + long long zInt64; + unsigned long long zUint64; ASSERT(0 == Z.asInt(&zInt)); ASSERT(0 == Z.asUint(&zUint)); - ASSERT(0 == Z.asInt64(&zInt64)); - ASSERT(0 == Z.asUint64(&zUint64)); + ASSERT(0 == Z.asLonglong(&zInt64)); + ASSERT(0 == Z.asUlonglong(&zUint64)); ASSERT(0 == zInt); ASSERT(0 == zUint); diff --git a/groups/bdl/bdljsn/bdljsn_jsontestsuiteutil.t.cpp b/groups/bdl/bdljsn/bdljsn_jsontestsuiteutil.t.cpp index ae895cfc37..b2da504c30 100644 --- a/groups/bdl/bdljsn/bdljsn_jsontestsuiteutil.t.cpp +++ b/groups/bdl/bdljsn/bdljsn_jsontestsuiteutil.t.cpp @@ -388,11 +388,11 @@ int main(int argc, char *argv[]) } break; case 'i': { ASSERTV(EXPECTED, Util::e_EITHER == EXPECTED); } break; - default: { ASSERTV(TEST_NAME[0], 0 == "expected value"); + default: { ASSERTV(TEST_NAME[0], false && "expected value"); } break; } } else { - ASSERTV(TEST_NAME, 0 == "well-formed `TEST_NAME`"); + ASSERTV(TEST_NAME, false && "well-formed 'TEST_NAME'"); } bsl::string PATH = testParsingDir + "/" + bsl::string(TEST_NAME); diff --git a/groups/bdl/bdljsn/bdljsn_numberutil.cpp b/groups/bdl/bdljsn/bdljsn_numberutil.cpp index cabcff2053..84080d72bc 100644 --- a/groups/bdl/bdljsn/bdljsn_numberutil.cpp +++ b/groups/bdl/bdljsn/bdljsn_numberutil.cpp @@ -7,11 +7,15 @@ BSLS_IDENT_RCSID(bdljsn_numberutil_cpp, "$Id$ $CSID$") #include #include + #include #include + #include #include + +#include #include #include @@ -26,18 +30,18 @@ namespace bdljsn { namespace { namespace u { -static const bsls::Types::Uint64 INT64_MAX_VALUE = +static const bsls::Types::Uint64 k_INT64_MAX_VALUE = bsl::numeric_limits::max(); -static const bsls::Types::Uint64 UINT64_MAX_VALUE = +static const bsls::Types::Uint64 k_UINT64_MAX_VALUE = bsl::numeric_limits::max(); -static const bsls::Types::Uint64 UINT64_MAX_DIVIDED_BY_10 = - UINT64_MAX_VALUE / 10; -static const bsls::Types::Uint64 UINT64_MAX_DIVIDED_BY_10_TO_THE_10 = - UINT64_MAX_VALUE / 10000000000ULL; -static const bsls::Types::Uint64 UINT64_MAX_VALUE_LAST_DIGIT = 5; +static const bsls::Types::Uint64 k_UINT64_MAX_DIVIDED_BY_10 = + k_UINT64_MAX_VALUE / 10; +static const bsls::Types::Uint64 k_UINT64_MAX_DIVIDED_BY_10_TO_THE_10 = + k_UINT64_MAX_VALUE / 10000000000ULL; +static const bsls::Types::Uint64 k_UINT64_MAX_VALUE_LAST_DIGIT = 5; -static const bsls::Types::Uint64 POWER_OF_TEN_LOOKUP[] = { +static const bsls::Types::Uint64 k_POWER_OF_TEN_LOOKUP[] = { 1ULL, // 1e0 10ULL, // 1e1 100ULL, // 1e2 @@ -207,7 +211,10 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, bool isNeg, isExpNegative; Int64 exponentBias; - bsl::string_view integer, fraction, exponentStr, significantDigits; + bsl::string_view integer, + fraction, + exponentStr, + significantDigits; bsl::string_view::size_type significantDigitsDotOffset; ImpUtil::decompose(&isNeg, @@ -243,12 +250,12 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, int rc = ImpUtil::appendDigits(&uExponent, 0, exponentStr); - if (0 != rc || uExponent > u::UINT64_MAX_DIVIDED_BY_10) { + if (0 != rc || uExponent > u::k_UINT64_MAX_DIVIDED_BY_10) { if (isExpNegative) { *result = 0; return k_NOT_INTEGRAL; // RETURN } - *result = u::UINT64_MAX_VALUE; + *result = u::k_UINT64_MAX_VALUE; return k_OVERFLOW; // RETURN } @@ -268,7 +275,7 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, // from the exponent) to fit into a Uint64. if (numSigDigits + exponent > 20) { - *result = u::UINT64_MAX_VALUE; + *result = u::k_UINT64_MAX_VALUE; return k_OVERFLOW; // RETURN } @@ -320,11 +327,11 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, Uint64 tmp; if (0 != ImpUtil::appendDigits(&tmp, 0, digits)) { - *result = u::UINT64_MAX_VALUE; + *result = u::k_UINT64_MAX_VALUE; return k_OVERFLOW; // RETURN } if (0 != ImpUtil::appendDigits(&tmp, tmp, moreDigits)) { - *result = u::UINT64_MAX_VALUE; + *result = u::k_UINT64_MAX_VALUE; return k_OVERFLOW; // RETURN } @@ -333,8 +340,8 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, BSLS_ASSERT(exponent <= 19); // sanity. tested above if (exponent >= 10) { - if (tmp > u::UINT64_MAX_DIVIDED_BY_10_TO_THE_10) { - *result = u::UINT64_MAX_VALUE; + if (tmp > u::k_UINT64_MAX_DIVIDED_BY_10_TO_THE_10) { + *result = u::k_UINT64_MAX_VALUE; return k_OVERFLOW; // RETURN } tmp *= 10000000000ULL; @@ -344,13 +351,13 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, BSLS_ASSERT(exponent <= 9); // sanity. tested above const bsls::Types::Uint64 uExponentMultiple = - u::POWER_OF_TEN_LOOKUP[exponent]; + u::k_POWER_OF_TEN_LOOKUP[exponent]; - if (u::UINT64_MAX_VALUE / uExponentMultiple >= tmp) { + if (u::k_UINT64_MAX_VALUE / uExponentMultiple >= tmp) { tmp *= uExponentMultiple; } else { - *result = u::UINT64_MAX_VALUE; + *result = u::k_UINT64_MAX_VALUE; return k_OVERFLOW; // RETURN } } @@ -361,7 +368,7 @@ int NumberUtil::asUint64(bsls::Types::Uint64 *result, // value is UINT64_MAX_VALUE plus some fraction, we prefer to report an // overflow. - return (tmp == u::UINT64_MAX_VALUE) ? k_OVERFLOW : k_NOT_INTEGRAL; + return (tmp == u::k_UINT64_MAX_VALUE) ? k_OVERFLOW : k_NOT_INTEGRAL; // RETURN } @@ -410,10 +417,10 @@ int NumberUtil::asDecimal64Exact(bdldfp::Decimal64 *result, bsl::string dataString(value, &allocator); - // Note that there an issue in the inteldfp library with incorrect status + // Note that there an issue in the 'inteldfp' library with incorrect status // values returned for 0's with an exponent outside the valid range for a - // 'Decimal64'. E.g., 0e200 will incorrectly return k_INEXACT. The test - // driver function 'asDecimal64ExactOracle' provides an alternative + // 'Decimal64'. E.g., '0e200' will incorrectly return 'k_INEXACT'. The + // test driver function 'asDecimal64ExactOracle' provides an alternative // implementation that fixes that (but is far more complicated). int rc = bdldfp::DecimalUtil::parseDecimal64Exact(result, @@ -507,11 +514,11 @@ bool NumberUtil::areEqual(const bsl::string_view& lhs, } // If computing the canonical exponent at any point requires values that - // cannot be represented as a int64, fall back to a string comparison. + // cannot be represented as a 'Int64', fall back to a string comparison. - if (lUExp > u::INT64_MAX_VALUE || rUExp > u::INT64_MAX_VALUE || - u::INT64_MAX_VALUE - l.d_significantDigitsBias < lUExp || - u::INT64_MAX_VALUE - r.d_significantDigitsBias < rUExp) { + if (lUExp > u::k_INT64_MAX_VALUE || rUExp > u::k_INT64_MAX_VALUE || + u::k_INT64_MAX_VALUE - l.d_significantDigitsBias < lUExp || + u::k_INT64_MAX_VALUE - r.d_significantDigitsBias < rUExp) { return compareNumberTextFallback(l, r); // RETURN } @@ -619,7 +626,10 @@ bool NumberUtil::isIntegralNumber(const bsl::string_view& value) bool isNeg, isExpNegative; Int64 exponentBias; - bsl::string_view integer, fraction, exponentStr, significantDigits; + bsl::string_view integer, + fraction, + exponentStr, + significantDigits; bsl::string_view::size_type significantDigitsDotOffset; ImpUtil::decompose(&isNeg, @@ -651,7 +661,7 @@ bool NumberUtil::isIntegralNumber(const bsl::string_view& value) } // 'value' is integral if the canonical exponent for 'value' is >= 0 (see - // NumberUtil_Imp::decompose for an explantion of the canonical exponent). + // NumberUtil_Imp::decompose for an explanation of the canonical exponent). // A mathematical expression for this would be: //.. // return 0 <= (isExpNegative ? -exponent : exponent) + exponentBias @@ -670,8 +680,8 @@ bool NumberUtil::isIntegralNumber(const bsl::string_view& value) void NumberUtil::stringify(bsl::string *result, bsls::Types::Int64 value) { - typedef bslalg::NumericFormatterUtil NFU; - char buffer[NFU::ToCharsMaxLength::k_VALUE]; + typedef bslalg::NumericFormatterUtil NFUtil; + char buffer[NFUtil::ToCharsMaxLength::k_VALUE]; char *ret = bslalg::NumericFormatterUtil::toChars( buffer, buffer + sizeof buffer, value); BSLS_ASSERT(0 != ret); @@ -681,8 +691,8 @@ void NumberUtil::stringify(bsl::string *result, bsls::Types::Int64 value) void NumberUtil::stringify(bsl::string *result, bsls::Types::Uint64 value) { - typedef bslalg::NumericFormatterUtil NFU; - char buffer[NFU::ToCharsMaxLength::k_VALUE]; + typedef bslalg::NumericFormatterUtil NFUtil; + char buffer[NFUtil::ToCharsMaxLength::k_VALUE]; char *ret = bslalg::NumericFormatterUtil::toChars( buffer, buffer + sizeof buffer, value); BSLS_ASSERT(0 != ret); @@ -692,8 +702,8 @@ void NumberUtil::stringify(bsl::string *result, bsls::Types::Uint64 value) void NumberUtil::stringify(bsl::string *result, double value) { - typedef bslalg::NumericFormatterUtil NFU; - char buffer[NFU::ToCharsMaxLength::k_VALUE]; + typedef bslalg::NumericFormatterUtil NFUtil; + char buffer[NFUtil::ToCharsMaxLength::k_VALUE]; char *ret = bslalg::NumericFormatterUtil::toChars( buffer, buffer + sizeof buffer, value); BSLS_ASSERT(0 != ret); @@ -727,9 +737,9 @@ int NumberUtil_ImpUtil::appendDigits(bsls::Types::Uint64 *result, bsl::string_view::const_iterator iter = digits.begin(); while (iter != digits.end()) { const unsigned digitValue = *iter - '0'; - if (value < u::UINT64_MAX_DIVIDED_BY_10 || - (u::UINT64_MAX_DIVIDED_BY_10 == value && - digitValue <= u::UINT64_MAX_VALUE_LAST_DIGIT)) { + if (value < u::k_UINT64_MAX_DIVIDED_BY_10 || + (u::k_UINT64_MAX_DIVIDED_BY_10 == value && + digitValue <= u::k_UINT64_MAX_VALUE_LAST_DIGIT)) { value = value * 10 + digitValue; ++iter; } @@ -783,11 +793,11 @@ void NumberUtil_ImpUtil::decompose( bsl::string_view::const_iterator intEnd = u::findFirstNonDigit(input.begin(), input.end()); - bsl::string_view::size_type intLen = bsl::distance(input.begin(), intEnd); - + bsl::string_view::size_type intLen = bsl::distance(input.begin(), + intEnd); *integer = input.substr(0, intLen); - bsl::string_view digits; + bsl::string_view digits; bsl::string_view::size_type digitsDotOffset; // Iterate over the fractional digits. @@ -824,8 +834,8 @@ void NumberUtil_ImpUtil::decompose( // 'digits' and 'digitsDotOffset' contain the non-canonical digits (and // decimal point) of 'value', from them we need to remove the leading and // trailing 0s to compute 'significantDigits', - // 'significantDigitsDotOffset', and 'signficantDigitsBias'. Some - // example values: + // 'significantDigitsDotOffset', and 'signficantDigitsBias'. Some example + // values: //.. // | digits | significantDigits | sigDigitsDotOffset | sigDigitsBias | // |----------|-------------------|--------------------|---------------| @@ -844,8 +854,8 @@ void NumberUtil_ImpUtil::decompose( bsl::string_view::size_type firstSignificantDigit, lastSignificantDigit; bsl::string_view::size_type lastNotZero = digits.find_last_not_of('0'); - // We ignore the fraction of 'digits' if there is no decimal point, - // or if the fraction 'digits' are all 0s. + // We ignore the fraction of 'digits' if there is no decimal point, or if + // the fraction 'digits' are all 0s. bool ignoreFraction = digitsDotOffset == npos; if (!ignoreFraction && lastNotZero == digitsDotOffset) { @@ -876,8 +886,8 @@ void NumberUtil_ImpUtil::decompose( lastSignificantDigit = lastNotZero; - *significantDigitsBias = -1LL * (lastSignificantDigit - integer->length()); - + *significantDigitsBias = -1LL * (lastSignificantDigit + - integer->length()); if (digits[0] != '0') { // E.g., "34.50" firstSignificantDigit = 0; diff --git a/groups/bdl/bdljsn/bdljsn_numberutil.h b/groups/bdl/bdljsn/bdljsn_numberutil.h index d9ddfb2e2a..2553ab405f 100644 --- a/groups/bdl/bdljsn/bdljsn_numberutil.h +++ b/groups/bdl/bdljsn/bdljsn_numberutil.h @@ -120,6 +120,7 @@ BSLS_IDENT("$Id: $") #include #include #include + #include #include #include @@ -201,6 +202,8 @@ struct NumberUtil { // typed integer conversions +// BDE_VERIFY pragma: -FABC01 // not in alphabetic order + /// Load the specified `result` with the specified `value`, even if a /// non-zero status is returned (truncating fractional digits if /// necessary). Return 0 on success, `k_OVERFLOW` if `value` is larger @@ -215,10 +218,20 @@ struct NumberUtil { /// `true`. Note that this operation will correctly handle exponents /// (e.g., a `value` of "0.00000000000000000001e20" will produce a `result` /// of 1). - static int asInt(int *result, const bsl::string_view& value); - static int asInt64(Int64 *result, const bsl::string_view& value); - static int asUint(unsigned int *result, const bsl::string_view& value); - static int asUint64(Uint64 *result, const bsl::string_view& value); + static int asShort(short *result, const bsl::string_view& value); + static int asUshort(unsigned short *result, const bsl::string_view& value); + static int asInt (int *result, const bsl::string_view& value); + static int asUint (unsigned int *result, const bsl::string_view& value); + static int asLong (long *result, const bsl::string_view& value); + static int asUlong(unsigned long *result, const bsl::string_view& value); + static int asLonglong + (long long *result, const bsl::string_view& value); + static int asUlonglong + (unsigned long long *result, const bsl::string_view& value); + static int asInt64 (Int64 *result, const bsl::string_view& value); + static int asUint64(Uint64 *result, const bsl::string_view& value); + +// BDE_VERIFY pragma: +FABC01 // not in alphabetic order // generic integer conversion @@ -250,9 +263,9 @@ struct NumberUtil { /// Load into the specified `result` a string representation of /// specified numerical `value`. - static void stringify(bsl::string *result, Int64 value); - static void stringify(bsl::string *result, Uint64 value); - static void stringify(bsl::string *result, double value); + static void stringify(bsl::string *result, long long value); + static void stringify(bsl::string *result, unsigned long long value); + static void stringify(bsl::string *result, double value); static void stringify(bsl::string *result, const bdldfp::Decimal64& value); // comparison @@ -325,9 +338,9 @@ struct NumberUtil_ImpUtil { /// `NumberUtil::asInteger`. template static int asIntegerDispatchImp( - t_INTEGER_TYPE *result, - const bsl::string_view& value, - bslmf::SelectTraitCase); + t_INTEGER_TYPE *result, + const bsl::string_view& value, + bslmf::SelectTraitCase); template static int asIntegerDispatchImp(t_INTEGER_TYPE *result, const bsl::string_view& value, @@ -416,6 +429,8 @@ struct NumberUtil_ImpUtil { // struct NumberUtil // ----------------- +// BDE_VERIFY pragma: -FABC01 // not in alphabetic order + inline double NumberUtil::asDouble(const bsl::string_view& value) { @@ -445,24 +460,61 @@ float NumberUtil::asFloat(const bsl::string_view& value) return static_cast(asDouble(value)); } +inline +int NumberUtil::asShort(short *result, const bsl::string_view& value) +{ + return asInteger(result, value); +} + inline int NumberUtil::asInt(int *result, const bsl::string_view& value) { return asInteger(result, value); } +inline +int NumberUtil::asLong(long *result, const bsl::string_view& value) +{ + return asInteger(result, value); +} + +inline +int NumberUtil::asLonglong(long long *result, const bsl::string_view& value) +{ + return asInteger(result, value); +} + inline int NumberUtil::asInt64(Int64 *result, const bsl::string_view& value) { return asInteger(result, value); } +inline +int NumberUtil::asUshort(unsigned short *result, const bsl::string_view& value) +{ + return asInteger(result, value); +} + inline int NumberUtil::asUint(unsigned int *result, const bsl::string_view& value) { return asInteger(result, value); } +inline +int NumberUtil::asUlong(unsigned long *result, const bsl::string_view& value) +{ + return asInteger(result, value); +} + +inline +int NumberUtil::asUlonglong(unsigned long long *result, + const bsl::string_view& value) +{ + return asInteger(result, value); +} + template int NumberUtil::asInteger(t_INTEGER_TYPE *result, const bsl::string_view& value) @@ -473,6 +525,8 @@ int NumberUtil::asInteger(t_INTEGER_TYPE *result, return NumberUtil_ImpUtil::asInteger(result, value); } +// BDE_VERIFY pragma: +FABC01 // not in alphabetic order + // ------------------------- // struct NumberUtil_ImpUtil // ------------------------- diff --git a/groups/bdl/bdljsn/bdljsn_numberutil.t.cpp b/groups/bdl/bdljsn/bdljsn_numberutil.t.cpp index d09f252d9c..8872c10abd 100644 --- a/groups/bdl/bdljsn/bdljsn_numberutil.t.cpp +++ b/groups/bdl/bdljsn/bdljsn_numberutil.t.cpp @@ -45,6 +45,8 @@ #include #include +#include // 'uint8_t' + using bsl::cout; using bsl::endl; using namespace BloombergLP; @@ -70,15 +72,21 @@ using namespace BloombergLP; // [ 6] double asDouble(const bsl::string_view&); // [ 7] bdldfp::Decimal64 asDecimal64(const bsl::string_view&); // [ 7] int asDecimal64Exact(Decimal64 *, const bsl::string_view&); +// [11] int asShort(short *, const bsl::string_view&); // [11] int asInt(int *, const bsl::string_view&); +// [11] int asLong(long *, const bsl::string_view&); +// [11] int asLonglong(long long *, const bsl::string_view&); // [11] int asInt64(Int64 *, const bsl::string_view&); // [11] int asUint(unsigned int *, const bsl::string_view&); +// [11] int asUshort(unsigned short *, const bsl::string_view&); +// [11] int asUlong(unsigned long *,, const bsl::string_view&); // [ 5] int asUint64(Uint64 *, const bsl::string_view&); +// [ 5] int asUlonglong(unsigned long long *, const bsl::string_view&); // [10] int asInteger(t_INTEGER_TYPE *, const bsl::string_view&); -// [12] void stringify(bsl::string *, Int64); -// [12] void stringify(bsl::string *, Uint64); +// [12] void stringify(bsl::string *, long long); +// [12] void stringify(bsl::string *, unsigned long long); // [12] void stringify(bsl::string *, double); -// [12] void stringify(bsl::string *, const bdldfp::Decimal64& vlue); +// [12] void stringify(bsl::string *, const bdldfp::Decimal64& value); // [ 8] bool areEqual(const string_view& , const string_view&); // // NumberUtil_ImpUtil @@ -92,6 +100,7 @@ using namespace BloombergLP; // [ 3] CONCERN: `IsValidNumber` functor can be used as an oracle. // [ 2] CONCERN: Test Machinery // [ ] CONCERN: `BSLS_REVIEW` failures should lead to test failures +// [ ] CONCERN: `bsls::Log` messages are sent by the tested module only. // [-1] BENCHMARK: asDecimal64Exact vs asDecimal64ExactOracle // ============================================================================ @@ -166,6 +175,65 @@ BSLA_MAYBE_UNUSED bool veryVerbose; BSLA_MAYBE_UNUSED bool veryVeryVerbose; BSLA_MAYBE_UNUSED bool veryVeryVeryVerbose; +// ============================================================================ +// HELPERS FOR REVIEW & LOG MESSAGE HANDLING +// ---------------------------------------------------------------------------- + +static bool containsCaseless(const bsl::string_view& string, + const bsl::string_view& subString) + // Return 'true' if the specified 'subString' is present in the specified + // 'string' disregarding case of alphabet characters '[a-zA-Z]', otherwise + // return 'false'. +{ + if (subString.empty()) { + return true; // RETURN + } + + typedef bdlb::StringViewUtil SVU; + const bsl::string_view rsv = SVU::strstrCaseless(string, subString); + + return !rsv.empty(); +} + +// ============================================================================ +// EXPECTED 'BSLS_REVIEW' TEST HANDLERS +// ---------------------------------------------------------------------------- + +// These handlers are needed only temporarily until we determine how to fix the +// broken contract of 'bdlb::NumericParseUtil::parseDouble()' that says under- +// and overflow is not allowed yet the function supports it. + +bool isBdlbNumericParseUtilReview(const bsls::ReviewViolation& reviewViolation) + // Return 'true' if the specified 'reviewViolation' has been raised by the + // 'bdlb_numericparseutil' component or no source file names are supported + // by the build, otherwise return 'false'. +{ + const char *fn = reviewViolation.fileName(); + const bool fileOk = ('\0' == fn[0]) // empty or has the component name + || containsCaseless(fn, "bdlb_numericparseutil"); + return fileOk; +} + +bool isRangeReview(const bsls::ReviewViolation& reviewViolation) + // Return 'true' if the specified 'reviewViolation' is an overflow or + // underflow message from the 'bdlb_numericparseutil' component (or no + // source file names are supported by the build), otherwise return 'false'. +{ + return isBdlbNumericParseUtilReview(reviewViolation) && + (containsCaseless(reviewViolation.comment(), "overflow") || + containsCaseless(reviewViolation.comment(), "underflow")); +} + +void ignoreRangeMsgs(const bsls::ReviewViolation& reviewViolation) + // If the specified 'reviewViolation' is an expected overflow-related + // message from 'parseDouble' do nothing, otherwise call + // 'bsls::Review::failByAbort()'. +{ + if (!isRangeReview(reviewViolation)) { + bsls::Review::failByAbort(reviewViolation); + } +} + // ============================================================================ // JSON NUMBER VALIDATOR ORACLE // ---------------------------------------------------------------------------- @@ -269,8 +337,8 @@ int fuzzyCompare(FP_TYPE a, FP_TYPE b) /// text to an in-process number type that can be called generically. int convertValue(double *result, const bsl::string_view& value) { - *result = Obj::NumberUtil::asDouble(value); - return 0; + *result = Obj::NumberUtil::asDouble(value); + return 0; } /// Load the specified `result` with the specified `value`. Note that these @@ -288,7 +356,7 @@ int convertValue(bdldfp::Decimal64 *result, const bsl::string_view& value) template int convertValue(t_INTEGER_TYPE *result, const bsl::string_view& value) { - return Obj::NumberUtil::asInteger(result, value); + return Obj::NumberUtil::asInteger(result, value); } /// Load the specified `significand` from the specified `value`. @@ -585,7 +653,7 @@ int asDecimal64ExactOracle(bdldfp::Decimal64 *result, // A flag indicating if the significant digits range includes a '.' - bool sigDigitsSeparated = significantDotOffset != bsl::string_view::npos; + bool sigDigitsSeparated = significantDotOffset != bsl::string_view::npos; Int64 numSigDigits = significant.size() - (sigDigitsSeparated ? 1 : 0); if (numSigDigits > 16) { @@ -593,7 +661,7 @@ int asDecimal64ExactOracle(bdldfp::Decimal64 *result, return Obj::k_INEXACT; // RETURN } - Uint64 significand; + Uint64 significand; bsl::string_view digits, moreDigits; if (!sigDigitsSeparated) { // If significant digits do not have a '.' in the middle @@ -631,7 +699,7 @@ template struct AsIntegralTest { // CONSTANTS - static const bool d_isSignedType = + static const bool s_isSignedType = BloombergLP::bdljsn::NumberUtil_IsSigned::value; /// See documentation for test case 10. @@ -654,7 +722,7 @@ struct AsIntegralTest { struct Data { int d_line; int d_status; - const char *d_input; + const char *d_input_p; bool d_isNegative; Uint64 d_result; } DATA[] = { @@ -735,7 +803,7 @@ struct AsIntegralTest { for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; const int STATUS = DATA[i].d_status; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const bool ISNEG = DATA[i].d_isNegative; const Uint64 EXPECTED_U64 = DATA[i].d_result; @@ -750,7 +818,7 @@ struct AsIntegralTest { // for `t_INTEGRAL_TYPE`. Uint64 maxAsU64 = static_cast(k_MAX_VALUE); - Uint64 minAsU64 = (d_isSignedType) ? maxAsU64 + 1 : 0; + Uint64 minAsU64 = (s_isSignedType) ? maxAsU64 + 1 : 0; t_INTEGRAL_TYPE expected; int expectedRc = (STATUS == ENOTINT) ? ENOTINT : 0; @@ -825,12 +893,20 @@ struct AsIntegralTest { // Test data generated from https://github.com/nst/JSONTestSuite (MIT License) // Generation done by the python script embedded below. Then the numeric test -// points extracted and massaged a bit. +// points extracted and messaged a bit. +// +// Two modified tests: +//.. +// n_multidigit_number_then_00.json - test data format can't handle embedded 0, +// manually tested // // One modified tests: // y_number_after_space.json - this is not a valid number according to the JSON // spec. A whitespace prefix will be handled by // the tokenizer it bdljsn. +//.. + +// BDE_VERIFY pragma: -NA01 // non-ASCII characters static bool isValidJSonSuiteFileData(const bsl::string_view& fname) { BSLS_ASSERT_OPT(!fname.empty()); @@ -849,6 +925,7 @@ static bool isValidJSonSuiteFileData(const bsl::string_view& fname) { || tag == 'i'; // implementation defined, we support all } +#if 0 /// Using the specified `hayStack and `needle': /// return bdlb::StringViewUtil::strstrCaseless(hayStack, needle) != /// bsl::string_view(); @@ -858,6 +935,7 @@ static bool containsCaseless(const bsl::string_view& hayStack, return bdlb::StringViewUtil::strstrCaseless(hayStack, needle) != bsl::string_view(); } +#endif static bool isOutOfRangeJSonSuiteFileData(const bsl::string_view& fname) { BSLS_ASSERT_OPT(!fname.empty()); @@ -1040,9 +1118,9 @@ extern "C" /// this component and return zero. int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - const char *FUZZ = reinterpret_cast(data); - const int LENGTH = static_cast(size); - const int test = 3; + const char *FUZZ = reinterpret_cast(data); + const int LENGTH = static_cast(size); + const int test = 3; switch (test) { case 0: // Zero is always the leading case. case 3: { @@ -1114,9 +1192,8 @@ int main(int argc, char *argv[]) bsl::cout << "\nUSAGE EXAMPLE" "\n=============" << bsl::endl; } - - bsl::ostringstream oss; - bsl::streambuf *savedStreambuf_p = bsl::cout.rdbuf(); + bsl::ostringstream oss; + bsl::streambuf *savedStreambuf_p = bsl::cout.rdbuf(); bsl::cout.rdbuf(oss.rdbuf()); ///Usage @@ -1169,7 +1246,7 @@ int main(int argc, char *argv[]) // Finally, we convert that number to an integer: // ``` bsls::Types::Int64 value; - int rc = bdljsn::NumberUtil::asInt64(&value, EXAMPLE); + int rc = bdljsn::NumberUtil::asInt64(&value, EXAMPLE); // bsl::cout << " * value: " << value; // @@ -1213,7 +1290,7 @@ int main(int argc, char *argv[]) case 12: { // -------------------------------------------------------------------- - // TESTING: stringify + // TESTING: 'stringify' // This is a white box test, the methods under test all simply // forward to `NumericFormatterUtil` and `DecimalUtil` // @@ -1231,23 +1308,24 @@ int main(int argc, char *argv[]) // original. // // Testing: - // void stringify(bsl::string *, Int64 ); - // void stringify(bsl::string *, Uint64 ); - // void stringify(bsl::string *, double ); - // void stringify(bsl::string *, const bdldfp::Decimal64& vlue); + // void stringify(bsl::string *, long long); + // void stringify(bsl::string *, unsigned long long); + // void stringify(bsl::string *, double); + // void stringify(bsl::string *, const bdldfp::Decimal64& value); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: stringify" - << "\n==================" << bsl::endl; + bsl::cout << "\nTESTING: 'stringify'" + << "\n====================" << bsl::endl; } - if (verbose) bsl::cout << "\tTest stringify Int64" << bsl::endl; + if (verbose) bsl::cout << "\tTest 'stringify' 'long long'" + << bsl::endl; { struct Data { int d_line; - Int64 d_input; - const char *d_expected; + long long d_input; + const char *d_expected_p; } DATA[] = { { L_, 0, "0" }, { L_, 15, "15" }, @@ -1261,9 +1339,9 @@ int main(int argc, char *argv[]) const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); for (int i = 0; i < NUM_DATA; ++i) { - const int LINE = DATA[i].d_line; - const Int64 INPUT = DATA[i].d_input; - const char *EXPECTED = DATA[i].d_expected; + const int LINE = DATA[i].d_line; + const long long INPUT = DATA[i].d_input; + const char *EXPECTED = DATA[i].d_expected_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P(EXPECTED); @@ -1274,19 +1352,20 @@ int main(int argc, char *argv[]) ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); Int64 roundTrip; - int rc = Obj::asInt64(&roundTrip, result); + int rc = Obj::asLonglong(&roundTrip, result); ASSERTV(LINE, INPUT, rc, roundTrip, result, 0 == rc); ASSERTV(LINE, INPUT, roundTrip, result, roundTrip == INPUT); } } - if (verbose) bsl::cout << "\tTest stringify Uint64" << bsl::endl; + if (verbose) bsl::cout << "\tTest 'stringify' 'unsigned long long'" + << bsl::endl; { struct Data { - int d_line; - Uint64 d_input; - const char *d_expected; + int d_line; + unsigned long long d_input; + const char *d_expected_p; } DATA[] = { { L_, 0, "0" }, { L_, 15, "15" }, @@ -1298,12 +1377,12 @@ int main(int argc, char *argv[]) const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); for (int i = 0; i < NUM_DATA; ++i) { - const int LINE = DATA[i].d_line; - const Uint64 INPUT = DATA[i].d_input; - const char *EXPECTED = DATA[i].d_expected; + const int LINE = DATA[i].d_line; + const unsigned long long INPUT = DATA[i].d_input; + const char *EXPECTED = DATA[i].d_expected_p; if (veryVeryVerbose) { - P_(LINE); P_(INPUT); P(EXPECTED); + P_(LINE) P_(INPUT) P(EXPECTED) } bsl::string result; Obj::stringify(&result, INPUT); @@ -1311,19 +1390,19 @@ int main(int argc, char *argv[]) ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); Uint64 roundTrip; - int rc = Obj::asUint64(&roundTrip, result); + int rc = Obj::asUlonglong(&roundTrip, result); ASSERTV(LINE, INPUT, rc, roundTrip, result, 0 == rc); ASSERTV(LINE, INPUT, roundTrip, result, roundTrip == INPUT); } } - if (verbose) bsl::cout << "\tTest stringify double" << bsl::endl; + if (verbose) bsl::cout << "\tTest 'stringify' 'double'" << bsl::endl; { struct Data { int d_line; double d_input; - const char *d_expected; + const char *d_expected_p; } DATA[] = { { L_, 0, "0" }, { L_, 15, "15" }, @@ -1343,7 +1422,7 @@ int main(int argc, char *argv[]) for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; const double INPUT = DATA[i].d_input; - const char *EXPECTED = DATA[i].d_expected; + const char *EXPECTED = DATA[i].d_expected_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P(EXPECTED); @@ -1360,11 +1439,11 @@ int main(int argc, char *argv[]) } if (verbose) - bsl::cout << "\tTest stringify bdldfp::Decimal64" << bsl::endl; + bsl::cout << "\tTest 'stringify' 'bdldfp::Decimal64'" << bsl::endl; { struct Data { int d_line; - const char *d_text; + const char *d_text_p; } DATA[] = { { L_, "0" }, { L_, "15" }, @@ -1380,8 +1459,8 @@ int main(int argc, char *argv[]) const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); for (int i = 0; i < NUM_DATA; ++i) { - const int LINE = DATA[i].d_line; - const char *TEXT = DATA[i].d_text; + const int LINE = DATA[i].d_line; + const char *TEXT = DATA[i].d_text_p; bdldfp::Decimal64 INPUT; @@ -1402,7 +1481,7 @@ int main(int argc, char *argv[]) } break; case 11: { // -------------------------------------------------------------------- - // TESTING: as[Ui|I]nt[64] + // TESTING: 'as[Ui|I]nt[64]' // This is a white box test, the methods under test all simply // forward to `asInteger` which was previously tested. // @@ -1425,23 +1504,110 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // int asInt(int *, const bsl::string_view& ); - // int asUint(unsigned int *, const bsl::string_view& ); - // int asInt64(Int64 *, const bsl::string_view& ); + // int asShort(short *, const bsl::string_view&); + // int asInt(int *, const bsl::string_view&); + // int asLong(long *, const bsl::string_view&); + // int asLonglong(long long *, const bsl::string_view&); + // int asInt64(Int64 *, const bsl::string_view&); + // int asUshort(unsigned short *, const bsl::string_view&); + // int asUint(unsigned int *, const bsl::string_view&); + // int asUlong(unsigned long *,, const bsl::string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: as[Ui|I]nt[64]" - << "\n=======================" << bsl::endl; + bsl::cout << "\nTESTING: 'as[Ui|I]nt[64]'" + << "\n=========================" << bsl::endl; } - if (verbose) bsl::cout << "\tTest asInt" << bsl::endl; + if (verbose) bsl::cout << "\tTest 'asShort'" << bsl::endl; + { + struct Data { + int d_line; + int d_expectedRc; + short d_expected; + const char *d_input_p; + } DATA[] = { + { L_, OK, 0, "0" }, + { L_, OK, 0, "-0.0000" }, + { L_, OK, 15, "15" }, + { L_, OK, -15, "-15" }, + { L_, OK, 100, "1e2" }, + + // boundary checks + { L_, OK, 32767, "32767" }, + { L_, EOVER, 32767, "32768" }, + { L_, OK, -32768, "-32768" }, + { L_, EUNDER, -32768, "-32769" }, + + // fraction + { L_, ENOTINT, 1, "15e-1" } + }; + const int NUM_DATA = sizeof DATA / sizeof *DATA; + + for (int i = 0; i < NUM_DATA; ++i) { + const int LINE = DATA[i].d_line; + const int EXPRC = DATA[i].d_expectedRc; + const int EXPECTED = DATA[i].d_expected; + const char *INPUT = DATA[i].d_input_p; + + if (veryVeryVerbose) { + P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); + } + short result; + int rc = Obj::asShort(&result, INPUT); + + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); + } + } + + if (verbose) bsl::cout << "\tTest 'asUshort'" << bsl::endl; + { + struct Data { + int d_line; + int d_expectedRc; + unsigned short d_expected; + const char *d_input_p; + } DATA[] = { + { L_, OK, 0U, "0" }, + { L_, OK, 0U, "-0.0000" }, + { L_, OK, 15U, "15" }, + { L_, OK, 100U, "1e2" }, + + // boundary checks + { L_, OK, 65535U, "65535" }, + { L_, EOVER, 65535U, "65536" }, + { L_, EUNDER, 0U, "-1" }, + + // fraction + { L_, ENOTINT, 1U, "15e-1" } + }; + const int NUM_DATA = sizeof DATA / sizeof *DATA; + + for (int i = 0; i < NUM_DATA; ++i) { + const int LINE = DATA[i].d_line; + const int EXPRC = DATA[i].d_expectedRc; + const unsigned int EXPECTED = DATA[i].d_expected; + const char *INPUT = DATA[i].d_input_p; + + if (veryVeryVerbose) { + P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); + } + unsigned short result; + int rc = Obj::asUshort(&result, INPUT); + + ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + } + } + + if (verbose) bsl::cout << "\tTest 'asInt'" << bsl::endl; { struct Data { int d_line; int d_expectedRc; int d_expected; - const char *d_input; + const char *d_input_p; } DATA[] = { { L_, OK, 0, "0" }, { L_, OK, 0, "-0.0000" }, @@ -1456,15 +1622,15 @@ int main(int argc, char *argv[]) { L_, EUNDER, -2147483647-1, "-2147483649" }, // fraction - { L_, ENOTINT, 1, "15e-1" }, + { L_, ENOTINT, 1, "15e-1" } }; - const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); + const int NUM_DATA = sizeof DATA / sizeof *DATA; for (int i = 0; i < NUM_DATA; ++i) { - const int LINE = DATA[i].d_line; - const int EXPRC = DATA[i].d_expectedRc; - const int EXPECTED = DATA[i].d_expected; - const char *INPUT = DATA[i].d_input; + const int LINE = DATA[i].d_line; + const int EXPRC = DATA[i].d_expectedRc; + const int EXPECTED = DATA[i].d_expected; + const char *INPUT = DATA[i].d_input_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); @@ -1472,19 +1638,18 @@ int main(int argc, char *argv[]) int result; int rc = Obj::asInt(&result, INPUT); + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); - ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); - } } - if (verbose) bsl::cout << "\tTest asUint" << bsl::endl; + if (verbose) bsl::cout << "\tTest 'asUint'" << bsl::endl; { struct Data { int d_line; int d_expectedRc; unsigned int d_expected; - const char *d_input; + const char *d_input_p; } DATA[] = { { L_, OK, 0U, "0" }, { L_, OK, 0U, "-0.0000" }, @@ -1497,41 +1662,200 @@ int main(int argc, char *argv[]) { L_, EUNDER, 0U, "-1" }, // fraction - { L_, ENOTINT, 1U, "15e-1" }, + { L_, ENOTINT, 1U, "15e-1" } }; - const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); + const int NUM_DATA = sizeof DATA / sizeof *DATA; for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; const int EXPRC = DATA[i].d_expectedRc; const unsigned int EXPECTED = DATA[i].d_expected; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); } unsigned int result; - int rc = Obj::asUint(&result, INPUT); + int rc = Obj::asUint(&result, INPUT); ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); - ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + } + } + + if (verbose) bsl::cout << "\tTest 'asLong'" << bsl::endl; + { + const long k_MAX = bsl::numeric_limits::max(); + const char *const s_MAX_32_OK = "2147483647"; + const char *const s_MAX_32_OVER = "2147483648"; + const char *const s_MIN_32_OK = "-2147483648"; + const char *const s_MIN_32_UNDER = "-2147483649"; + // | + + const char *const s_MAX_64_OK = "9223372036854775807"; + const char *const s_MAX_64_OVER = "9223372036854775808"; + const char *const s_MIN_64_OK = "-9223372036854775808"; + const char *const s_MIN_64_UNDER = "-9223372036854775809"; + // | + const bool IS_LP64 = sizeof(long) == sizeof(long long); + + if (veryVeryVerbose) { + P(IS_LP64) + } + + const char *const s_MAX_OK = IS_LP64 + ? s_MAX_64_OK + : s_MAX_32_OK; + + const char *const s_MAX_OVER = IS_LP64 + ? s_MAX_64_OVER + : s_MAX_32_OVER; + + const char *const s_MIN_OK = IS_LP64 + ? s_MIN_64_OK + : s_MIN_32_OK; + + const char *const s_MIN_UNDER = IS_LP64 + ? s_MIN_64_UNDER + : s_MIN_32_UNDER; + + struct Data { + int d_line; + int d_expectedRc; + long d_expected; + const char *d_input_p; + } DATA[] = { + { L_, OK, 0L, "0" }, + { L_, OK, 0L, "-0.0000" }, + { L_, OK, 15L, "15" }, + { L_, OK, -15L, "-15" }, + { L_, OK, 100L, "1e2" }, + + // boundary checks + { L_, OK, k_MAX, s_MAX_OK }, + { L_, EOVER, k_MAX, s_MAX_OVER }, + { L_, OK, -k_MAX-1, s_MIN_OK }, + { L_, EUNDER, -k_MAX-1, s_MIN_UNDER }, + + // fraction + { L_, ENOTINT, 1L, "15e-1" } + + }; + const int NUM_DATA = sizeof DATA / sizeof *DATA; + + for (int i = 0; i < NUM_DATA; ++i) { + const int LINE = DATA[i].d_line; + const int EXPRC = DATA[i].d_expectedRc; + const long EXPECTED = DATA[i].d_expected; + const char *INPUT = DATA[i].d_input_p; + + if (veryVeryVerbose) { + P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); + } + long result; + int rc = Obj::asLong(&result, INPUT); // TEST + + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); } } - if (verbose) bsl::cout << "\tTest asInt64" << bsl::endl; + if (verbose) bsl::cout << "\tTest 'asUlong'" << bsl::endl; + { + const unsigned long k_MAX = bsl::numeric_limits + ::max(); + const unsigned long k_MIN = bsl::numeric_limits + ::min(); + ASSERTV(k_MIN, 0UL == k_MIN); + + const char *const s_MAX_32_OK = "4294967295"; + const char *const s_MAX_32_OVER = "4294967296"; + const char *const s_MIN_32_OK = "0"; + const char *const s_MIN_32_UNDER = "-1"; + // | + + const char *const s_MAX_64_OK = "18446744073709551615"; + const char *const s_MAX_64_OVER = "18446744073709551616"; + const char *const s_MIN_64_OK = "0"; + const char *const s_MIN_64_UNDER = "-1"; + // | + + const bool IS_LP64 = sizeof(unsigned long) == + sizeof(unsigned long long); + + if (veryVeryVerbose) { + P(IS_LP64) + } + + const char *const s_MAX_OK = IS_LP64 + ? s_MAX_64_OK + : s_MAX_32_OK; + + const char *const s_MAX_OVER = IS_LP64 + ? s_MAX_64_OVER + : s_MAX_32_OVER; + + const char *const s_MIN_OK = IS_LP64 + ? s_MIN_64_OK + : s_MIN_32_OK; + + const char *const s_MIN_UNDER = IS_LP64 + ? s_MIN_64_UNDER + : s_MIN_32_UNDER; + + struct Data { + int d_line; + int d_expectedRc; + unsigned long d_expected; + const char *d_input_p; + } DATA[] = { + { L_, OK, 0UL, "0" }, + { L_, OK, 0UL, "-0.0000" }, + { L_, OK, 15UL, "15" }, + { L_, OK, 100UL, "1e2" }, + + // boundary checks + { L_, OK, k_MAX, s_MAX_OK }, + { L_, EOVER, k_MAX, s_MAX_OVER }, + { L_, OK, k_MIN, s_MIN_OK }, + { L_, EUNDER, k_MIN, s_MIN_UNDER }, + + // fraction + { L_, ENOTINT, 1UL, "15e-1" } + }; + const int NUM_DATA = sizeof DATA / sizeof *DATA; + + for (int i = 0; i < NUM_DATA; ++i) { + const int LINE = DATA[i].d_line; + const int EXPRC = DATA[i].d_expectedRc; + const unsigned long EXPECTED = DATA[i].d_expected; + const char *INPUT = DATA[i].d_input_p; + + if (veryVeryVerbose) { + P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); + } + unsigned long result; + int rc = Obj::asUlong(&result, INPUT); // TEST + + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); + } + } + + if (verbose) bsl::cout << "\tTest 'asInt64'/'asLonglong'" << bsl::endl; { struct Data { int d_line; int d_expectedRc; Int64 d_expected; - const char *d_input; + const char *d_input_p; } DATA[] = { - { L_, OK, 0, "0" }, - { L_, OK, 0, "-0.0000" }, - { L_, OK, 15, "15" }, - { L_, OK, -15, "-15" }, - { L_, OK, 100, "1e2" }, + { L_, OK, 0, "0" }, + { L_, OK, 0, "-0.0000" }, + { L_, OK, 15, "15" }, + { L_, OK, -15, "-15" }, + { L_, OK, 100, "1e2" }, // boundary checks { L_, OK, 9223372036854775807LL, "9223372036854775807" }, @@ -1539,26 +1863,32 @@ int main(int argc, char *argv[]) { L_, OK, -9223372036854775807LL - 1, "-9223372036854775808" }, { L_, EUNDER, -9223372036854775807LL - 1, "-9223372036854775809" }, - // fraction - { L_, ENOTINT, 1, "15e-1" }, + // fraction + { L_, ENOTINT, 1, "15e-1" }, }; const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); for (int i = 0; i < NUM_DATA; ++i) { - const int LINE = DATA[i].d_line; - const int EXPRC = DATA[i].d_expectedRc; + const int LINE = DATA[i].d_line; + const int EXPRC = DATA[i].d_expectedRc; const Int64 EXPECTED = DATA[i].d_expected; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P_(EXPRC); P(EXPECTED); } + Int64 result; - int rc = Obj::asInt64(&result, INPUT); + int rc = Obj::asInt64(&result, INPUT); // TEST + + long long resultLL; + int rcLL = Obj::asLonglong(&resultLL, INPUT); // TEST + ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); - ASSERTV(LINE, INPUT, EXPRC, rc, EXPRC == rc); + ASSERTV(LINE, rc, rcLL, rc == rcLL); + ASSERTV(LINE, result, resultLL, result == resultLL); } } @@ -1624,12 +1954,12 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // int asInteger(t_INTEGER_TYPE *, const bsl::string_view& ); + // int asInteger(t_INTEGER_TYPE *, const bsl::string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: asInteger" - << "\n==================" << bsl::endl; + bsl::cout << "\nTESTING: 'asInteger'" + << "\n====================" << bsl::endl; } if (verbose) bsl::cout << "\tUse the template test facility" << bsl::endl; @@ -1637,9 +1967,9 @@ int main(int argc, char *argv[]) #define INTEGRAL_TYPES char, unsigned char, short, unsigned short, int, \ unsigned int, long, unsigned long, long long, unsigned long long - BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE(AsIntegralTest, - testCase10, - INTEGRAL_TYPES); + BSLTF_TEMPLATETESTFACILITY_RUN_EACH_TYPE(AsIntegralTest, + testCase10, + INTEGRAL_TYPES); #undef INTEGRAL_TYPES } @@ -1655,7 +1985,7 @@ int main(int argc, char *argv[]) case 9: { // -------------------------------------------------------------------- - // TESTING: isIntegerNumber + // TESTING: 'isIntegerNumber' // //Concerns: // 1. That `isIntegerNumber` returns `true` when the text supplied for @@ -1694,12 +2024,12 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // bool isIntegralNumber(const bsl::string_view& ); + // bool isIntegralNumber(const bsl::string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: isIntegerNumber" - << "\n========================" << bsl::endl; + bsl::cout << "\nTESTING: 'isIntegerNumber'" + << "\n==========================" << bsl::endl; } if (verbose) { @@ -1713,7 +2043,7 @@ int main(int argc, char *argv[]) struct Data { int d_line; bool d_isInteger; - const char *d_input; + const char *d_input_p; } DATA[] = { { L_, T, "0" }, { L_, T, "-0" }, @@ -1755,8 +2085,8 @@ int main(int argc, char *argv[]) const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const bool EXP = DATA[i].d_isInteger; - const char *INPUT = DATA[i].d_input; + const bool EXP = DATA[i].d_isInteger; + const char *INPUT = DATA[i].d_input_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P(EXP); @@ -1780,12 +2110,12 @@ int main(int argc, char *argv[]) } if (verbose) { - bsl::cout << "\tTest an extreme number of digits" << bsl::endl; + bsl::cout << "\tTest an extreme number of digits" << bsl::endl; } for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; const bool EXP = DATA[i].d_isInteger; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; if (veryVeryVerbose) { P_(LINE); P_(INPUT); P(EXP); @@ -1797,13 +2127,13 @@ int main(int argc, char *argv[]) bsl::vector::const_iterator it = equivalentData.begin(); for (; it != equivalentData.end(); ++it) { - const bsl::string& input = *it; - if (veryVeryVerbose) { - bsl::cout << " "; - P_(LINE); P_(INPUT); P(input.size()); + const bsl::string& input = *it; + if (veryVeryVerbose) { + bsl::cout << " "; + P_(LINE); P_(INPUT); P(input.size()); + } + ASSERTV(LINE, EXP, EXP == Obj::isIntegralNumber(input)); } - ASSERTV(LINE, EXP, EXP == Obj::isIntegralNumber(input)); - } } if (verbose) bsl::cout << "\tNegative Testing." << bsl::endl; @@ -1818,7 +2148,7 @@ int main(int argc, char *argv[]) } break; case 8: { // -------------------------------------------------------------------- - // TESTING: areEqual + // TESTING: 'areEqual' // //Concerns: // 1. That `areEqual` returns `true` when the text supplied for @@ -1857,12 +2187,12 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // bool areEqual(const string_view& , const string_view& ); + // bool areEqual(const string_view& , const string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: areEqual" - << "\n=================" << bsl::endl; + bsl::cout << "\nTESTING: 'areEqual'" + << "\n===================" << bsl::endl; } if (verbose) { @@ -1873,7 +2203,7 @@ int main(int argc, char *argv[]) struct Data { int d_line; // line int d_equalityGroup; // group of equal numbers - const char *d_input; // input text + const char *d_input_p; // input text } DATA[] = { { L_, 0, "0" }, { L_, 0, "-0" }, @@ -1922,12 +2252,12 @@ int main(int argc, char *argv[]) for (int i = 0; i < NUM_DATA; ++i) { const int LHS_LINE = DATA[i].d_line; const int LHS_GROUP = DATA[i].d_equalityGroup; - const char *LHS = DATA[i].d_input; + const char *LHS = DATA[i].d_input_p; for (int j = 0; j < NUM_DATA; ++j) { const int RHS_LINE = DATA[j].d_line; const int RHS_GROUP = DATA[j].d_equalityGroup; - const char *RHS = DATA[j].d_input; + const char *RHS = DATA[j].d_input_p; bool EXP = RHS_GROUP == LHS_GROUP; @@ -1989,7 +2319,7 @@ int main(int argc, char *argv[]) // representation (both convert to 0). The logic here assumes // that the test data does *not* include two different test // points that are numerically equal but do not convert to a - // Decimal64 exactly (a property we manually verified). + // 'Decimal64' exactly (a property we manually verified). bdldfp::Decimal64 junk; const bool bothAreExact = @@ -2020,7 +2350,7 @@ int main(int argc, char *argv[]) // This is a white box test. `asDecimal64` delegates to // `DecimalUtil` which we assume to be correct. This // test primarily looks for edge cases in the JSON number - // specification that might result in strtod reporting an error. + // specification that might result in 'strtod' reporting an error. // //Concerns: // 1. That `asDecimal64[Exact]` returns the closest decimal floating @@ -2067,13 +2397,13 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // bdldfp::Decimal64 asDecimal64(const bsl::string_view& ); - // int asDecimal64Exact(Decimal64 *, const bsl::string_view& ); + // bdldfp::Decimal64 asDecimal64(const bsl::string_view&); + // int asDecimal64Exact(Decimal64 *, const bsl::string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: asDecimal" - << "\n==================" << bsl::endl; + bsl::cout << "\nTESTING: 'asDecimal64'" + << "\n======================" << bsl::endl; } const int k_INE = Obj::k_INEXACT; @@ -2082,14 +2412,14 @@ int main(int argc, char *argv[]) struct Data { int d_line; int d_status; - const char *d_input; + const char *d_input_p; } DATA[] = { { L_, 0, "0" }, { L_, 0, "-0" }, { L_, 0, "0.0" }, { L_, 0, "0.0000" }, -// The following tests are disabled because of an issue in the inteldfp +// The following tests are disabled because of an issue in the 'inteldfp' // library. See note in the implementation. // { L_, 0, "0.0000e100000" }, // { L_, 0, "0.0e-100000" }, @@ -2143,7 +2473,7 @@ int main(int argc, char *argv[]) } for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const int STATUS = DATA[i].d_status; // This is a sanity check that our test data is correct. @@ -2198,11 +2528,11 @@ int main(int argc, char *argv[]) } } if (verbose) { - bsl::cout << "\tTest an extreme number of digits" << bsl::endl; + bsl::cout << "\tTest an extreme number of digits" << bsl::endl; } for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const int STATUS = DATA[i].d_status; if (veryVerbose) { @@ -2210,8 +2540,9 @@ int main(int argc, char *argv[]) } bdldfp::Decimal64 EXPECTED; - int rc = bdldfp::DecimalUtil::parseDecimal64(&EXPECTED, INPUT); - + int rc = bdldfp::DecimalUtil::parseDecimal64( + &EXPECTED, + INPUT); ASSERTV(rc, 0 == rc); bsl::vector equivalentData; @@ -2229,7 +2560,7 @@ int main(int argc, char *argv[]) bdldfp::Decimal64 result = Obj::asDecimal64(input); ASSERTV(LINE, EXPECTED, result, EXPECTED == result); if (result == bdldfp::Decimal64(0)) { - // There is a known issue with the inteldfp library + // There is a known issue with the 'inteldfp' library // incorrectly returning INEXACT for 0 with large or small // exponents. @@ -2290,7 +2621,7 @@ int main(int argc, char *argv[]) // This is a white box test. `asDouble` delegates to // NumericParseUtil, which delegates to the standard library, which // we assume to be correct. This test primarily looks for edge cases - // in the JSON number specification that might result in strtod + // in the JSON number specification that might result in 'strtod' // reporting an error. // //Concerns: @@ -2328,13 +2659,13 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // float asFloat(const bsl::string_view& ); - // double asDouble(const bsl::string_view& ); + // float asFloat(const bsl::string_view&); + // double asDouble(const bsl::string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: asDouble, asFloat" - << "\n==========================" << bsl::endl; + bsl::cout << "\nTESTING: 'asDouble', 'asFloat'" + << "\n==============================" << bsl::endl; } const double k_BIG_INT = @@ -2348,7 +2679,7 @@ int main(int argc, char *argv[]) struct Data { int d_line; - const char *d_input; + const char *d_input_p; double d_result; } DATA[] = { { L_, "0", 0 }, @@ -2400,7 +2731,7 @@ int main(int argc, char *argv[]) } for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const volatile double EXPECTED = DATA[i].d_result; if (veryVerbose) { @@ -2426,25 +2757,25 @@ int main(int argc, char *argv[]) for (; it != equivalentData.end(); ++it) { const bsl::string& input = *it; if (veryVeryVerbose) { - bsl::cout << " "; - P_(LINE); P(input); - } - volatile double result = Obj::asDouble(input); + bsl::cout << " "; + P_(LINE); P(input); + } + volatile double result = Obj::asDouble(input); - ASSERTV(LINE, input, EXPECTED, result, + ASSERTV(LINE, input, EXPECTED, result, fuzzyCompare(EXPECTED, result)); - volatile float fR = Obj::asFloat(input); - ASSERTV(LINE, input, fR == static_cast(result)); - } + volatile float fR = Obj::asFloat(input); + ASSERTV(LINE, input, fR == static_cast(result)); + } } if (verbose) { - bsl::cout << "\tTest an extreme number of digits" << bsl::endl; + bsl::cout << "\tTest an extreme number of digits" << bsl::endl; } for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const volatile double EXPECTED = DATA[i].d_result; if (veryVerbose) { @@ -2496,7 +2827,7 @@ int main(int argc, char *argv[]) } double result, expected; - int rc = bdlb::NumericParseUtil::parseDouble(&expected, INPUT); + int rc = bdlb::NumericParseUtil::parseDouble(&expected, INPUT); ASSERTV(LINE, INPUT, 0 == rc || (RANGE_ERR && ERANGE == rc)); result = Obj::asDouble(INPUT); @@ -2514,7 +2845,7 @@ int main(int argc, char *argv[]) } break; case 5: { // -------------------------------------------------------------------- - // TESTING: asUint64 + // TESTING: 'asUint64', 'asUlonglong' // // Concerns: // 1. For basic integral text input in the [0, k_MAX] return that @@ -2558,12 +2889,13 @@ int main(int argc, char *argv[]) // triggered when. // // Testing: - // int asUint64(Uint64 *, const bsl::string_view& ); + // int asUint64(Uint64 *, const bsl::string_view&); + // int asUlonglong(unsigned long long *, const bsl::string_view&); // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: asUint64" - << "\n=================" << bsl::endl; + bsl::cout << "\nTESTING: 'asUint64', 'asUlonglong'" + << "\n==================================" << bsl::endl; } const Uint64 k_MAX = bsl::numeric_limits::max(); @@ -2571,7 +2903,7 @@ int main(int argc, char *argv[]) struct Data { int d_line; int d_status; - const char *d_input; + const char *d_input_p; Uint64 d_result; } DATA[] = { { L_, OK, "0", 0 }, @@ -2630,7 +2962,7 @@ int main(int argc, char *argv[]) for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; const int STATUS = DATA[i].d_status; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const Uint64 EXPECTED = DATA[i].d_result; if (veryVerbose) { @@ -2638,7 +2970,7 @@ int main(int argc, char *argv[]) } Uint64 result; - int rc = Obj::asUint64(&result, INPUT); + int rc = Obj::asUint64(&result, INPUT); ASSERTV(LINE, INPUT, STATUS, rc, STATUS == rc); ASSERTV(LINE, INPUT, EXPECTED, result, EXPECTED == result); @@ -2659,25 +2991,32 @@ int main(int argc, char *argv[]) bsl::vector::const_iterator it = equivalentData.begin(); for (; it != equivalentData.end(); ++it) { - const bsl::string& input = *it; - if (veryVeryVerbose) { - bsl::cout << " "; - P_(LINE); P(input); - } - Uint64 result; - int rc = Obj::asUint64(&result, input); - ASSERTV(LINE, input, STATUS, rc, STATUS == rc); - ASSERTV(LINE, input, EXPECTED, result, EXPECTED == result); + const bsl::string& input = *it; + if (veryVeryVerbose) { + bsl::cout << " "; + P_(LINE); P(input); + } + Uint64 result; + int rc = Obj::asUint64(&result, input); + ASSERTV(LINE, input, STATUS, rc, STATUS == rc); + ASSERTV(LINE, input, EXPECTED, result, EXPECTED == result); + + { + unsigned long long result; + + int rc = Obj::asUlonglong(&result, input); // TEST + ASSERTV(LINE, input, STATUS, rc, STATUS == rc); + } } } if (verbose) { - bsl::cout << "\tTest an extreme number of digits" << bsl::endl; + bsl::cout << "\tTest an extreme number of digits" << bsl::endl; } for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; const int STATUS = DATA[i].d_status; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const Uint64 EXPECTED = DATA[i].d_result; if (veryVerbose) { @@ -2701,6 +3040,14 @@ int main(int argc, char *argv[]) int rc = Obj::asUint64(&result, input); ASSERTV(LINE, STATUS, rc, STATUS == rc); ASSERTV(LINE, EXPECTED, result, EXPECTED == result); + + { + unsigned long long result; + + int rc = Obj::asUlonglong(&result, input); // TEST + ASSERTV(LINE, STATUS, rc, STATUS == rc); + ASSERTV(LINE, EXPECTED, result, EXPECTED == result); + } } } @@ -2729,7 +3076,7 @@ int main(int argc, char *argv[]) // if the comparison is successful, either when converting both // numbers to an integer, or converting both numbers to a double. - Uint64 result; + Uint64 result, resultUll; double expected; int rc = bdlb::NumericParseUtil::parseDouble(&expected, INPUT); @@ -2737,7 +3084,13 @@ int main(int argc, char *argv[]) ASSERTV(LINE, INPUT, RANGE_ERR, 0 == rc || (RANGE_ERR && ERANGE ==rc)); - rc = Obj::asUint64(&result, INPUT); + int rcUll; + + rc = Obj::asUint64 (&result, INPUT); + rcUll = Obj::asUlonglong(&resultUll, INPUT); + + ASSERTV(LINE, rc, rcUll, rc == rcUll); + ASSERTV(LINE, result, resultUll, result == resultUll); if (1 > expected) { expected = 0; @@ -2758,11 +3111,18 @@ int main(int argc, char *argv[]) ASSERT_PASS(Obj::asUint64(&result, "0")); ASSERT_FAIL(Obj::asUint64(&result, "NaN")); + + { + unsigned long long result; + ASSERT_PASS(Obj::asUlonglong(&result, "0")); + ASSERT_FAIL(Obj::asUlonglong(&result, "NaN")); + } + } } break; case 4: { // -------------------------------------------------------------------- - // TESTING: decompose + // TESTING: 'decompose' // // Concerns: // 1. `isNegative` correctly returns whether the text begins with '-'. @@ -2821,22 +3181,22 @@ int main(int argc, char *argv[]) // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: decompose" - << "\n==================" << bsl::endl; + bsl::cout << "\nTESTING: 'decompose'" + << "\n====================" << bsl::endl; } const bool F = false; const bool T = true; struct Data { int d_line; - const char *d_text; // input text - bool d_isNeg; // is negative - const char *d_intPart; // integer digits - const char *d_fracPart; // fraction digits - bool d_isExpNeg; // is exponent negative - const char *d_expPart; // exponent digits - const char *d_sigPart; // significant digits - Int64 d_sigBias; // significant digits bias to exponent + const char *d_text_p; // input text + bool d_isNeg; // is negative + const char *d_intPart_p; // integer digits + const char *d_fracPart_p; // fraction digits + bool d_isExpNeg; // is exponent negative + const char *d_expPart_p; // exponent digits + const char *d_sigPart_p; // significant digits + Int64 d_sigBias; // significant digits bias to exponent } DATA[] = { // line, text, neg, int, frac,eN, exp, sigDig, bias // ---- ---- --- --- ---- -- --- ------ ---- @@ -2875,13 +3235,13 @@ int main(int argc, char *argv[]) if (verbose) bsl::cout << "\tTest a wide array of input." << bsl::endl; for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const char *TEXT = DATA[i].d_text; + const char *TEXT = DATA[i].d_text_p; const bool IS_NEG = DATA[i].d_isNeg; - const char *INT_PART = DATA[i].d_intPart; - const char *FRAC_PART = DATA[i].d_fracPart; + const char *INT_PART = DATA[i].d_intPart_p; + const char *FRAC_PART = DATA[i].d_fracPart_p; const bool IS_EXP_NEG = DATA[i].d_isExpNeg; - const char *EXP_PART = DATA[i].d_expPart; - const char *SIG_PART = DATA[i].d_sigPart; + const char *EXP_PART = DATA[i].d_expPart_p; + const char *SIG_PART = DATA[i].d_sigPart_p; const Int64 SIG_BIAS = DATA[i].d_sigBias; if (veryVerbose) { @@ -2972,7 +3332,7 @@ int main(int argc, char *argv[]) } break; case 3: { // -------------------------------------------------------------------- - // TESTING: isValidNumber + // TESTING: 'isValidNumber' // // Concerns: // 1. `isValidNumber` rejects text with preceding or trailing white @@ -3037,8 +3397,8 @@ int main(int argc, char *argv[]) // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nTESTING: isValidNumber" - << "\n======================" << bsl::endl; + bsl::cout << "\nTESTING: 'isValidNumber'" + << "\n========================" << bsl::endl; } const char *HUGE_INT = @@ -3065,7 +3425,7 @@ int main(int argc, char *argv[]) { struct { const int d_line; - const char *d_string; + const char *d_string_p; const bool d_isValid; } DATA[] = { // line string isValid @@ -3129,7 +3489,7 @@ int main(int argc, char *argv[]) for (int i = 0; i < int(sizeof(DATA) / sizeof(DATA[0])); ++i) { const int LINE = DATA[i].d_line; - const char *STRING = DATA[i].d_string; + const char *STRING = DATA[i].d_string_p; const bool IS_VALID = DATA[i].d_isValid; if (veryVerbose) { @@ -3141,6 +3501,7 @@ int main(int argc, char *argv[]) bsls::AlignmentUtil::roundUpToMaximalAlignment(STRLEN); char *block = static_cast( ga.allocate(paddedSize)); + char *firstProtectedAddress = block + paddedSize; char *data = firstProtectedAddress - STRLEN; @@ -3176,6 +3537,7 @@ int main(int argc, char *argv[]) bsls::AlignmentUtil::roundUpToMaximalAlignment(LENGTH); char *block = static_cast( ga.allocate(paddedSize)); + char *firstProtectedAddress = block + paddedSize; char *data = firstProtectedAddress - LENGTH; @@ -3198,7 +3560,7 @@ int main(int argc, char *argv[]) { // Recreates n_multidigit_number_then_00.json from JSONTestSuite const bsl::string_view input("123\0", 4); - bool rc = Obj::isValidNumber(input); + bool rc = Obj::isValidNumber(input); ASSERTV("embedded null char", false == rc); bool rcOr = oracle(input); @@ -3207,7 +3569,7 @@ int main(int argc, char *argv[]) } break; case 2: { // -------------------------------------------------------------------- - // CONCERN: Test Machinery + // CONCERN: TEST MACHINERY // // Concerns: // 1. That generateEquivalenceSet can correctly adjust the exponent @@ -3233,11 +3595,11 @@ int main(int argc, char *argv[]) // value in the range. // // Testing: - // CONCERN: Test Machinery + // CONCERN: TEST MACHINERY // -------------------------------------------------------------------- if (verbose) { - bsl::cout << "\nCONCERN: Test Machinery" + bsl::cout << "\nCONCERN: TEST MACHINERY" << "\n=======================" << bsl::endl; } @@ -3247,9 +3609,9 @@ int main(int argc, char *argv[]) struct Data { int d_line; - const char *d_input; + const char *d_input_p; int d_adjustment; - const char *d_expected; + const char *d_expected_p; } DATA[] = { { L_, "0", 0, "0e0" }, { L_, "0.0", 0, "0e0" }, @@ -3273,9 +3635,9 @@ int main(int argc, char *argv[]) const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); for (int i = 0; i < NUM_DATA; ++i) { const int LINE = DATA[i].d_line; - const char *INPUT = DATA[i].d_input; + const char *INPUT = DATA[i].d_input_p; const int ADJ = DATA[i].d_adjustment; - const char *EXP = DATA[i].d_expected; + const char *EXP = DATA[i].d_expected_p; if (veryVerbose) { P_(LINE); P_(INPUT); P_(ADJ); P(EXP); @@ -3366,8 +3728,8 @@ int main(int argc, char *argv[]) { struct { const int d_line; - const char *d_string; - const char *d_expectedString; + const char *d_string_p; + const char *d_expectedString_p; unsigned int d_typesFlags; } DATA[] = { // line string expect types @@ -3420,7 +3782,7 @@ int main(int argc, char *argv[]) , { L_, "-9e1", "-90", e_ALL_SIGNED_INT_TYPES } , { L_, "-10e1", "-100", e_ALL_SIGNED_INT_TYPES } - // simple FP values (w/ exponents) + // simple floating point values (w/ exponents) , { L_, "0.25e1", "2.5", e_ALL_FP_TYPES } , { L_, "0.5e1", "5", e_ALL_FP_TYPES } , { L_, "0.75e1", "7.5", e_ALL_FP_TYPES } @@ -3428,7 +3790,7 @@ int main(int argc, char *argv[]) , { L_, "-0.5e1", "-5", e_ALL_FP_TYPES } , { L_, "-0.75e1", "-7.5", e_ALL_FP_TYPES } - // decimal FP + // decimal floating point , { L_, "0.0", "0.0", e_DECIMAL64 } , { L_, "0.1", "0.1", e_DECIMAL64 } , { L_, "0.01e1", "0.1", e_DECIMAL64 } @@ -3476,11 +3838,15 @@ int main(int argc, char *argv[]) }; for (int i = 0; i < int(sizeof(DATA)/sizeof(DATA[0])); ++i) { - const int LINE = DATA[i].d_line; - const char *STRING = DATA[i].d_string; - const char *EXPECT = DATA[i].d_expectedString; + const int LINE = DATA[i].d_line; + const char *STRING = DATA[i].d_string_p; + const char *EXPECT = DATA[i].d_expectedString_p; const unsigned int TYPE_FLAGS = DATA[i].d_typesFlags; + if (veryVerbose) { + P_(LINE); P_(STRING); P_(EXPECT); P(TYPE_FLAGS); + } + #define TEST_TYPE(FLAG, TYPE, CAST_TYPE) \ if (TYPE_FLAGS & FLAG) { \ TYPE num; \ @@ -3528,7 +3894,7 @@ int main(int argc, char *argv[]) { struct { const int d_line; - const char *d_string; + const char *d_string_p; const bool d_isValid; } DATA[] = { // line string isValid @@ -3560,9 +3926,13 @@ int main(int argc, char *argv[]) for (int i = 0; i < int(sizeof(DATA) / sizeof(DATA[0])); ++i) { const int LINE = DATA[i].d_line; - const char *STRING = DATA[i].d_string; + const char *STRING = DATA[i].d_string_p; const bool IS_VALID = DATA[i].d_isValid; + if (veryVerbose) { + P_(LINE); P_(STRING); P(IS_VALID); + } + bool rc = Obj::isValidNumber(STRING); ASSERTV(LINE, STRING, rc, IS_VALID, IS_VALID == rc); } @@ -3573,7 +3943,7 @@ int main(int argc, char *argv[]) << bsl::endl; { Uint64 val = 1234567890; - int rc; + int rc; rc = Obj::asUint64(&val, "1"); ASSERTV(L_, rc, val, 0 == rc && 1 == val); @@ -3607,8 +3977,7 @@ int main(int argc, char *argv[]) ASSERTV(L_, rc, val, 0 == rc && 1200 == val); val = 1234567890; - // Note that - // https://www.rfc-editor.org/rfc/rfc8259#section-6 + // Note that https://www.rfc-editor.org/rfc/rfc8259#section-6 // allows for leading 0's after the [eE]. rc = Obj::asUint64(&val, "120.0000e-00001"); ASSERTV(L_, rc, val, 0 == rc && 12 == val); @@ -3630,7 +3999,7 @@ int main(int argc, char *argv[]) } break; case -1: { //--------------------------------------------------------------------- - // BENCHMARK: asDecimal64Exact vs asDecimal64ExactOracle + // BENCHMARK: 'asDecimal64Exact' VS 'asDecimal64ExactOracle' // // This benchmark compares the current `asDecimal64Exact` function // which delegates entirely to `inteldfp` parsing, to a "homebrewn" @@ -3640,11 +4009,9 @@ int main(int argc, char *argv[]) //--------------------------------------------------------------------- if (verbose) { - bsl::cout - << bsl::endl - << "BENCHMARK: asDecimal64Exact vs asDecimal64ExactOracle\n" - << "=====================================================\n" - << bsl::endl; + bsl::cout << + "\n" "BENCHMARK: 'asDecimal64Exact' VS 'asDecimal64ExactOracle'" "\n" + "=========================================================" "\n"; } const char *DATA[] = { @@ -3675,6 +4042,7 @@ int main(int argc, char *argv[]) //"1238414121.14134242341543", //"18446744073709551615", }; + const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); int numIterations = 100000;