Skip to content

Commit 60ccc1f

Browse files
evalon32baylesj
andauthored
feat: support std::string_view in Value API (#1584)
This adds direct support for `std::string_view` when available (C++17 and above). The current API can be used with `std::string_view` via the low-level two-pointer methods, but is not ergonomic. E.g., compare: ``` Json::Value node; std::string foo, bar, baz; std::string_view foo_sv, bar_sv, baz_sv; // Efficient & readable: node[foo][bar][baz]; // Less efficient, less readable: node[std::string(foo_sv)][std::string(bar_sv)][std::string(baz_sv)]; // Efficient, but a lot less readable: *node.demand(foo_sv.data(), foo_sv.data() + foo_sv.size()) ->demand(bar_sv.data(), bar_sv.data() + bar_sv.size()) ->demand(baz_sv.data(), baz_sv.data() + baz_sv.size()) // After this change, efficient & readable: node[foo_sv][bar_sv][baz_sv]; ``` * The constructor can take a `std::string_view` parameter. The existing overloads taking `const std::string&` and `const char*` are still necessary to support assignment from those types. * `operator[]`, `get()`, `isMember()` and `removeMember()` take a `std::string_view` parameter. This supersedes the overloads taking `const std::string&` and `const char*`. The overloads taking a pair of pointers (begin, end) are preserved for source compatibility. * `getString()` has an overload with a `std::string_view` output parameter. The one with a pair of pointers is preserved for source compatibility. Signed-off-by: Lev Kandel <[email protected]> Co-authored-by: Jordan Bayles <[email protected]>
1 parent 07a8fe6 commit 60ccc1f

File tree

2 files changed

+125
-7
lines changed

2 files changed

+125
-7
lines changed

include/json/value.h

+54-7
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,21 @@
3939
#endif
4040
#endif
4141

42+
#if __cplusplus >= 201703L
43+
#define JSONCPP_HAS_STRING_VIEW 1
44+
#endif
45+
4246
#include <array>
4347
#include <exception>
4448
#include <map>
4549
#include <memory>
4650
#include <string>
4751
#include <vector>
4852

53+
#ifdef JSONCPP_HAS_STRING_VIEW
54+
#include <string_view>
55+
#endif
56+
4957
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
5058
// be used by...
5159
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
@@ -342,6 +350,9 @@ class JSON_API Value {
342350
*/
343351
Value(const StaticString& value);
344352
Value(const String& value);
353+
#ifdef JSONCPP_HAS_STRING_VIEW
354+
Value(std::string_view value);
355+
#endif
345356
Value(bool value);
346357
Value(std::nullptr_t ptr) = delete;
347358
Value(const Value& other);
@@ -384,6 +395,12 @@ class JSON_API Value {
384395
* \return false if !string. (Seg-fault if str or end are NULL.)
385396
*/
386397
bool getString(char const** begin, char const** end) const;
398+
#ifdef JSONCPP_HAS_STRING_VIEW
399+
/** Get string_view of string-value.
400+
* \return false if !string. (Seg-fault if str is NULL.)
401+
*/
402+
bool getString(std::string_view* str) const;
403+
#endif
387404
Int asInt() const;
388405
UInt asUInt() const;
389406
#if defined(JSON_HAS_INT64)
@@ -470,6 +487,15 @@ class JSON_API Value {
470487
bool insert(ArrayIndex index, const Value& newValue);
471488
bool insert(ArrayIndex index, Value&& newValue);
472489

490+
#ifdef JSONCPP_HAS_STRING_VIEW
491+
/// Access an object value by name, create a null member if it does not exist.
492+
/// \param key may contain embedded nulls.
493+
Value& operator[](std::string_view key);
494+
/// Access an object value by name, returns null if there is no member with
495+
/// that name.
496+
/// \param key may contain embedded nulls.
497+
const Value& operator[](std::string_view key) const;
498+
#else
473499
/// Access an object value by name, create a null member if it does not exist.
474500
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
475501
/// Exceeding that will cause an exception.
@@ -484,6 +510,7 @@ class JSON_API Value {
484510
/// that name.
485511
/// \param key may contain embedded nulls.
486512
const Value& operator[](const String& key) const;
513+
#endif
487514
/** \brief Access an object value by name, create a null member if it does not
488515
* exist.
489516
*
@@ -497,18 +524,24 @@ class JSON_API Value {
497524
* \endcode
498525
*/
499526
Value& operator[](const StaticString& key);
527+
#ifdef JSONCPP_HAS_STRING_VIEW
500528
/// Return the member named key if it exist, defaultValue otherwise.
501529
/// \note deep copy
502-
Value get(const char* key, const Value& defaultValue) const;
530+
Value get(std::string_view key, const Value& defaultValue) const;
531+
#else
503532
/// Return the member named key if it exist, defaultValue otherwise.
504533
/// \note deep copy
505-
/// \note key may contain embedded nulls.
506-
Value get(const char* begin, const char* end,
507-
const Value& defaultValue) const;
534+
Value get(const char* key, const Value& defaultValue) const;
508535
/// Return the member named key if it exist, defaultValue otherwise.
509536
/// \note deep copy
510537
/// \param key may contain embedded nulls.
511538
Value get(const String& key, const Value& defaultValue) const;
539+
#endif
540+
/// Return the member named key if it exist, defaultValue otherwise.
541+
/// \note deep copy
542+
/// \note key may contain embedded nulls.
543+
Value get(const char* begin, const char* end,
544+
const Value& defaultValue) const;
512545
/// Most general and efficient version of isMember()const, get()const,
513546
/// and operator[]const
514547
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
@@ -525,20 +558,28 @@ class JSON_API Value {
525558
/// Do nothing if it did not exist.
526559
/// \pre type() is objectValue or nullValue
527560
/// \post type() is unchanged
561+
#if JSONCPP_HAS_STRING_VIEW
562+
void removeMember(std::string_view key);
563+
#else
528564
void removeMember(const char* key);
529565
/// Same as removeMember(const char*)
530566
/// \param key may contain embedded nulls.
531567
void removeMember(const String& key);
532-
/// Same as removeMember(const char* begin, const char* end, Value* removed),
533-
/// but 'key' is null-terminated.
534-
bool removeMember(const char* key, Value* removed);
568+
#endif
535569
/** \brief Remove the named map member.
536570
*
537571
* Update 'removed' iff removed.
538572
* \param key may contain embedded nulls.
539573
* \return true iff removed (no exceptions)
540574
*/
575+
#if JSONCPP_HAS_STRING_VIEW
576+
bool removeMember(std::string_view key, Value* removed);
577+
#else
541578
bool removeMember(String const& key, Value* removed);
579+
/// Same as removeMember(const char* begin, const char* end, Value* removed),
580+
/// but 'key' is null-terminated.
581+
bool removeMember(const char* key, Value* removed);
582+
#endif
542583
/// Same as removeMember(String const& key, Value* removed)
543584
bool removeMember(const char* begin, const char* end, Value* removed);
544585
/** \brief Remove the indexed array element.
@@ -549,12 +590,18 @@ class JSON_API Value {
549590
*/
550591
bool removeIndex(ArrayIndex index, Value* removed);
551592

593+
#ifdef JSONCPP_HAS_STRING_VIEW
594+
/// Return true if the object has a member named key.
595+
/// \param key may contain embedded nulls.
596+
bool isMember(std::string_view key) const;
597+
#else
552598
/// Return true if the object has a member named key.
553599
/// \note 'key' must be null-terminated.
554600
bool isMember(const char* key) const;
555601
/// Return true if the object has a member named key.
556602
/// \param key may contain embedded nulls.
557603
bool isMember(const String& key) const;
604+
#endif
558605
/// Same as isMember(String const& key)const
559606
bool isMember(const char* begin, const char* end) const;
560607

src/lib_json/json_value.cpp

+71
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
#include <sstream>
1818
#include <utility>
1919

20+
#ifdef JSONCPP_HAS_STRING_VIEW
21+
#include <string_view>
22+
#endif
23+
2024
// Provide implementation equivalent of std::snprintf for older _MSC compilers
2125
#if defined(_MSC_VER) && _MSC_VER < 1900
2226
#include <stdarg.h>
@@ -420,6 +424,14 @@ Value::Value(const String& value) {
420424
value.data(), static_cast<unsigned>(value.length()));
421425
}
422426

427+
#ifdef JSONCPP_HAS_STRING_VIEW
428+
Value::Value(std::string_view value) {
429+
initBasic(stringValue, true);
430+
value_.string_ = duplicateAndPrefixStringValue(
431+
value.data(), static_cast<unsigned>(value.length()));
432+
}
433+
#endif
434+
423435
Value::Value(const StaticString& value) {
424436
initBasic(stringValue);
425437
value_.string_ = const_cast<char*>(value.c_str());
@@ -627,6 +639,21 @@ bool Value::getString(char const** begin, char const** end) const {
627639
return true;
628640
}
629641

642+
#ifdef JSONCPP_HAS_STRING_VIEW
643+
bool Value::getString(std::string_view* str) const {
644+
if (type() != stringValue)
645+
return false;
646+
if (value_.string_ == nullptr)
647+
return false;
648+
const char* begin;
649+
unsigned length;
650+
decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
651+
&begin);
652+
*str = std::string_view(begin, length);
653+
return true;
654+
}
655+
#endif
656+
630657
String Value::asString() const {
631658
switch (type()) {
632659
case nullValue:
@@ -1108,6 +1135,17 @@ Value* Value::demand(char const* begin, char const* end) {
11081135
"objectValue or nullValue");
11091136
return &resolveReference(begin, end);
11101137
}
1138+
#ifdef JSONCPP_HAS_STRING_VIEW
1139+
const Value& Value::operator[](std::string_view key) const {
1140+
Value const* found = find(key.data(), key.data() + key.length());
1141+
if (!found)
1142+
return nullSingleton();
1143+
return *found;
1144+
}
1145+
Value& Value::operator[](std::string_view key) {
1146+
return resolveReference(key.data(), key.data() + key.length());
1147+
}
1148+
#else
11111149
const Value& Value::operator[](const char* key) const {
11121150
Value const* found = find(key, key + strlen(key));
11131151
if (!found)
@@ -1128,6 +1166,7 @@ Value& Value::operator[](const char* key) {
11281166
Value& Value::operator[](const String& key) {
11291167
return resolveReference(key.data(), key.data() + key.length());
11301168
}
1169+
#endif
11311170

11321171
Value& Value::operator[](const StaticString& key) {
11331172
return resolveReference(key.c_str());
@@ -1167,12 +1206,18 @@ Value Value::get(char const* begin, char const* end,
11671206
Value const* found = find(begin, end);
11681207
return !found ? defaultValue : *found;
11691208
}
1209+
#ifdef JSONCPP_HAS_STRING_VIEW
1210+
Value Value::get(std::string_view key, const Value& defaultValue) const {
1211+
return get(key.data(), key.data() + key.length(), defaultValue);
1212+
}
1213+
#else
11701214
Value Value::get(char const* key, Value const& defaultValue) const {
11711215
return get(key, key + strlen(key), defaultValue);
11721216
}
11731217
Value Value::get(String const& key, Value const& defaultValue) const {
11741218
return get(key.data(), key.data() + key.length(), defaultValue);
11751219
}
1220+
#endif
11761221

11771222
bool Value::removeMember(const char* begin, const char* end, Value* removed) {
11781223
if (type() != objectValue) {
@@ -1188,12 +1233,31 @@ bool Value::removeMember(const char* begin, const char* end, Value* removed) {
11881233
value_.map_->erase(it);
11891234
return true;
11901235
}
1236+
#ifdef JSONCPP_HAS_STRING_VIEW
1237+
bool Value::removeMember(std::string_view key, Value* removed) {
1238+
return removeMember(key.data(), key.data() + key.length(), removed);
1239+
}
1240+
#else
11911241
bool Value::removeMember(const char* key, Value* removed) {
11921242
return removeMember(key, key + strlen(key), removed);
11931243
}
11941244
bool Value::removeMember(String const& key, Value* removed) {
11951245
return removeMember(key.data(), key.data() + key.length(), removed);
11961246
}
1247+
#endif
1248+
1249+
#ifdef JSONCPP_HAS_STRING_VIEW
1250+
void Value::removeMember(std::string_view key) {
1251+
JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
1252+
"in Json::Value::removeMember(): requires objectValue");
1253+
if (type() == nullValue)
1254+
return;
1255+
1256+
CZString actualKey(key.data(), unsigned(key.length()),
1257+
CZString::noDuplication);
1258+
value_.map_->erase(actualKey);
1259+
}
1260+
#else
11971261
void Value::removeMember(const char* key) {
11981262
JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
11991263
"in Json::Value::removeMember(): requires objectValue");
@@ -1204,6 +1268,7 @@ void Value::removeMember(const char* key) {
12041268
value_.map_->erase(actualKey);
12051269
}
12061270
void Value::removeMember(const String& key) { removeMember(key.c_str()); }
1271+
#endif
12071272

12081273
bool Value::removeIndex(ArrayIndex index, Value* removed) {
12091274
if (type() != arrayValue) {
@@ -1233,12 +1298,18 @@ bool Value::isMember(char const* begin, char const* end) const {
12331298
Value const* value = find(begin, end);
12341299
return nullptr != value;
12351300
}
1301+
#ifdef JSONCPP_HAS_STRING_VIEW
1302+
bool Value::isMember(std::string_view key) const {
1303+
return isMember(key.data(), key.data() + key.length());
1304+
}
1305+
#else
12361306
bool Value::isMember(char const* key) const {
12371307
return isMember(key, key + strlen(key));
12381308
}
12391309
bool Value::isMember(String const& key) const {
12401310
return isMember(key.data(), key.data() + key.length());
12411311
}
1312+
#endif
12421313

12431314
Value::Members Value::getMemberNames() const {
12441315
JSON_ASSERT_MESSAGE(

0 commit comments

Comments
 (0)