-
Notifications
You must be signed in to change notification settings - Fork 14
Add phlex::identifier #288
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
Merged
Merged
Changes from 11 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
221b5c8
Introduce phlex::identifier
beojan c74758e
Change return value of value_if_exists to identifier
beojan 76d5ce3
Add tag_invoke for identifier
beojan 5236ed9
Apply clang-format fixes
github-actions[bot] 130fe54
Inconsequential change
beojan dfd9f91
Address review comments
beojan 2b2b6c9
Fix a signature
beojan 65cc8cd
Add test for identifier assignment from string_view
beojan 38c7f69
Add test for value_if_exists
beojan a17e7bb
Typo
beojan 110f528
Apply clang-format fixes
github-actions[bot] f5d25aa
Remove potentially unnecessary assignment operator
beojan 0d126ed
Compare strings as well in identifer
beojan 1d1e4ad
Remove not-strictly-necessary error checking to improve coverage
beojan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| #include "identifier.hpp" | ||
|
|
||
| #include <boost/hash2/hash_append.hpp> | ||
| #include <boost/hash2/xxhash.hpp> | ||
|
|
||
| namespace phlex::experimental { | ||
| identifier_query literals::operator""_idq(char const* lit, std::size_t len) | ||
| { | ||
| return {identifier::hash_string(std::string_view(lit, len))}; | ||
| } | ||
|
|
||
| std::uint64_t identifier::hash_string(std::string_view str) | ||
| { | ||
| // Hash quality is very important here, since comparisons are done using only the hash | ||
| using namespace boost::hash2; | ||
| xxhash_64 h; | ||
| hash_append(h, {}, str); | ||
| return h.result(); | ||
| } | ||
|
|
||
| identifier::identifier(std::string_view str) : content_(str), hash_(hash_string(content_)) {} | ||
| identifier& identifier::operator=(std::string_view str) | ||
| { | ||
| content_ = str; | ||
| hash_ = hash_string(content_); | ||
| return *this; | ||
| } | ||
|
|
||
| identifier::operator std::string_view() const noexcept { return std::string_view(content_); } | ||
| bool identifier::operator==(identifier const& rhs) const noexcept { return hash_ == rhs.hash_; } | ||
| std::strong_ordering identifier::operator<=>(identifier const& rhs) const noexcept | ||
| { | ||
| return hash_ <=> rhs.hash_; | ||
| } | ||
|
|
||
| bool operator==(identifier const& lhs, identifier_query rhs) { return lhs.hash_ == rhs.hash; } | ||
| std::strong_ordering operator<=>(identifier const& lhs, identifier_query rhs) | ||
| { | ||
| return lhs.hash_ <=> rhs.hash; | ||
| } | ||
|
|
||
| identifier literals::operator""_id(char const* lit, std::size_t len) | ||
| { | ||
| return identifier{std::string_view(lit, len)}; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| #ifndef PHLEX_MODEL_IDENTIFIER_H_ | ||
| #define PHLEX_MODEL_IDENTIFIER_H_ | ||
|
|
||
| #include <boost/json/fwd.hpp> | ||
|
|
||
| #include <fmt/format.h> | ||
|
|
||
| #include <cstdint> | ||
| #include <string> | ||
| #include <string_view> | ||
|
|
||
| namespace phlex::experimental { | ||
| /// If you're comparing to an identifier you know at compile time, you're probably not going to need | ||
| /// to print it. | ||
| struct identifier_query { | ||
| std::uint64_t hash; | ||
| }; | ||
|
|
||
| /// Carries around the string itself (as a shared_ptr to string to make copies lighter) | ||
| /// along with a precomputed hash used for all comparisons | ||
| class identifier { | ||
| public: | ||
| static std::uint64_t hash_string(std::string_view str); | ||
| identifier(identifier const& other) = default; | ||
| identifier(identifier&& other) noexcept = default; | ||
|
|
||
| explicit identifier(std::string_view str); | ||
|
|
||
| identifier& operator=(identifier const& rhs) = default; | ||
| identifier& operator=(identifier&& rhs) noexcept = default; | ||
|
|
||
| // Assignment for identifiers read from a file | ||
| // Not sure we really need this | ||
| identifier& operator=(std::string_view str); | ||
beojan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ~identifier() = default; | ||
|
|
||
| // Conversion to std::string_view | ||
| explicit operator std::string_view() const noexcept; | ||
|
|
||
| bool operator==(identifier const& rhs) const noexcept; | ||
| std::strong_ordering operator<=>(identifier const& rhs) const noexcept; | ||
|
|
||
| // Comparison operators with _id queries | ||
| friend bool operator==(identifier const& lhs, identifier_query rhs); | ||
| friend std::strong_ordering operator<=>(identifier const& lhs, identifier_query rhs); | ||
| friend std::hash<identifier>; | ||
|
|
||
| private: | ||
| std::string content_; | ||
| std::uint64_t hash_; | ||
| }; | ||
|
|
||
| // Identifier UDL | ||
| namespace literals { | ||
| identifier operator""_id(char const* lit, std::size_t len); | ||
| identifier_query operator""_idq(char const* lit, std::size_t len); | ||
| } | ||
|
|
||
| // Really trying to avoid the extra function call here | ||
| inline std::string_view format_as(identifier const& id) { return std::string_view(id); } | ||
| } | ||
|
|
||
| template <> | ||
| struct std::hash<phlex::experimental::identifier> { | ||
| std::size_t operator()(phlex::experimental::identifier const& id) const noexcept | ||
| { | ||
| return id.hash_; | ||
| } | ||
| }; | ||
|
|
||
| #endif // PHLEX_MODEL_IDENTIFIER_H_ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| #include "phlex/model/identifier.hpp" | ||
| #include <phlex/configuration.hpp> | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <cassert> | ||
| #include <string_view> | ||
|
|
||
| #include <boost/json.hpp> | ||
| #include <fmt/format.h> | ||
|
|
||
| int main() | ||
| { | ||
| using namespace phlex::experimental; | ||
| using namespace phlex::experimental::literals; | ||
| using namespace std::string_view_literals; | ||
| identifier a = "a"_id; | ||
| identifier a_copy = a; | ||
| identifier a2 = "a"_id; | ||
| identifier b = "b"_id; | ||
|
|
||
| boost::json::object parsed_json = boost::json::parse(R"( {"identifier": "b" } )").as_object(); | ||
| auto b_from_json = phlex::detail::value_if_exists(parsed_json, "identifier"); | ||
| assert(b_from_json); | ||
|
|
||
| fmt::print("a ({}) == \"a\"_idq: {}\n", a, a == "a"_idq); | ||
| fmt::print("a == a_copy ({}): {}\n", a_copy, a == a_copy); | ||
| fmt::print("a == a2 ({}): {}\n", a2, a == a2); | ||
| fmt::print("a != b ({}): {}\n", b, a != b); | ||
| fmt::print("b == *b_from_json ({}): {}\n", *b_from_json, b == *b_from_json); | ||
|
|
||
| assert(a == "a"_idq); | ||
| assert(a == a_copy); | ||
| assert(a == a2); | ||
| assert(a != b); | ||
| assert(b == *b_from_json); | ||
|
|
||
| // reassigning | ||
| a = "new a"_id; | ||
| a_copy = a; | ||
|
|
||
| // reassigning with a string_view | ||
| b = "new b"sv; | ||
|
|
||
| std::array<std::string_view, 9> strings{ | ||
| "a"sv, "b"sv, "c"sv, "d"sv, "e"sv, "long-id-1"sv, "long-id-2"sv, "test"sv, "other_test"sv}; | ||
| std::array<identifier, 9> identifiers{"a"_id, | ||
| "b"_id, | ||
| "c"_id, | ||
| "d"_id, | ||
| "e"_id, | ||
| "long-id-1"_id, | ||
| "long-id-2"_id, | ||
| "test"_id, | ||
| "other_test"_id}; | ||
| std::ranges::sort(identifiers); | ||
| std::ranges::sort(strings); | ||
| fmt::print("Sorted identifiers (should not be in alphabetical order): \n"); | ||
| for (auto const& id : identifiers) { | ||
| fmt::print("- {}\n", id); | ||
| } | ||
|
|
||
| bool ok = false; | ||
| for (std::size_t i = 0; i < identifiers.size(); ++i) { | ||
| if (strings.at(i) != std::string_view(identifiers.at(i))) { | ||
| ok = true; | ||
| break; | ||
| } | ||
| } | ||
| assert(ok); | ||
| return 0; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.