From 619def44c1cca9f3cdf63544d5f24f2c7a7d9b77 Mon Sep 17 00:00:00 2001 From: Klaus Aehlig Date: Tue, 22 Feb 2022 17:03:21 +0100 Subject: Initial self-hosting commit This is the initial version of our tool that is able to build itself. In can be bootstrapped by ./bin/bootstrap.py Co-authored-by: Oliver Reiche Co-authored-by: Victor Moreno --- src/utils/cpp/TARGETS | 40 +++++++ src/utils/cpp/atomic.hpp | 119 ++++++++++++++++++++ src/utils/cpp/concepts.hpp | 55 +++++++++ src/utils/cpp/hash_combine.hpp | 15 +++ src/utils/cpp/hex_string.hpp | 19 ++++ src/utils/cpp/json.hpp | 83 ++++++++++++++ src/utils/cpp/type_safe_arithmetic.hpp | 197 +++++++++++++++++++++++++++++++++ 7 files changed, 528 insertions(+) create mode 100644 src/utils/cpp/TARGETS create mode 100644 src/utils/cpp/atomic.hpp create mode 100644 src/utils/cpp/concepts.hpp create mode 100644 src/utils/cpp/hash_combine.hpp create mode 100644 src/utils/cpp/hex_string.hpp create mode 100644 src/utils/cpp/json.hpp create mode 100644 src/utils/cpp/type_safe_arithmetic.hpp (limited to 'src/utils/cpp') diff --git a/src/utils/cpp/TARGETS b/src/utils/cpp/TARGETS new file mode 100644 index 00000000..6b4347a2 --- /dev/null +++ b/src/utils/cpp/TARGETS @@ -0,0 +1,40 @@ +{ "hash_combine": + { "type": ["@", "rules", "CC", "library"] + , "name": ["hash_combine"] + , "hdrs": ["hash_combine.hpp"] + , "deps": [["@", "gsl-lite", "", "gsl-lite"]] + , "stage": ["src", "utils", "cpp"] + } +, "type_safe_arithmetic": + { "type": ["@", "rules", "CC", "library"] + , "name": ["type_safe_arithmetic"] + , "hdrs": ["type_safe_arithmetic.hpp"] + , "deps": [["@", "gsl-lite", "", "gsl-lite"]] + , "stage": ["src", "utils", "cpp"] + } +, "json": + { "type": ["@", "rules", "CC", "library"] + , "name": ["json"] + , "hdrs": ["json.hpp"] + , "deps": [["@", "json", "", "json"], ["@", "gsl-lite", "", "gsl-lite"]] + , "stage": ["src", "utils", "cpp"] + } +, "concepts": + { "type": ["@", "rules", "CC", "library"] + , "name": ["concepts"] + , "hdrs": ["concepts.hpp"] + , "stage": ["src", "utils", "cpp"] + } +, "atomic": + { "type": ["@", "rules", "CC", "library"] + , "name": ["atomic"] + , "hdrs": ["atomic.hpp"] + , "stage": ["src", "utils", "cpp"] + } +, "hex_string": + { "type": ["@", "rules", "CC", "library"] + , "name": ["hex_string"] + , "hdrs": ["hex_string.hpp"] + , "stage": ["src", "utils", "cpp"] + } +} \ No newline at end of file diff --git a/src/utils/cpp/atomic.hpp b/src/utils/cpp/atomic.hpp new file mode 100644 index 00000000..7f7631d0 --- /dev/null +++ b/src/utils/cpp/atomic.hpp @@ -0,0 +1,119 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_ATOMIC_HPP +#define INCLUDED_SRC_UTILS_CPP_ATOMIC_HPP + +#include +#include +#include + +// Atomic wrapper with notify/wait capabilities. +// TODO(modernize): Replace any use this class by C++20's std::atomic, once +// libcxx adds support for notify_*() and wait(). +// [https://libcxx.llvm.org/docs/Cxx2aStatus.html] +template +class atomic { + public: + atomic() = default; + explicit atomic(T value) : value_{std::move(value)} {} + atomic(atomic const& other) = delete; + atomic(atomic&& other) = delete; + ~atomic() = default; + + auto operator=(atomic const& other) -> atomic& = delete; + auto operator=(atomic&& other) -> atomic& = delete; + auto operator=(T desired) -> T { // NOLINT + std::shared_lock lock(mutex_); + value_ = desired; + return desired; + } + operator T() const { return static_cast(value_); } // NOLINT + + void store(T desired, std::memory_order order = std::memory_order_seq_cst) { + std::shared_lock lock(mutex_); + value_.store(std::move(desired), order); + } + [[nodiscard]] auto load( + std::memory_order order = std::memory_order_seq_cst) const -> T { + return value_.load(order); + } + + template >> + auto operator++() -> T { + std::shared_lock lock(mutex_); + return ++value_; + } + template >> + [[nodiscard]] auto operator++(int) -> T { + std::shared_lock lock(mutex_); + return value_++; + } + template >> + auto operator--() -> T { + std::shared_lock lock(mutex_); + return --value_; + } + template >> + [[nodiscard]] auto operator--(int) -> T { + std::shared_lock lock(mutex_); + return value_--; + } + + void notify_one() { cv_.notify_one(); } + void notify_all() { cv_.notify_all(); } + void wait(T old, + std::memory_order order = std::memory_order::seq_cst) const { + std::unique_lock lock(mutex_); + cv_.wait(lock, + [this, &old, order]() { return value_.load(order) != old; }); + } + + private: + std::atomic value_{}; + mutable std::shared_mutex mutex_{}; + mutable std::condition_variable_any cv_{}; +}; + +// Atomic shared_pointer with notify/wait capabilities. +// TODO(modernize): Replace any use this class by C++20's +// std::atomic>, once libcxx adds support for it. +// [https://libcxx.llvm.org/docs/Cxx2aStatus.html] +template +class atomic_shared_ptr { + using ptr_t = std::shared_ptr; + + public: + atomic_shared_ptr() = default; + explicit atomic_shared_ptr(ptr_t value) : value_{std::move(value)} {} + atomic_shared_ptr(atomic_shared_ptr const& other) = delete; + atomic_shared_ptr(atomic_shared_ptr&& other) = delete; + ~atomic_shared_ptr() = default; + + auto operator=(atomic_shared_ptr const& other) + -> atomic_shared_ptr& = delete; + auto operator=(atomic_shared_ptr&& other) -> atomic_shared_ptr& = delete; + auto operator=(ptr_t desired) -> ptr_t { // NOLINT + std::shared_lock lock(mutex_); + value_ = desired; + return desired; + } + operator ptr_t() const { value_; } // NOLINT + + void store(ptr_t desired) { + std::shared_lock lock(mutex_); + value_ = std::move(desired); + } + [[nodiscard]] auto load() const -> ptr_t { return value_; } + + void notify_one() { cv_.notify_one(); } + void notify_all() { cv_.notify_all(); } + void wait(ptr_t old) const { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this, &old]() { return value_ != old; }); + } + + private: + ptr_t value_{}; + mutable std::shared_mutex mutex_{}; + mutable std::condition_variable_any cv_{}; +}; + +#endif // INCLUDED_SRC_UTILS_CPP_ATOMIC_HPP diff --git a/src/utils/cpp/concepts.hpp b/src/utils/cpp/concepts.hpp new file mode 100644 index 00000000..92718b43 --- /dev/null +++ b/src/utils/cpp/concepts.hpp @@ -0,0 +1,55 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_CONCEPTS_HPP +#define INCLUDED_SRC_UTILS_CPP_CONCEPTS_HPP + +#include +#include + +// TODO(modernize): remove this once std::derived_from is shipped with libcxx +template +concept derived_from = std::is_base_of_v&& + std::is_convertible_v; + +// TODO(modernize): remove this once std::same_as is shipped with libcxx +template +concept same_as = std::is_same_vand std::is_same_v; + +template +concept ContainsString = requires { + typename T::value_type; +} +and std::is_same_v; + +template +concept HasSize = requires(T const c) { + { c.size() } + ->same_as; // TODO(modernize): replace by std::same_as +}; + +template +concept HasToString = requires(T const t) { + { t.ToString() } + ->same_as; // TODO(modernize): replace by std::same_as +}; + +template +concept InputIterableContainer = requires(T const c) { + { c.begin() } + ->same_as; // TODO(modernize): replace by + // std::input_iterator + { c.end() } + ->same_as; // TODO(modernize): replace by + // std::input_iterator +}; + +template +concept OutputIterableContainer = InputIterableContainerand requires(T c) { + { std::inserter(c, c.begin()) } + ->same_as>; // TODO(modernize): replace by + // std::output_iterator +}; + +template +concept InputIterableStringContainer = + InputIterableContainerand ContainsString; + +#endif // INCLUDED_SRC_UTILS_CPP_CONCEPTS_HPP diff --git a/src/utils/cpp/hash_combine.hpp b/src/utils/cpp/hash_combine.hpp new file mode 100644 index 00000000..65c0c8ad --- /dev/null +++ b/src/utils/cpp/hash_combine.hpp @@ -0,0 +1,15 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_HASH_COMBINE_HPP +#define INCLUDED_SRC_UTILS_CPP_HASH_COMBINE_HPP + +#include "gsl-lite/gsl-lite.hpp" + +// Taken from Boost, as hash_combine did not yet make it to STL. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0814r0.pdf +template +inline auto hash_combine(gsl::not_null const& seed, T const& v) + -> void { + *seed ^= + std::hash{}(v) + 0x9e3779b9 + (*seed << 6) + (*seed >> 2); // NOLINT +} + +#endif diff --git a/src/utils/cpp/hex_string.hpp b/src/utils/cpp/hex_string.hpp new file mode 100644 index 00000000..86ea1b9e --- /dev/null +++ b/src/utils/cpp/hex_string.hpp @@ -0,0 +1,19 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_HEX_STRING_HPP +#define INCLUDED_SRC_UTILS_CPP_HEX_STRING_HPP + +#include +#include +#include + +[[nodiscard]] static inline auto ToHexString(std::string const& bytes) + -> std::string { + std::ostringstream ss{}; + ss << std::hex << std::setfill('0'); + for (auto const& b : bytes) { + ss << std::setw(2) + << static_cast(static_cast(b)); + } + return ss.str(); +} + +#endif // INCLUDED_SRC_UTILS_CPP_HEX_STRING_HPP diff --git a/src/utils/cpp/json.hpp b/src/utils/cpp/json.hpp new file mode 100644 index 00000000..8945e975 --- /dev/null +++ b/src/utils/cpp/json.hpp @@ -0,0 +1,83 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_JSON_HPP +#define INCLUDED_SRC_UTILS_CPP_JSON_HPP + +#include +#include +#include +#include + +#include "nlohmann/json.hpp" +#include "gsl-lite/gsl-lite.hpp" + +template +auto ExtractValueAs( + nlohmann::json const& j, + std::string const& key, + std::function&& logger = + [](std::string const & /*unused*/) -> void {}) noexcept + -> std::optional { + try { + auto it = j.find(key); + if (it == j.end()) { + logger("key " + key + " cannot be found in JSON object"); + return std::nullopt; + } + return it.value().template get(); + } catch (std::exception& e) { + logger(e.what()); + return std::nullopt; + } +} + +namespace detail { + +[[nodiscard]] static inline auto IndentListsOnlyUntilDepth( + nlohmann::json const& json, + std::string const& indent, + std::size_t until, + std::size_t depth) -> std::string { + using iterator = std::ostream_iterator; + if (json.is_object()) { + std::size_t i{}; + std::ostringstream oss{}; + oss << '{' << std::endl; + for (auto const& [key, value] : json.items()) { + std::fill_n(iterator{oss}, depth + 1, indent); + oss << nlohmann::json(key).dump() << ": " + << IndentListsOnlyUntilDepth(value, indent, until, depth + 1) + << (++i == json.size() ? "" : ",") << std::endl; + } + std::fill_n(iterator{oss}, depth, indent); + oss << '}'; + gsl_EnsuresAudit(nlohmann::json::parse(oss.str()) == json); + return oss.str(); + } + if (json.is_array() and depth < until) { + std::size_t i{}; + std::ostringstream oss{}; + oss << '[' << std::endl; + for (auto const& value : json) { + std::fill_n(iterator{oss}, depth + 1, indent); + oss << IndentListsOnlyUntilDepth(value, indent, until, depth + 1) + << (++i == json.size() ? "" : ",") << std::endl; + } + std::fill_n(iterator{oss}, depth, indent); + oss << ']'; + gsl_EnsuresAudit(nlohmann::json::parse(oss.str()) == json); + return oss.str(); + } + return json.dump(); +} + +} // namespace detail + +/// \brief Dump json with indent. Indent lists only until specified depth. +[[nodiscard]] static inline auto IndentListsOnlyUntilDepth( + nlohmann::json const& json, + std::size_t indent, + std::size_t until_depth = 0) -> std::string { + return detail::IndentListsOnlyUntilDepth( + json, std::string(indent, ' '), until_depth, 0); +} + +#endif // INCLUDED_SRC_UTILS_CPP_JSON_HPP diff --git a/src/utils/cpp/type_safe_arithmetic.hpp b/src/utils/cpp/type_safe_arithmetic.hpp new file mode 100644 index 00000000..21bba0b5 --- /dev/null +++ b/src/utils/cpp/type_safe_arithmetic.hpp @@ -0,0 +1,197 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_TYPE_SAFE_ARITHMETIC_HPP +#define INCLUDED_SRC_UTILS_CPP_TYPE_SAFE_ARITHMETIC_HPP + +#include +#include + +#include "gsl-lite/gsl-lite.hpp" + +/// \struct type_safe_arithmetic_tag +/// \brief Abstract tag defining types and limits for custom arithmetic types. +/// Usage example: +/// struct my_type_tag : type_safe_arithmetic_tag {}; +/// using my_type_t = type_safe_arithmetic; +template ::lowest(), + T MAX_VALUE = std::numeric_limits::max(), + T SMALLEST_VALUE = std::numeric_limits::min()> +struct type_safe_arithmetic_tag { + static_assert(std::is_arithmetic::value, + "T must be an arithmetic type (integer or floating-point)"); + + using value_t = T; + using reference_t = T&; + using const_reference_t = T const&; + using pointer_t = T*; + using const_pointer_t = T const*; + + static constexpr value_t max_value = MAX_VALUE; + static constexpr value_t min_value = MIN_VALUE; + static constexpr value_t smallest_value = SMALLEST_VALUE; +}; + +/// \class type_safe_arithmetic +/// \brief Abstract class for defining custom arithmetic types. +/// \tparam TAG The actual \ref type_safe_arithmetic_tag +template +class type_safe_arithmetic { + typename TAG::value_t m_value{}; + + public: + using tag_t = TAG; + using value_t = typename tag_t::value_t; + using reference_t = typename tag_t::reference_t; + using const_reference_t = typename tag_t::const_reference_t; + using pointer_t = typename tag_t::pointer_t; + using const_pointer_t = typename tag_t::const_pointer_t; + + static constexpr value_t max_value = tag_t::max_value; + static constexpr value_t min_value = tag_t::min_value; + static constexpr value_t smallest_value = tag_t::smallest_value; + + constexpr type_safe_arithmetic() = default; + + // NOLINTNEXTLINE + constexpr /*explicit*/ type_safe_arithmetic(value_t value) { set(value); } + + type_safe_arithmetic(type_safe_arithmetic const&) = default; + type_safe_arithmetic(type_safe_arithmetic&&) noexcept = default; + auto operator=(type_safe_arithmetic const&) + -> type_safe_arithmetic& = default; + auto operator=(type_safe_arithmetic&&) noexcept + -> type_safe_arithmetic& = default; + ~type_safe_arithmetic() = default; + + auto operator=(value_t value) -> type_safe_arithmetic& { + set(value); + return *this; + } + + // NOLINTNEXTLINE + constexpr /*explicit*/ operator value_t() const { return m_value; } + + constexpr auto get() const -> value_t { return m_value; } + + constexpr void set(value_t value) { + gsl_Expects(value >= min_value && value <= max_value && + "value output of range"); + m_value = value; + } + + auto pointer() const -> const_pointer_t { return &m_value; } +}; + +// template +// bool operator==(type_safe_arithmetic lhs, type_safe_arithmetic rhs) +// { +// return lhs.get() == rhs.get(); +// } +// +// template +// bool operator!=(type_safe_arithmetic lhs, type_safe_arithmetic rhs) +// { +// return !(lhs == rhs); +// } +// +// template +// bool operator>(type_safe_arithmetic lhs, type_safe_arithmetic rhs) +// { +// return lhs.get() > rhs.get(); +// } +// +// template +// bool operator>=(type_safe_arithmetic lhs, type_safe_arithmetic rhs) +// { +// return lhs.get() >= rhs.get(); +// } +// +// template +// bool operator<(type_safe_arithmetic lhs, type_safe_arithmetic rhs) +// { +// return lhs.get() < rhs.get(); +// } +// +// template +// bool operator<=(type_safe_arithmetic lhs, type_safe_arithmetic rhs) +// { +// return lhs.get() <= rhs.get(); +// } +// +// template +// type_safe_arithmetic operator+(type_safe_arithmetic lhs, +// type_safe_arithmetic rhs) { +// return type_safe_arithmetic{lhs.get() + rhs.get()}; +// } + +template +auto operator+=(type_safe_arithmetic& lhs, type_safe_arithmetic rhs) + -> type_safe_arithmetic& { + lhs.set(lhs.get() + rhs.get()); + return lhs; +} + +// template +// type_safe_arithmetic operator-(type_safe_arithmetic lhs, +// type_safe_arithmetic rhs) { +// return type_safe_arithmetic{lhs.get() - rhs.get()}; +// } +// +// template +// type_safe_arithmetic& operator-=(type_safe_arithmetic& lhs, +// type_safe_arithmetic rhs) { +// lhs.set(lhs.get() - rhs.get()); +// return lhs; +// } +// +// template +// type_safe_arithmetic operator*(type_safe_arithmetic lhs, +// typename TAG::value_t rhs) { +// return type_safe_arithmetic{lhs.get() - rhs}; +// } +// +// template +// type_safe_arithmetic& operator*=(type_safe_arithmetic& lhs, +// typename TAG::value_t rhs) { +// lhs.set(lhs.get() * rhs); +// return lhs; +// } +// +// template +// type_safe_arithmetic operator/(type_safe_arithmetic lhs, +// typename TAG::value_t rhs) { +// return type_safe_arithmetic{lhs.get() / rhs}; +// } +// +// template +// type_safe_arithmetic& operator/=(type_safe_arithmetic& lhs, +// typename TAG::value_t rhs) { +// lhs.set(lhs.get() / rhs); +// return lhs; +// } +// +// template +// type_safe_arithmetic& operator++(type_safe_arithmetic& a) { +// return a += type_safe_arithmetic{1}; +// } + +template +auto operator++(type_safe_arithmetic& a, int) + -> type_safe_arithmetic { + auto r = a; + a += type_safe_arithmetic{1}; + return r; +} + +// template +// type_safe_arithmetic& operator--(type_safe_arithmetic& a) { +// return a -= type_safe_arithmetic{1}; +// } +// +// template +// type_safe_arithmetic operator--(type_safe_arithmetic& a, int) { +// auto r = a; +// a += type_safe_arithmetic{1}; +// return r; +// } + +#endif // INCLUDED_SRC_UTILS_CPP_TYPE_SAFE_ARITHMETIC_HPP -- cgit v1.2.3