Skip to content

Commit 3b927e4

Browse files
committed
fix: inline std::string_view methods to prevent ABI breaks
This commit completely eliminates the ABI breakage that occurs across C++ standard boundaries when using `std::string_view`. Previously, when the library was built with C++17+, CMake would leak `JSONCPP_HAS_STRING_VIEW=1` as a PUBLIC definition. A C++11 consumer would receive this definition, attempt to parse the header, and fail with compiler errors because `std::string_view` is not available in their environment. Conversely, if the library was built in C++11 (without `string_view` symbols), a C++17 consumer would naturally define `JSONCPP_HAS_STRING_VIEW` based on `__cplusplus` inside `value.h`. The consumer would then call the declared `string_view` methods, resulting in linker errors because the methods weren't compiled into the library. By moving all `std::string_view` overloads directly into `value.h` as `inline` methods that delegate to the fundamental `const char*, const char*` methods: 1. The consumer's compiler dictates whether the overloads are visible (via `__cplusplus >= 201703L`). 2. The consumer compiles the inline wrappers locally, removing any reliance on the library's exported symbols for `std::string_view`. 3. CMake no longer needs to pollute the consumer's environment with PUBLIC compile definitions.
1 parent 60149bc commit 3b927e4

3 files changed

Lines changed: 31 additions & 76 deletions

File tree

