Flow 1.0.0
Flow project: Full implementation reference.
shared_ptr_alias_holder.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22
23namespace flow::util
24{
25
26// Classes.
27
28/**
29 * Convenience class template that endows the given subclass `T` with nested aliases `Ptr` and `Const_ptr`
30 * aliased to `shared_ptr<T>` and `shared_ptr<const T>` respectively. In addition a few syntactic-sugary `static`
31 * methods are provided, in particular to perform shared pointer versions of `static_cast<T*>` and
32 * `dynamic_cast<T*>`.
33 *
34 * ### When to use ###
35 * The following assumes you are familiar with `shared_ptr` (`boost::` and `std::` are basically equivalent).
36 * Mechanically speaking, if class `C` subclasses Shared_ptr_alias_holder, then, simply, you gain a shorter way
37 * to write `boost::shared_ptr<C>` and `boost::shared_ptr<const C>`; plus briefer ways to cast down-class
38 * polymorphic `shared_ptr`s to those types (e.g., if `C2` derives from `C`, and one has a `shared_ptr<C2>` but
39 * needs a `shared_ptr<C>`). It is prettier and more pleasant to write `C::Ptr`, `C::Const_ptr`, `C::ptr_cast(p)`,
40 * etc., which may not seem like much, but in our experience it adds up over time. (Some coders aren't familiar with
41 * `{boost|std}::{static|dynamic}_pointer_cast()`, for example, so it's nice to provide equivalents "out of the box.")
42 *
43 * However, in addition to these mechanical niceties, subclassing Shared_ptr_alias_holder indicates the
44 * intention that the inheriting class `C` is to be used with the *shared ownership pattern*: essentially,
45 * that:
46 * -# `C`s are passed around by pointer, not by value;
47 * -# a `C` is generated via the factory pattern only and returned pre-wrapped in a `shared_ptr`; and
48 * -# a `C` is to be destroyed automatically once all outside *and* inside (to `C` code itself) references to that `C`
49 * have disappeared.
50 *
51 * It may be tempting to derive from Shared_ptr_alias_holder for every class ever: who doesn't like syntactic sugar?
52 * We recommend restraint, however. We recommend you do so if and only if `C` is indeed intended to be used per
53 * the above 3 bullet points (shared ownership pattern).
54 *
55 * For example, net_flow::Node is just a (rather big) class,
56 * intended to be used in boring ways, such as on the stack; and so it lacks any `Node::Ptr`. However, it stores
57 * (internally) tables of net_flow::Peer_socket objects, and it also returns them for public use
58 * via factory methods such as `Node::connect() -> Peer_socket::Ptr`. A net_flow::Peer_socket is never "out in the
59 * wild" without a wrapping `shared_ptr`; hence it -- unlike `net_flow::Node` -- derives from Shared_ptr_alias_holder.
60 * Once both the `Node` user has finishes with a given `Peer_socket`, and the `Node` has also deleted it from its
61 * internal structures, the `Peer_socket` is deleted automatically.
62 *
63 * In particular, the *mere* fact that you want to use *a* `shared_ptr<>` with some class `C` -- and there are many
64 * such situations -- is not sufficient to endow `C` with a Shared_ptr_alias_holder parent. It is more intended for
65 * when you want to use `C` with *only* `shared_ptr`s and in no other fashion. `shared_ptr` is still useful in
66 * a less expansive role, without Shared_ptr_alias_holder or the pattern the latter encourages.
67 *
68 * ### How to use ###
69 * `T` would typically `public`ly subclass `Shared_ptr_alias_holder<shared_ptr<T>, shared_ptr<T const>>`,
70 * where `shared_ptr` is either `boost::shared_ptr` or `std::shared_ptr` (or some other semantically identical
71 * ref-counting pointer class template). It's also reasonable to use `protected` or `private` inheritance. Behavior is
72 * undefined in any other use case.
73 *
74 * It is not recommended to have `C2` derive from us, if `C` already derives from us, and `C2` is a subclass of `C`
75 * (directly or indirectly). At that point `C2::Ptr` might refer to either alias, so compilation will likely fail
76 * when trying to use it, and browbeating it into working -- even if achieved -- is unlikely to result in syntactically
77 * pleasant code... which was the whole point in the first place. So, don't do that. If you need this stuff
78 * at multiple levels of a hierarchy, then it's probably best to just define the nested `Ptr` (etc.) manually.
79 * (It really isn't that hard in the first place... but reduced boiler-plate is reduced boiler-plate.)
80 *
81 * Also, if your `T` requires being an aggregate type -- such as if you intend to use `{}` direct member init --
82 * then you can't derive from anything, including us, and will have to make a manual `Ptr` (etc.). Note that's true
83 * of other similar utilities like `boost::noncopyable`.
84 *
85 * ### Rationale ###
86 * Why have this at all? Answer: It was tiring to keep manually making `using Ptr = shared_ptr<X>` nested aliases.
87 * Casting to `Ptr` in pretty fashion was also annoying to keep reimplementing. Down with boiler-plate!
88 *
89 * Why not take a single template arg, the target type `T`? Answer: That would've been fine, but it seemed nice to
90 * be able to support not just specifically one of `"{std|boost|...}::shared_ptr"` but all of them. Admittedly it's a
91 * bit uglier when declaring its use in the `class` or `struct` declaration; but since that's a one-time thing,
92 * trade-off seems worth it.
93 *
94 * @tparam Target_ptr
95 * Supposing the intended subclass is `T`, this must be `shared_ptr<T>`, where `shared_ptr` is a class template
96 * with `std::shared_ptr` semantics; namely at least `std::shared_ptr` itself and `boost::shared_ptr`.
97 * @tparam Const_target_ptr
98 * Same as `Target_ptr` but pointing to immutable object: `shared_ptr<T const>`. Note behavior is undefined
99 * if the `T` in `Const_target_ptr` isn't the same `T` as in `Target_ptr`; or if
100 * the `shared_ptr` template is different between the two (e.g., one uses `std::`, the other `boost::`).
101 * Update: It is now recommended to leave this at its default. Had the authors originally known about
102 * `pointer_traits::rebind` this would never have been a parameter in the first place. We are leaving it
103 * in for compatibility with existing code.
104 *
105 * @todo flow::util::Shared_ptr_alias_holder `Const_target_ptr` is deprecated and shall be always left at its default
106 * value in future code; eventually remove it entirely and hard-code the default value internally.
107 *
108 * @todo Add example usage snippets in Shared_ptr_alias_holder doc header, illustrating the pattern.
109 */
110template<typename Target_ptr,
111 typename Const_target_ptr> // Const_target_ptr is defaulted in _fwd.hpp.
113{
114public:
115 // Types.
116
117 /// Short-hand for ref-counted pointer to mutable values of type `Target_type::element_type` (a-la `T*`).
118 using Ptr = Target_ptr;
119 /// Short-hand for ref-counted pointer to immutable values of type `Target_type::element_type` (a-la `T const *`).
120 using Const_ptr = Const_target_ptr;
121
122 // Methods.
123
124 /**
125 * Provides syntactic-sugary way to perform a `static_pointer_cast<>` from a compatible smart pointer type `From_ptr`,
126 * typically `From_ptr::element_type` being in the same class hierarchy as `Target_ptr::element_type`.
127 *
128 * @tparam From_ptr
129 * See type of arg to `std::static_pointer_cast<>()` or `boost::static_pointer_cast<>()`.
130 * @param ptr_to_cast
131 * The smart pointer to cast.
132 * @return Result of the cast.
133 */
134 template<typename From_ptr>
135 static Ptr ptr_cast(const From_ptr& ptr_to_cast);
136
137 /**
138 * Identical to ptr_cast() but adds `const`-ness (immutability) to the pointed-to type. So if ptr_cast()
139 * casts from `A*` to `B*`, this casts from `A*` to `B const *` (a/k/a `const B*`).
140 *
141 * @tparam From_ptr
142 * See ptr_cast().
143 * @param ptr_to_cast
144 * The smart pointer to cast.
145 * @return Result of the cast.
146 */
147 template<typename From_ptr>
148 static Const_ptr const_ptr_cast(const From_ptr& ptr_to_cast);
149
150 /**
151 * Equivalent to ptr_cast() but a `dynamic_pointer_cast` instead of static.
152 *
153 * @tparam From_ptr
154 * See ptr_cast().
155 * @param ptr_to_cast
156 * The smart pointer to cast.
157 * @return Result of the cast. Recall this may be an empty (null) pointer.
158 */
159 template<typename From_ptr>
160 static Ptr dynamic_ptr_cast(const From_ptr& ptr_to_cast);
161
162 /**
163 * Identical to const_ptr_cast() but a `dynamic_pointer_cast` instead of static.
164 *
165 * @tparam From_ptr
166 * See ptr_cast().
167 * @param ptr_to_cast
168 * The smart pointer to cast.
169 * @return Result of the cast. Recall this may be an empty (null) pointer.
170 */
171 template<typename From_ptr>
172 static Const_ptr dynamic_const_ptr_cast(const From_ptr& ptr_to_cast);
173}; // class Shared_ptr_alias_holder
174
175// Template implementations.
176
177template<typename Target_ptr, typename Const_target_ptr>
178template<typename From_ptr>
181{
182 // This was taken, conceptually, from the `{static|dynamic|...}_pointer_cast` page of cppreference.com.
183 auto const raw_ptr_post_cast = static_cast<typename Target_ptr::element_type*>(ptr_to_cast.get());
184 return Target_ptr(ptr_to_cast, raw_ptr_post_cast);
185}
186
187template<typename Target_ptr, typename Const_target_ptr>
188template<typename From_ptr>
191{
192 // This was taken, conceptually, from the `{static|dynamic|...}_pointer_cast` page of cppreference.com.
193 auto const raw_ptr_post_cast = static_cast<typename Target_ptr::element_type const *>(ptr_to_cast.get());
194 return Const_target_ptr(ptr_to_cast, raw_ptr_post_cast);
195}
196
197template<typename Target_ptr, typename Const_target_ptr>
198template<typename From_ptr>
201{
202 // This was taken, conceptually, from the `{static|dynamic|...}_pointer_cast` page of cppreference.com.
203 auto const raw_ptr_post_cast = dynamic_cast<typename Target_ptr::element_type*>(ptr_to_cast.get());
204 return raw_ptr_post_cast ? Target_ptr(ptr_to_cast, raw_ptr_post_cast)
205 : Target_ptr();
206}
207
208template<typename Target_ptr, typename Const_target_ptr>
209template<typename From_ptr>
212{
213 // This was taken, conceptually, from the `{static|dynamic|...}_pointer_cast` page of cppreference.com.
214 auto const raw_ptr_post_cast = dynamic_cast<typename Target_ptr::element_type const *>(ptr_to_cast.get());
215 return raw_ptr_post_cast ? Const_target_ptr(ptr_to_cast, raw_ptr_post_cast)
216 : Const_target_ptr();
217}
218
219} // namespace flow::util
Convenience class template that endows the given subclass T with nested aliases Ptr and Const_ptr ali...
static Const_ptr dynamic_const_ptr_cast(const From_ptr &ptr_to_cast)
Identical to const_ptr_cast() but a dynamic_pointer_cast instead of static.
static Const_ptr const_ptr_cast(const From_ptr &ptr_to_cast)
Identical to ptr_cast() but adds const-ness (immutability) to the pointed-to type.
static Ptr ptr_cast(const From_ptr &ptr_to_cast)
Provides syntactic-sugary way to perform a static_pointer_cast<> from a compatible smart pointer type...
Target_ptr Ptr
Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*).
static Ptr dynamic_ptr_cast(const From_ptr &ptr_to_cast)
Equivalent to ptr_cast() but a dynamic_pointer_cast instead of static.
Const_target_ptr Const_ptr
Short-hand for ref-counted pointer to immutable values of type Target_type::element_type (a-la T cons...
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
Definition: basic_blob.hpp:29