diff --git a/docs/mkdocs/docs/examples/custom_array_type.cpp b/docs/mkdocs/docs/examples/custom_array_type.cpp new file mode 100644 index 000000000..63651cadf --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_array_type.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +#include "custom_array_type.hpp" + +using custom_json = nlohmann::basic_json; + +int main() +{ + custom_json j = custom_json::array(); + j.push_back(1); + j.push_back(2); + j.push_back(3); + + std::cout << j.dump() << std::endl; + std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << std::endl; +} diff --git a/docs/mkdocs/docs/examples/custom_array_type.hpp b/docs/mkdocs/docs/examples/custom_array_type.hpp new file mode 100644 index 000000000..750d52a81 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_array_type.hpp @@ -0,0 +1,152 @@ +#pragma once + +#include +#include +#include + +// A minimal, self-contained ArrayType built around a private std::vector. +// See https://json.nlohmann.me/features/types/template_parameters/#arraytype +template> +class custom_array_type +{ + using vector_t = std::vector; + vector_t data_; + + public: + using value_type = typename vector_t::value_type; + using size_type = typename vector_t::size_type; + using iterator = typename vector_t::iterator; + using const_iterator = typename vector_t::const_iterator; + + custom_array_type() = default; + custom_array_type(const custom_array_type&) = default; + custom_array_type(custom_array_type&&) = default; + custom_array_type& operator=(const custom_array_type&) = default; + custom_array_type& operator=(custom_array_type&&) = default; + + template + custom_array_type(InputIt first, InputIt last) : data_(first, last) {} + + custom_array_type(size_type count, const T& value) : data_(count, value) {} + + iterator begin() + { + return data_.begin(); + } + iterator end() + { + return data_.end(); + } + const_iterator begin() const + { + return data_.begin(); + } + const_iterator end() const + { + return data_.end(); + } + const_iterator cbegin() const + { + return data_.cbegin(); + } + const_iterator cend() const + { + return data_.cend(); + } + + bool empty() const + { + return data_.empty(); + } + size_type size() const + { + return data_.size(); + } + size_type max_size() const + { + return data_.max_size(); + } + void clear() + { + data_.clear(); + } + void resize(size_type n) + { + data_.resize(n); + } + + T& operator[](size_type pos) + { + return data_[pos]; + } + const T& operator[](size_type pos) const + { + return data_[pos]; + } + + T& back() + { + return data_.back(); + } + const T& back() const + { + return data_.back(); + } + + void push_back(const T& value) + { + data_.push_back(value); + } + void push_back(T&& value) + { + data_.push_back(std::move(value)); + } + + template + void emplace_back(Args&& ... args) + { + data_.emplace_back(std::forward(args)...); + } + + void pop_back() + { + data_.pop_back(); + } + + iterator insert(const_iterator pos, const T& value) + { + return data_.insert(pos, value); + } + iterator insert(const_iterator pos, size_type count, const T& value) + { + return data_.insert(pos, count, value); + } + template + iterator insert(const_iterator pos, InputIt first, InputIt last) + { + return data_.insert(pos, first, last); + } + + iterator erase(const_iterator pos) + { + return data_.erase(pos); + } + iterator erase(const_iterator first, const_iterator last) + { + return data_.erase(first, last); + } + + void swap(custom_array_type& other) + { + data_.swap(other.data_); + } + + friend bool operator==(const custom_array_type& lhs, const custom_array_type& rhs) + { + return lhs.data_ == rhs.data_; + } + friend bool operator<(const custom_array_type& lhs, const custom_array_type& rhs) + { + return lhs.data_ < rhs.data_; + } +}; diff --git a/docs/mkdocs/docs/examples/custom_array_type.output b/docs/mkdocs/docs/examples/custom_array_type.output new file mode 100644 index 000000000..f5ceaa555 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_array_type.output @@ -0,0 +1,2 @@ +[1,2,3] +true diff --git a/docs/mkdocs/docs/examples/custom_binary_type.cpp b/docs/mkdocs/docs/examples/custom_binary_type.cpp new file mode 100644 index 000000000..cea34ea38 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_binary_type.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include + +#include + +#include "custom_binary_type.hpp" + +using custom_json = nlohmann::basic_json; + +int main() +{ + const auto j = custom_json::binary({0x01, 0x02, 0x03}); + + std::cout << j.dump() << std::endl; + std::cout << std::boolalpha << (custom_json::from_cbor(custom_json::to_cbor(j)) == j) << std::endl; +} diff --git a/docs/mkdocs/docs/examples/custom_binary_type.hpp b/docs/mkdocs/docs/examples/custom_binary_type.hpp new file mode 100644 index 000000000..77b4ad49a --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_binary_type.hpp @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include + +// A minimal, self-contained BinaryType built around a private std::vector. +// See https://json.nlohmann.me/features/types/template_parameters/#binarytype +class custom_binary_type +{ + using vector_t = std::vector; + vector_t data_; + + public: + using value_type = vector_t::value_type; + using size_type = vector_t::size_type; + using iterator = vector_t::iterator; + using const_iterator = vector_t::const_iterator; + + custom_binary_type() = default; + custom_binary_type(const custom_binary_type&) = default; + custom_binary_type(custom_binary_type&&) = default; + custom_binary_type& operator=(const custom_binary_type&) = default; + custom_binary_type& operator=(custom_binary_type&&) = default; + + template + custom_binary_type(InputIt first, InputIt last) : data_(first, last) {} + + // so basic_json::binary({0x01, 0x02}) can build one directly + custom_binary_type(std::initializer_list init) : data_(init) {} + + size_type size() const + { + return data_.size(); + } + bool empty() const + { + return data_.empty(); + } + void clear() + { + data_.clear(); + } + void resize(size_type n) + { + data_.resize(n); + } + + // read-only is enough: the writers only ever read from a binary value + const std::uint8_t* data() const + { + return data_.data(); + } + + std::uint8_t& operator[](size_type pos) + { + return data_[pos]; + } + std::uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + std::uint8_t& back() + { + return data_.back(); + } + std::uint8_t back() const + { + return data_.back(); + } + + iterator begin() + { + return data_.begin(); + } + iterator end() + { + return data_.end(); + } + const_iterator begin() const + { + return data_.begin(); + } + const_iterator end() const + { + return data_.end(); + } + const_iterator cbegin() const + { + return data_.cbegin(); + } + const_iterator cend() const + { + return data_.cend(); + } + + template + iterator insert(const_iterator pos, InputIt first, InputIt last) + { + return data_.insert(pos, first, last); + } + + friend bool operator==(const custom_binary_type& lhs, const custom_binary_type& rhs) + { + return lhs.data_ == rhs.data_; + } + friend bool operator<(const custom_binary_type& lhs, const custom_binary_type& rhs) + { + return lhs.data_ < rhs.data_; + } +}; diff --git a/docs/mkdocs/docs/examples/custom_binary_type.output b/docs/mkdocs/docs/examples/custom_binary_type.output new file mode 100644 index 000000000..b4814d6ed --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_binary_type.output @@ -0,0 +1,2 @@ +{"bytes":[1,2,3],"subtype":null} +true diff --git a/docs/mkdocs/docs/examples/custom_object_type.cpp b/docs/mkdocs/docs/examples/custom_object_type.cpp new file mode 100644 index 000000000..d4f99ccf0 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_object_type.cpp @@ -0,0 +1,26 @@ +#include +#include +#include + +#include + +#include "custom_object_type.hpp" + +using custom_json = nlohmann::basic_json; + +int main() +{ + custom_json j; + j["pi"] = 3.141; + j["happy"] = true; + j["list"] = {1, 2, 3}; + + std::cout << j.dump(2) << std::endl; + std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << std::endl; + + // custom_object_type has no key_compare member, so object_comparator_t + // falls back to its default + std::cout << std::boolalpha + << std::is_same::value + << std::endl; +} diff --git a/docs/mkdocs/docs/examples/custom_object_type.hpp b/docs/mkdocs/docs/examples/custom_object_type.hpp new file mode 100644 index 000000000..da715ed4e --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_object_type.hpp @@ -0,0 +1,144 @@ +#pragma once + +#include +#include + +// A minimal, self-contained ObjectType built around a private std::map. +// key_compare is deliberately not exposed: when an ObjectType has no +// key_compare member, the library falls back to its own default comparator. +// See https://json.nlohmann.me/features/types/template_parameters/#objecttype +template +class custom_object_type +{ + using map_t = std::map; + map_t data_; + + public: + using key_type = typename map_t::key_type; + using mapped_type = typename map_t::mapped_type; + using value_type = typename map_t::value_type; + using size_type = typename map_t::size_type; + using iterator = typename map_t::iterator; + using const_iterator = typename map_t::const_iterator; + + custom_object_type() = default; + custom_object_type(const custom_object_type&) = default; + custom_object_type(custom_object_type&&) = default; + custom_object_type& operator=(const custom_object_type&) = default; + custom_object_type& operator=(custom_object_type&&) = default; + + template + custom_object_type(InputIt first, InputIt last) : data_(first, last) {} + + iterator begin() + { + return data_.begin(); + } + iterator end() + { + return data_.end(); + } + const_iterator begin() const + { + return data_.begin(); + } + const_iterator end() const + { + return data_.end(); + } + const_iterator cbegin() const + { + return data_.cbegin(); + } + const_iterator cend() const + { + return data_.cend(); + } + + bool empty() const + { + return data_.empty(); + } + size_type size() const + { + return data_.size(); + } + size_type max_size() const + { + return data_.max_size(); + } + void clear() + { + data_.clear(); + } + + iterator find(const key_type& key) + { + return data_.find(key); + } + const_iterator find(const key_type& key) const + { + return data_.find(key); + } + size_type count(const key_type& key) const + { + return data_.count(key); + } + + std::pair emplace(const key_type& key, const mapped_type& value) + { + return data_.emplace(key, value); + } + + std::pair insert(const value_type& value) + { + return data_.insert(value); + } + + template + void insert(InputIt first, InputIt last) + { + data_.insert(first, last); + } + + mapped_type& operator[](const key_type& key) + { + return data_[key]; + } + + mapped_type& at(const key_type& key) + { + return data_.at(key); + } + const mapped_type& at(const key_type& key) const + { + return data_.at(key); + } + + iterator erase(iterator pos) + { + return data_.erase(pos); + } + iterator erase(iterator first, iterator last) + { + return data_.erase(first, last); + } + size_type erase(const key_type& key) + { + return data_.erase(key); + } + + void swap(custom_object_type& other) + { + data_.swap(other.data_); + } + + friend bool operator==(const custom_object_type& lhs, const custom_object_type& rhs) + { + return lhs.data_ == rhs.data_; + } + friend bool operator<(const custom_object_type& lhs, const custom_object_type& rhs) + { + return lhs.data_ < rhs.data_; + } +}; diff --git a/docs/mkdocs/docs/examples/custom_object_type.output b/docs/mkdocs/docs/examples/custom_object_type.output new file mode 100644 index 000000000..48b3e9630 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_object_type.output @@ -0,0 +1,11 @@ +{ + "happy": true, + "list": [ + 1, + 2, + 3 + ], + "pi": 3.141 +} +true +true diff --git a/docs/mkdocs/docs/examples/custom_string_type.cpp b/docs/mkdocs/docs/examples/custom_string_type.cpp new file mode 100644 index 000000000..63b798fc6 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_string_type.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +#include + +#include "custom_string_type.hpp" + +using custom_json = nlohmann::basic_json; + +int main() +{ + custom_json j; + j["pi"] = 3.141; + j["happy"] = true; + j["list"] = {1, 2, 3}; + + std::cout << j.dump(2) << std::endl; + std::cout << std::boolalpha << (custom_json::parse(j.dump()) == j) << std::endl; +} diff --git a/docs/mkdocs/docs/examples/custom_string_type.hpp b/docs/mkdocs/docs/examples/custom_string_type.hpp new file mode 100644 index 000000000..ec48501fc --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_string_type.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include +#include + +// A minimal, self-contained StringType built around a private std::string. +// Wraps rather than inherits, so it exposes exactly what the library needs +// and nothing more of std::string's interface. +// +// Covers the "Always required" members, the extras needed for the binary +// formats, and the extras needed for JSON Pointer / flatten / unflatten / +// diff. Extending it further (e.g. for std::hash or to_bson) is +// a matter of adding the extra members listed in the "Required for other +// functionality" table. +// +// See https://json.nlohmann.me/features/types/template_parameters/#stringtype +class custom_string_type +{ + std::string data_; + + public: + using value_type = char; + using size_type = std::string::size_type; + using iterator = std::string::iterator; + using const_iterator = std::string::const_iterator; + + static constexpr size_type npos = std::string::npos; + + custom_string_type() = default; + custom_string_type(const custom_string_type&) = default; + custom_string_type(custom_string_type&&) = default; + custom_string_type& operator=(const custom_string_type&) = default; + custom_string_type& operator=(custom_string_type&&) = default; + + // not explicit: the library relies on being able to hand it a string literal + custom_string_type(const char* s) : data_(s) {} + custom_string_type(const char* s, size_type count) : data_(s, count) {} + custom_string_type(size_type count, char ch) : data_(count, ch) {} + + size_type size() const + { + return data_.size(); + } + bool empty() const + { + return data_.empty(); + } + void clear() + { + data_.clear(); + } + void resize(size_type n) + { + data_.resize(n); + } + void resize(size_type n, char c) + { + data_.resize(n, c); + } + void reserve(size_type n) + { + data_.reserve(n); + } + + // must stay null-terminated -- the parser hands this to std::strtoull & + // friends; std::string::data() has guaranteed that since C++11 + const char* data() const + { + return data_.data(); + } + + void push_back(char c) + { + data_.push_back(c); + } + + char& operator[](size_type pos) + { + return data_[pos]; + } + char operator[](size_type pos) const + { + return data_[pos]; + } + + custom_string_type& append(const char* s, size_type count) + { + data_.append(s, count); + return *this; + } + custom_string_type& append(const custom_string_type& other) + { + data_.append(other.data_); + return *this; + } + + size_type find_first_of(char c, size_type pos = 0) const + { + return data_.find_first_of(c, pos); + } + + iterator begin() + { + return data_.begin(); + } + iterator end() + { + return data_.end(); + } + const_iterator begin() const + { + return data_.begin(); + } + const_iterator end() const + { + return data_.end(); + } + + friend bool operator==(const custom_string_type& lhs, const custom_string_type& rhs) + { + return lhs.data_ == rhs.data_; + } + friend bool operator<(const custom_string_type& lhs, const custom_string_type& rhs) + { + return lhs.data_ < rhs.data_; + } + + // not required by the library itself, but dump() returns a custom_string_type + // and this makes `std::cout << j.dump()` work as expected + friend std::ostream& operator<<(std::ostream& os, const custom_string_type& s) + { + return os << s.data_; + } +}; diff --git a/docs/mkdocs/docs/examples/custom_string_type.output b/docs/mkdocs/docs/examples/custom_string_type.output new file mode 100644 index 000000000..d792e2a72 --- /dev/null +++ b/docs/mkdocs/docs/examples/custom_string_type.output @@ -0,0 +1,10 @@ +{ + "happy": true, + "list": [ + 1, + 2, + 3 + ], + "pi": 3.141 +} +true diff --git a/docs/mkdocs/docs/features/types/template_parameters.md b/docs/mkdocs/docs/features/types/template_parameters.md index 61d4fb90b..760972dff 100644 --- a/docs/mkdocs/docs/features/types/template_parameters.md +++ b/docs/mkdocs/docs/features/types/template_parameters.md @@ -202,6 +202,29 @@ conservatively -- this is correct, but slower. The library does not sort or de-duplicate keys itself; the behavior described in [`object_t`](../../api/basic_json/object_t.md) is entirely the behavior of the chosen container. +!!! tip "Reference implementation" + + `docs/mkdocs/docs/examples/custom_object_type.hpp` wraps a private `#!cpp std::map` and satisfies every + requirement above. It does not define `key_compare`, so `object_comparator_t` falls back to + [`default_object_comparator_t`](../../api/basic_json/default_object_comparator_t.md) -- a good starting point for + a custom `ObjectType`. + + ```cpp + --8<-- "examples/custom_object_type.hpp" + ``` + +??? example "Compiling and using it" + + ```cpp + --8<-- "examples/custom_object_type.cpp" + ``` + + Output: + + ```json + --8<-- "examples/custom_object_type.output" + ``` + ### Compatible containers | Container | Notes | @@ -274,6 +297,27 @@ using array_t = ArrayType>; pointers of all elements are refreshed after every insertion, which makes adding *n* elements cost O(*n*²). Only diagnostics builds pay this; without them `capacity()` is never called. +!!! tip "Reference implementation" + + `docs/mkdocs/docs/examples/custom_array_type.hpp` wraps a private `#!cpp std::vector` and satisfies every + requirement above -- a good starting point for a custom `ArrayType`. + + ```cpp + --8<-- "examples/custom_array_type.hpp" + ``` + +??? example "Compiling and using it" + + ```cpp + --8<-- "examples/custom_array_type.cpp" + ``` + + Output: + + ```json + --8<-- "examples/custom_array_type.output" + ``` + ### Compatible containers | Container | Notes | @@ -394,8 +438,26 @@ using array_t = ArrayType>; !!! tip "Reference implementation" - The unit test `tests/src/unit-alt-string.cpp` contains `alt_string`, a minimal string type that satisfies the - requirements needed for the tested subset of the API. It is a good starting point for a custom `StringType`. + `docs/mkdocs/docs/examples/custom_string_type.hpp` wraps a private `#!cpp std::string` and satisfies every + requirement above -- a good starting point for a custom `StringType`. The unit test + `tests/src/unit-alt-string.cpp` contains a more thorough variant, `alt_string`, exercised against a larger part + of the API. + + ```cpp + --8<-- "examples/custom_string_type.hpp" + ``` + +??? example "Compiling and using it" + + ```cpp + --8<-- "examples/custom_string_type.cpp" + ``` + + Output: + + ```json + --8<-- "examples/custom_string_type.output" + ``` ## `BooleanType` @@ -593,6 +655,27 @@ using binary_t = nlohmann::byte_container_with_subtype; See [`binary_t`](../../api/basic_json/binary_t.md) for how a non-default `BinaryType` changes the meaning of assigning such a container to a `basic_json` value. +!!! tip "Reference implementation" + + `docs/mkdocs/docs/examples/custom_binary_type.hpp` wraps a private `#!cpp std::vector` and satisfies + every requirement above -- a good starting point for a custom `BinaryType`. + + ```cpp + --8<-- "examples/custom_binary_type.hpp" + ``` + +??? example "Compiling and using it" + + ```cpp + --8<-- "examples/custom_binary_type.cpp" + ``` + + Output: + + ```json + --8<-- "examples/custom_binary_type.output" + ``` + ### Compatible containers | Container | Notes |