Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowing users to write directly to std::optional<std::string> #2078

Merged
merged 6 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 17 additions & 4 deletions doc/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ An overview of what you need to know to use simdjson, with examples.
- [Dynamic Number Types](#dynamic-number-types)
- [Raw Strings](#raw-strings)
- [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)
- [Storing Directly into an Existing String Instance](#storing-directly-into-an-existing-string-instance)
- [Thread Safety](#thread-safety)
- [Standard Compliance](#standard-compliance)
- [Backwards Compatibility](#backwards-compatibility)
Expand Down Expand Up @@ -1751,13 +1752,13 @@ string representation.
```


Storing Directly into an Existing std::string Instance
Storing Directly into an Existing String Instance
-----------------------------------------------------

The simdjson library favours the use of `std::string_view` instances because
it tends to lead to better performance due to causing fewer memory allocations.
However, they are cases where you need to store a string result in an `std::string``
instance. You can do so with a version of the `to_string()` method which takes as
instance. You can do so with a templated version of the `to_string()` method which takes as
a parameter a reference to an `std::string`.

```C++
Expand All @@ -1779,10 +1780,22 @@ The same routine can be written without exceptions handling:
if(err) { /* handle error */ }
```

The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances, it does not point at data that is
within our `parser` instance. The same caveat applies: you should
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
it does not point at data that is within our `parser` instance. The same caveat applies: you should
only consume a JSON string once.

Because `get_string()` is a template that requires a type that can be assigned an `std::string`, you
can use it with features such as `std::optional`:

```C++
auto json = R"({ "foo1": "3.1416" } )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::optional<std::string> value;
if(doc["foo1"].get_string(value)) { /* error */ }
// value was populated with "3.1416"
```

You should be mindful of the trade-off: allocating multiple
`std::string` instances can become expensive.

Expand Down
12 changes: 8 additions & 4 deletions include/simdjson/generic/ondemand/document-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ simdjson_inline simdjson_result<double> document::get_double_in_string() noexcep
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
simdjson_inline error_code document::get_string(std::string& receiver, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
Expand Down Expand Up @@ -400,7 +401,8 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(std::string& receiver, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
Expand Down Expand Up @@ -590,7 +592,8 @@ simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
simdjson_inline error_code document_reference::get_string(std::string& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
template <typename string_type>
simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
Expand Down Expand Up @@ -727,7 +730,8 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(std::string& receiver, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
Expand Down
12 changes: 8 additions & 4 deletions include/simdjson/generic/ondemand/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ class document {
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
Expand Down Expand Up @@ -638,7 +639,8 @@ class document_reference {
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
Expand Down Expand Up @@ -708,7 +710,8 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> : public SIM
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
Expand Down Expand Up @@ -781,7 +784,8 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference> :
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
Expand Down
6 changes: 4 additions & 2 deletions include/simdjson/generic/ondemand/value-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() no
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
simdjson_inline error_code value::get_string(std::string& receiver, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
Expand Down Expand Up @@ -342,7 +343,8 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_string(std::string& receiver, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
Expand Down
6 changes: 4 additions & 2 deletions include/simdjson/generic/ondemand/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ class value {
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;

/**
* Cast this JSON value to a "wobbly" string.
Expand Down Expand Up @@ -631,7 +632,8 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> : public SIMDJS
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
Expand Down
10 changes: 6 additions & 4 deletions include/simdjson/generic/ondemand/value_iterator-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -513,11 +513,12 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(std::string& receiver, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
receiver = std::string(content);
receiver = content;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
Expand Down Expand Up @@ -643,11 +644,12 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(std::string& receiver, bool check_trailing, bool allow_replacement) noexcept {
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
receiver = std::string(content);
receiver = content;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
Expand Down
6 changes: 4 additions & 2 deletions include/simdjson/generic/ondemand/value_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ class value_iterator {
*/

simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
Expand All @@ -313,7 +314,8 @@ class value_iterator {
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;

simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline error_code get_root_string(std::string& receiver, bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
Expand Down
22 changes: 21 additions & 1 deletion tests/ondemand/ondemand_readme_examples.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "simdjson.h"
#include "test_ondemand.h"

#if __cpp_lib_optional >= 201606L
#include <optional>
#endif
using namespace std;
using namespace simdjson;
using error_code=simdjson::error_code;
Expand Down Expand Up @@ -1481,6 +1483,23 @@ bool example1958() {
}


bool to_optional() {
TEST_START();
auto json = R"({ "foo1": "3.1416" } )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
#if __cpp_lib_optional >= 201606L
std::optional<std::string> value;
ASSERT_SUCCESS(doc["foo1"].get_string(value));
std::cout << value.value() << std::endl;
#else
std::string value;
ASSERT_SUCCESS(doc["foo1"].get_string(value));
std::cout << value << std::endl;
#endif
TEST_SUCCEED();
}

bool value_raw_json_array() {
TEST_START();
auto json = R"( [1,2,"fds", {"a":1}, [1,344]] )"_padded;
Expand Down Expand Up @@ -1514,6 +1533,7 @@ bool value_raw_json_object() {
bool run() {
return true
#if SIMDJSON_EXCEPTIONS
&& to_optional()
&& value_raw_json_array() && value_raw_json_object()
&& gen_raw1() && gen_raw2() && gen_raw3()
&& at_end()
Expand Down