include/json/value.h

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ class JSON_API Value {
357357
Value(const StaticString& value);
358358
Value(const String& value);
359359
#ifdef JSONCPP_HAS_STRING_VIEW
360-
Value(std::string_view value);
360+
inline Value(std::string_view value)
361+
: Value(value.data(), value.data() + value.length()) {}
361362
#endif
362363
Value(bool value);
363364
Value(std::nullptr_t ptr) = delete;
@@ -405,7 +406,14 @@ class JSON_API Value {
405406
/** Get string_view of string-value.
406407
* \return false if !string. (Seg-fault if str is NULL.)
407408
*/
408-
bool getString(std::string_view* str) const;
409+
inline bool getString(std::string_view* str) const {
410+
char const* begin;
411+
char const* end;
412+
if (!getString(&begin, &end))
413+
return false;
414+
*str = std::string_view(begin, static_cast<size_t>(end - begin));
415+
return true;
416+
}
409417
#endif
410418
Int asInt() const;
411419
UInt asUInt() const;
@@ -496,11 +504,18 @@ class JSON_API Value {
496504
#ifdef JSONCPP_HAS_STRING_VIEW
497505
/// Access an object value by name, create a null member if it does not exist.
498506
/// \param key may contain embedded nulls.
499-
Value& operator[](std::string_view key);
507+
inline Value& operator[](std::string_view key) {
508+
return resolveReference(key.data(), key.data() + key.length());
509+
}
500510
/// Access an object value by name, returns null if there is no member with
501511
/// that name.
502512
/// \param key may contain embedded nulls.
503-
const Value& operator[](std::string_view key) const;
513+
inline const Value& operator[](std::string_view key) const {
514+
Value const* found = find(key.data(), key.data() + key.length());
515+
if (!found)
516+
return nullSingleton();
517+
return *found;
518+
}
504519
#endif
505520
/// Access an object value by name, create a null member if it does not exist.
506521
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
@@ -532,7 +547,9 @@ class JSON_API Value {
532547
#ifdef JSONCPP_HAS_STRING_VIEW
533548
/// Return the member named key if it exist, defaultValue otherwise.
534549
/// \note deep copy
535-
Value get(std::string_view key, const Value& defaultValue) const;
550+
inline Value get(std::string_view key, const Value& defaultValue) const {
551+
return get(key.data(), key.data() + key.length(), defaultValue);
552+
}
536553
#endif
537554
/// Return the member named key if it exist, defaultValue otherwise.
538555
/// \note deep copy
@@ -586,7 +603,9 @@ class JSON_API Value {
586603
/// \pre type() is objectValue or nullValue
587604
/// \post type() is unchanged
588605
#if JSONCPP_HAS_STRING_VIEW
589-
void removeMember(std::string_view key);
606+
inline void removeMember(std::string_view key) {
607+
removeMember(key.data(), key.data() + key.length(), nullptr);
608+
}
590609
#endif
591610
void removeMember(const char* key);
592611
/// Same as removeMember(const char*)
@@ -599,7 +618,9 @@ class JSON_API Value {
599618
* \return true iff removed (no exceptions)
600619
*/
601620
#if JSONCPP_HAS_STRING_VIEW
602-
bool removeMember(std::string_view key, Value* removed);
621+
inline bool removeMember(std::string_view key, Value* removed) {
622+
return removeMember(key.data(), key.data() + key.length(), removed);
623+
}
603624
#endif
604625
bool removeMember(String const& key, Value* removed);
605626
/// Same as removeMember(const char* begin, const char* end, Value* removed),
@@ -618,7 +639,9 @@ class JSON_API Value {
618639
#ifdef JSONCPP_HAS_STRING_VIEW
619640
/// Return true if the object has a member named key.
620641
/// \param key may contain embedded nulls.
621-
bool isMember(std::string_view key) const;
642+
inline bool isMember(std::string_view key) const {
643+
return isMember(key.data(), key.data() + key.length());
644+
}
622645
#endif
623646
/// Return true if the object has a member named key.
624647
/// \note 'key' must be null-terminated.

src/lib_json/CMakeLists.txt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,6 @@ if(BUILD_SHARED_LIBS)
131131

132132
target_compile_features(${SHARED_LIB} PUBLIC ${REQUIRED_FEATURES})
133133

134-
if(JSONCPP_HAS_STRING_VIEW)
135-
target_compile_definitions(${SHARED_LIB} PUBLIC JSONCPP_HAS_STRING_VIEW=1)
136-
endif()
137134

138135
target_include_directories(${SHARED_LIB} PUBLIC
139136
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
@@ -168,9 +165,6 @@ if(BUILD_STATIC_LIBS)
168165

169166
target_compile_features(${STATIC_LIB} PUBLIC ${REQUIRED_FEATURES})
170167

171-
if(JSONCPP_HAS_STRING_VIEW)
172-
target_compile_definitions(${STATIC_LIB} PUBLIC JSONCPP_HAS_STRING_VIEW=1)
173-
endif()
174168

175169
target_include_directories(${STATIC_LIB} PUBLIC
176170
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
@@ -198,9 +192,6 @@ if(BUILD_OBJECT_LIBS)
198192

199193
target_compile_features(${OBJECT_LIB} PUBLIC ${REQUIRED_FEATURES})
200194

201-
if(JSONCPP_HAS_STRING_VIEW)
202-
target_compile_definitions(${OBJECT_LIB} PUBLIC JSONCPP_HAS_STRING_VIEW=1)
203-
endif()
204195

205196
target_include_directories(${OBJECT_LIB} PUBLIC
206197
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>

src/lib_json/json_value.cpp

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -441,13 +441,6 @@ Value::Value(const String& value) {
441441
value.data(), static_cast<unsigned>(value.length()));
442442
}
443443

444-
#ifdef JSONCPP_HAS_STRING_VIEW
445-
Value::Value(std::string_view value) {
446-
initBasic(stringValue, true);
447-
value_.string_ = duplicateAndPrefixStringValue(
448-
value.data(), static_cast<unsigned>(value.length()));
449-
}
450-
#endif
451444

452445
Value::Value(const StaticString& value) {
453446
initBasic(stringValue);
@@ -656,20 +649,6 @@ bool Value::getString(char const** begin, char const** end) const {
656649
return true;
657650
}
658651

659-
#ifdef JSONCPP_HAS_STRING_VIEW
660-
bool Value::getString(std::string_view* str) const {
661-
if (type() != stringValue)
662-
return false;
663-
if (value_.string_ == nullptr)
664-
return false;
665-
const char* begin;
666-
unsigned length;
667-
decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
668-
&begin);
669-
*str = std::string_view(begin, length);
670-
return true;
671-
}
672-
#endif
673652

674653
String Value::asString() const {
675654
switch (type()) {
@@ -1190,17 +1169,6 @@ Value* Value::demand(char const* begin, char const* end) {
11901169
"objectValue or nullValue");
11911170
return &resolveReference(begin, end);
11921171
}
1193-
#ifdef JSONCPP_HAS_STRING_VIEW
1194-
const Value& Value::operator[](std::string_view key) const {
1195-
Value const* found = find(key.data(), key.data() + key.length());
1196-
if (!found)
1197-
return nullSingleton();
1198-
return *found;
1199-
}
1200-
Value& Value::operator[](std::string_view key) {
1201-
return resolveReference(key.data(), key.data() + key.length());
1202-
}
1203-
#endif
12041172
const Value& Value::operator[](const char* key) const {
12051173
Value const* found = find(key, key + strlen(key));
12061174
if (!found)
@@ -1260,11 +1228,6 @@ Value Value::get(char const* begin, char const* end,
12601228
Value const* found = find(begin, end);
12611229
return !found ? defaultValue : *found;
12621230
}
1263-
#ifdef JSONCPP_HAS_STRING_VIEW
1264-
Value Value::get(std::string_view key, const Value& defaultValue) const {
1265-
return get(key.data(), key.data() + key.length(), defaultValue);
1266-
}
1267-
#endif
12681231
Value Value::get(char const* key, Value const& defaultValue) const {
12691232
return get(key, key + strlen(key), defaultValue);
12701233
}
@@ -1286,30 +1249,13 @@ bool Value::removeMember(const char* begin, const char* end, Value* removed) {
12861249
value_.map_->erase(it);
12871250
return true;
12881251
}
1289-
#ifdef JSONCPP_HAS_STRING_VIEW
1290-
bool Value::removeMember(std::string_view key, Value* removed) {
1291-
return removeMember(key.data(), key.data() + key.length(), removed);
1292-
}
1293-
#endif
12941252
bool Value::removeMember(const char* key, Value* removed) {
12951253
return removeMember(key, key + strlen(key), removed);
12961254
}
12971255
bool Value::removeMember(String const& key, Value* removed) {
12981256
return removeMember(key.data(), key.data() + key.length(), removed);
12991257
}
13001258

1301-
#ifdef JSONCPP_HAS_STRING_VIEW
1302-
void Value::removeMember(std::string_view key) {
1303-
JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
1304-
"in Json::Value::removeMember(): requires objectValue");
1305-
if (type() == nullValue)
1306-
return;
1307-
1308-
CZString actualKey(key.data(), unsigned(key.length()),
1309-
CZString::noDuplication);
1310-
value_.map_->erase(actualKey);
1311-
}
1312-
#endif
13131259
void Value::removeMember(const char* key) {
13141260
JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
13151261
"in Json::Value::removeMember(): requires objectValue");
@@ -1349,11 +1295,6 @@ bool Value::isMember(char const* begin, char const* end) const {
13491295
Value const* value = find(begin, end);
13501296
return nullptr != value;
13511297
}
1352-
#ifdef JSONCPP_HAS_STRING_VIEW
1353-
bool Value::isMember(std::string_view key) const {
1354-
return isMember(key.data(), key.data() + key.length());
1355-
}
1356-
#endif
13571298
bool Value::isMember(char const* key) const {
13581299
return isMember(key, key + strlen(key));
13591300
}

0 commit comments

Comments
 (0)