|
20 | 20 | #ifndef __CXXGRAPH_NODE_IMPL_H__ |
21 | 21 | #define __CXXGRAPH_NODE_IMPL_H__ |
22 | 22 |
|
| 23 | +#if __cplusplus >= 202004L |
| 24 | +#include <bitset> |
| 25 | +#else |
| 26 | +#include <cstring> |
| 27 | +#endif |
| 28 | + |
23 | 29 | #include <iomanip> |
| 30 | +#include <limits> |
| 31 | +#include <type_traits> |
24 | 32 |
|
25 | 33 | #include "Node_decl.h" |
26 | 34 |
|
27 | 35 | namespace CXXGraph { |
| 36 | +// internals not intended to be accessed by the end user |
| 37 | +namespace internals { |
| 38 | +template <typename AnyUserID> |
| 39 | +constexpr CXXGraph::id_t UserIdToId(const AnyUserID &id) { |
| 40 | + // in C++ 23 we have constexpr hashes |
| 41 | + if constexpr (__cplusplus >= 202302L) return std::hash<AnyUserID>{}(id); |
| 42 | + // hashing is trivial for integral types |
| 43 | + if constexpr (std::is_integral<AnyUserID>::value) { |
| 44 | + // assert nothing is greater than a size_t to avoid issues with GCC and |
| 45 | + // clang's 128 bit ints, a size_t is usually 64 bits |
| 46 | + static_assert(std::numeric_limits<CXXGraph::id_t>::max() >= |
| 47 | + std::numeric_limits<AnyUserID>::max()); |
| 48 | + |
| 49 | + if constexpr (std::is_signed<AnyUserID>::value) { |
| 50 | + // Add id to half max so that we have something unique |
| 51 | + return (std::numeric_limits<CXXGraph::id_t>::max() / 2) + id; |
| 52 | + } |
| 53 | + return static_cast<CXXGraph::id_t>(id); |
| 54 | + } |
| 55 | + // typepun the float into an int |
| 56 | + if constexpr (std::is_floating_point<AnyUserID>::value) { |
| 57 | + // static assert to avoid long double (80 bits rather than 64) |
| 58 | + static_assert(sizeof(CXXGraph::id_t) >= sizeof(AnyUserID)); |
| 59 | +// In C++20 we can do typepunning at compile time |
| 60 | +#if __cplusplus >= 202004L |
| 61 | + return std::bit_cast<CXXGraph::id_t>(id); |
| 62 | +#else // < C++20 |
| 63 | + // otherwise use memcpy (not compile time) |
| 64 | + CXXGraph::id_t result{}; |
| 65 | + std::memcpy(&result, &id, sizeof(id)); |
| 66 | + return result; |
| 67 | +#endif // >= C++20 |
| 68 | + } |
| 69 | + // resort back to runtime hash |
| 70 | + return std::hash<AnyUserID>{}(id); |
| 71 | +} |
| 72 | +} // namespace internals |
28 | 73 |
|
29 | | -template <typename T> |
| 74 | +template <typename T, typename UserID> |
30 | 75 | class Node; |
31 | 76 |
|
32 | | -template <typename T> |
33 | | -Node<T>::Node(const std::string &id, const T &data) { |
34 | | - this->userId = id; |
35 | | - // the userid is set as sha512 hash of the user provided id |
36 | | - setId(id); |
37 | | - this->data = data; |
| 77 | +template <typename T, typename UserID> |
| 78 | +constexpr Node<T, UserID>::Node(const UserID &otherId, const T &otherData) |
| 79 | + : id(internals::UserIdToId(otherId)), userId(otherId), data(otherData) { |
| 80 | + static_assert( |
| 81 | + !std::is_pointer<UserID>::value, |
| 82 | + "Pointer is not allowed as an ID type, did you mean std::string?"); |
38 | 83 | } |
39 | 84 |
|
40 | | -template <typename T> |
41 | | -Node<T>::Node(const std::string &id, T &&data) noexcept { |
42 | | - this->userId = id; |
43 | | - // the userid is set as sha512 hash of the user provided id |
44 | | - setId(id); |
45 | | - this->data = std::move(data); |
| 85 | +template <typename T, typename UserID> |
| 86 | +constexpr Node<T, UserID>::Node(const UserID &otherId, T &&otherData) noexcept( |
| 87 | + std::is_nothrow_move_assignable<T>::value) |
| 88 | + : id(internals::UserIdToId(otherId)), userId(otherId), data(otherData) { |
| 89 | + static_assert( |
| 90 | + !std::is_pointer<UserID>::value, |
| 91 | + "Pointer is not allowed as an ID type, did you mean std::string?"); |
46 | 92 | } |
47 | 93 |
|
48 | | -template <typename T> |
49 | | -void Node<T>::setId(const std::string &inpId) { |
50 | | - this->id = std::hash<std::string>{}(inpId); |
| 94 | +template <typename T, typename UserID> |
| 95 | +constexpr void Node<T, UserID>::setId(const UserID &inputId) { |
| 96 | + this->id = UserIdToId(inputId); |
51 | 97 | } |
52 | 98 |
|
53 | | -template <typename T> |
54 | | -constexpr const CXXGraph::id_t &Node<T>::getId() const { |
| 99 | +template <typename T, typename UserID> |
| 100 | +constexpr const CXXGraph::id_t &Node<T, UserID>::getId() const { |
55 | 101 | return id; |
56 | 102 | } |
57 | 103 |
|
58 | | -template <typename T> |
59 | | -const std::string &Node<T>::getUserId() const { |
| 104 | +template <typename T, typename UserID> |
| 105 | +constexpr const UserID &Node<T, UserID>::getUserId() const { |
60 | 106 | return userId; |
61 | 107 | } |
62 | 108 |
|
63 | | -template <typename T> |
64 | | -constexpr const T &Node<T>::getData() const { |
| 109 | +template <typename T, typename UserID> |
| 110 | +constexpr const T &Node<T, UserID>::getData() const { |
65 | 111 | return data; |
66 | 112 | } |
67 | 113 |
|
68 | | -template <typename T> |
69 | | -void Node<T>::setData(T &&new_data) { |
70 | | - this->data = std::move(new_data); |
| 114 | +template <typename T, typename UserID> |
| 115 | +constexpr void Node<T, UserID>::setData(T &&newData) { |
| 116 | + this->data = std::move(newData); |
71 | 117 | } |
72 | 118 |
|
73 | 119 | // The data type T must have an overload of the equality operator |
74 | | -template <typename T> |
75 | | -constexpr bool Node<T>::operator==(const Node<T> &b) const { |
| 120 | +template <typename T, typename UserID> |
| 121 | +constexpr bool Node<T, UserID>::operator==(const Node<T, UserID> &b) const { |
76 | 122 | return (this->id == b.id && this->data == b.data); |
77 | 123 | } |
78 | 124 |
|
79 | | -template <typename T> |
80 | | -constexpr bool Node<T>::operator<(const Node<T> &b) const { |
| 125 | +template <typename T, typename UserID> |
| 126 | +constexpr bool Node<T, UserID>::operator<(const Node<T, UserID> &b) const { |
81 | 127 | return (this->id < b.id); |
82 | 128 | } |
83 | 129 |
|
84 | 130 | // ostream overload |
85 | 131 | // The data type T must have an overload of the ostream operator |
86 | | -template <typename T> |
87 | | -std::ostream &operator<<(std::ostream &os, const Node<T> &node) { |
| 132 | +template <typename T, typename UserID> |
| 133 | +std::ostream &operator<<(std::ostream &os, const Node<T, UserID> &node) { |
88 | 134 | os << "Node: {\n" |
89 | 135 | << " Id:\t" << node.userId << "\n Data:\t" << node.data << "\n}"; |
90 | 136 | return os; |
|
0 commit comments