From b947c15eda3c6f0a57a76718cf4e5032c499eb89 Mon Sep 17 00:00:00 2001 From: Baber Nawaz Date: Tue, 21 Oct 2025 15:45:48 +0100 Subject: [PATCH] feat(util): Add const-propagating wrapper Can be used with pointers/smart pointers/custom types SDB-9943 --- toolbox/util/Concepts.hpp | 3 + toolbox/util/PropagateConst.hpp | 125 ++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 toolbox/util/PropagateConst.hpp diff --git a/toolbox/util/Concepts.hpp b/toolbox/util/Concepts.hpp index 61d2d50ba..748493c30 100644 --- a/toolbox/util/Concepts.hpp +++ b/toolbox/util/Concepts.hpp @@ -41,6 +41,9 @@ concept Streamable = requires (T& os) { os.write(std::declval(), std::declval()); }; +template +concept Pointer = std::is_pointer_v; + } // namespace util } // namespace toolbox diff --git a/toolbox/util/PropagateConst.hpp b/toolbox/util/PropagateConst.hpp new file mode 100644 index 000000000..b0012bdc6 --- /dev/null +++ b/toolbox/util/PropagateConst.hpp @@ -0,0 +1,125 @@ +// The Reactive C++ Toolbox. +// Copyright (C) 2025 Reactive Markets Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TOOLBOX_UTIL_PROPAGATECONST_HPP +#define TOOLBOX_UTIL_PROPAGATECONST_HPP + +#include +#include + +#include + +namespace toolbox { +inline namespace util { + +// user may specialise this for custom types +template +struct pointed_underlying; + +template + requires requires(T x) { { x.get() } -> Pointer; } +struct pointed_underlying { + auto* operator()(T& obj) { return obj.get(); } +}; + +template +struct pointed_underlying { + T* operator()(T* obj) { return obj; } +}; + +template +struct pointed_underlying { + T* operator()(T& obj) { return std::addressof(obj); } +}; + +template +class propagate_const { + using pointed_type = std::remove_pointer_t< + decltype(pointed_underlying{}(std::declval())) + >; + + public: + propagate_const() = default; + ~propagate_const() = default; + + explicit propagate_const(T obj) + : obj_(std::move(obj)) + { + } + + propagate_const(propagate_const& o) = default; + propagate_const& operator=(propagate_const&) = default; + + propagate_const(const propagate_const&) = delete; + propagate_const& operator=(const propagate_const&) = delete; + + propagate_const(propagate_const&&) = default; + propagate_const& operator=(propagate_const&&) = default; + + propagate_const& operator=(T&& other) + { + obj_ = std::move(other); + return *this; + } + propagate_const& operator=(const T& other) + { + obj_ = other; + return *this; + } + + pointed_type* get() { return pointed_underlying{}(obj_); } + const pointed_type* get() const { return pointed_underlying{}(obj_); } + + pointed_type* operator->() { return get(); } + const pointed_type* operator->() const { return get(); } + + pointed_type& operator*() { return *get(); } + const pointed_type& operator*() const { return *get(); } + + operator pointed_type*() { return get(); } + operator const pointed_type*() const { return get(); } + + explicit operator bool() const { return get() != nullptr; } + + friend bool operator==(const propagate_const&, const propagate_const&) = default; + friend auto operator<=>(const propagate_const&, const propagate_const&) = default; + + template + friend auto operator<=>(const propagate_const& p, const U& o) + { + return p.obj_ <=> o; + } + template + friend auto operator==(const propagate_const& p, const U& o) + { + return p.obj_ == o; + } + + friend bool operator==(const propagate_const& p, std::nullptr_t) { return p.get() == nullptr; } + + friend void swap(propagate_const& a, propagate_const& b) + { + using std::swap; + swap(a.obj_, b.obj_); + } + + private: + T obj_; +}; + +} // namespace util +} // namespace toolbox + +#endif