1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#pragma once
5
6#include <QtCore/qglobal.h>
7
8#include <functional>
9#include <optional>
10#include <type_traits>
11
12// TODO: Express the documentation such that QDoc would be able to see
13// it and process it correctly. This probably means that we would like
14// to associate the definition with a namespace, albeit we could use
15// the header file too, and put the documentation in an empty cpp
16// file. This is delayed as there currently isn't much namespacing for
17// anything in QDoc and such a namespacing should be added gradually
18// and attentively.
19
20// TODO: Review the semantics for construction and optmize it. Should we copy the
21// value? Should we only allow rvalues?
22
23// TODO: There is an high chance that we will need to "compose"
24// refinitions later on when dealing, for example, with the basics of
25// user-provided paths.
26// For example, when requiring that each user-inputted path is purged
27// as per QFileInfo definition of purging.
28// For example, it might be that instead of passing QString around we
29// might pass some Path type that is a purged QString.
30// Then, any other refinement over paths will need to use that as a
31// base type.
32// To avoid the clutter that comes from that, if such will be the
33// case, we will need to change the definition of refine and value if
34// the passed in type was refined already.
35// That is, such that if we have:
36//
37// QDOC_REFINE_TYPE(QString, Path) { ... }
38// QDOC_REFINE_TYPE(Path, Foo) { ... }
39//
40// Foo refines a QString and Foo.value returns a QString. This should
41// in general be trivial as long as we add a way to identify, such as
42// a tag that refinements derive from, what type was declared through
43// QDOC_REFINED_TYPEDEF and what type was not.
44
45// TODO: Provide a way to generate a standard documentation for all
46// members of a type generated by QDOC_REFINED_TYPEDEF without having
47// to copy-paste include command everywhere.
48// The main problem of doing this is that the preprocessor strips
49// comments away, making it impossible to generate comments, and hence
50// QDoc documentation, with the preprocessor.
51
52/*!
53 * \macro QDOC_REFINED_TYPEDEF(_type, _name)
54 * \relates refined_typedef.hpp
55 *
56 * Declares a wrapper type for \c {_type}, with identifier \c {_name},
57 * that represents a subset of \c {_type} for which some conditions
58 * hold.
59 *
60 * For example:
61 *
62 * \code
63 QDOC_REFINED_TYPEDEF(std::size_t, Fin5) {
64 return (value < 5) : std::make_optional<Fin5>{value} : std::nullopt;
65 }
66 * \endcode
67 *
68 * Represents the subset of \c {std::size_t} that contains the value 0, 1,
69 * 2, 3 and 4, that is, the general finite set of cardinality 5.
70 *
71 * As the example shows, usages of the macro require some type, an
72 * identifier and some code.
73 *
74 * The type that is provided is the type that will be wrapped.
75 * Do note that we expect a type with no-qualifiers and that is not a
76 * pointer type. Types passed with those kind of qualifiers will be
77 * simplified to their base type.
78 *
79 * That is, for example, \c {int*}, \c {const int}, \c {const int&},
80 * \c {int&} all counts as \c {int}.
81 *
82 * The identifier that is passed is used as the name for the newly
83 * declared type that wraps the original type.
84 *
85 * The code block that is passed will be run when an instance of the
86 * newly created wrapper type is being obtained.
87 * If the wrapper type is T, the codeblock must return a \c
88 * {std::optional<T>}.
89 * The code block should perform any check that ensures that the
90 * guarantees provided by the wrapper type holds and return a value if
91 * such is the case, otherwise returning \c {std::nullopt}.
92 *
93 * Inside the code block, the identifier \e {value} is implicitly
94 * bound to an element of the wrapped type that the instance is being
95 * built from and for which the guarantees provided by the wrapper
96 * type must hold.
97 *
98 * When a call to QDOC_REFINED_TYPEDEF is successful, a type with the
99 * provided identifier is declared.
100 *
101 * Let T be a type declared trough a call of QDOC_REFINED_TYPEDEF and
102 * W be the type that it wraps.
103 *
104 * An instance of T can be obtained by calling T::refine with an
105 * element of W.
106 *
107 * If the element of W respects the guarantees that T provides, then
108 * the call will return an optional that contains an instance of T,
109 * othewise it will return an empty optional.
110 *
111 * When an instance of T is obtained, it will wrap the element of W that
112 * was used to obtain it.
113 *
114 * The wrapped value can be accessed trough the \c {value} method.
115 *
116 * For example, considering \c {Fin5}, we could obtain an instance of
117 * it as follows:
118 *
119 * \code
120 * auto instance = *(Fin5::refine(std::size_t{1}));
121 * \endcode
122 *
123 * With that instance available we can retrieve the original value as
124 * follows:
125 *
126 * \code
127 * instance.value(); // The value 1
128 * \endcode
129 */
130
131#define QDOC_REFINED_TYPEDEF(_type, _name) \
132 struct _name { \
133 public: \
134 using wrapped_type = std::remove_reference_t<std::remove_cv_t<std::remove_pointer_t<_type>>>; \
135 \
136 inline static constexpr auto has_equality_operator_v = std::is_invocable_r_v<bool, std::equal_to<>, wrapped_type, wrapped_type>; \
137 inline static constexpr auto has_less_than_operator_v = std::is_invocable_r_v<bool, std::less_equal<>, wrapped_type, wrapped_type>; \
138 inline static constexpr auto has_strictly_less_than_operator_v = std::is_invocable_r_v<bool, std::less<>, wrapped_type, wrapped_type>; \
139 inline static constexpr auto has_greater_than_operator_v = std::is_invocable_r_v<bool, std::greater_equal<>, wrapped_type, wrapped_type>; \
140 inline static constexpr auto has_strictly_greater_than_operator_v = std::is_invocable_r_v<bool, std::greater<>, wrapped_type, wrapped_type>; \
141 \
142 public: \
143 static std::optional<_name> refine(wrapped_type value); \
144 \
145 [[nodiscard]] const wrapped_type& value() const noexcept { return _value; } \
146 \
147 _name(const _name&) = default; \
148 _name& operator=(const _name&) = default; \
149 \
150 _name(_name&&) = default; \
151 _name& operator=(_name&&) = default; \
152 \
153 operator wrapped_type() const { return _value; } \
154 \
155 public: \
156 \
157 template< \
158 typename = std::enable_if_t< \
159 has_equality_operator_v \
160 > \
161 > \
162 bool operator==(const _name& rhs) const noexcept { return _value == rhs._value; } \
163 \
164 template< \
165 typename = std::enable_if_t< \
166 has_equality_operator_v \
167 > \
168 > \
169 bool operator!=(const _name& rhs) const noexcept { return !(_value == rhs._value); } \
170 \
171 template< \
172 typename = std::enable_if_t< \
173 has_less_than_operator_v \
174 > \
175 > \
176 bool operator<=(const _name& rhs) const noexcept { return _value <= rhs._value; } \
177 \
178 \
179 template< \
180 typename = std::enable_if_t< \
181 has_strictly_less_than_operator_v \
182 > \
183 > \
184 bool operator<(const _name& rhs) const noexcept { return _value < rhs._value; } \
185 \
186 template< \
187 typename = std::enable_if_t< \
188 has_greater_than_operator_v \
189 > \
190 > \
191 bool operator>=(const _name& rhs) const noexcept { return _value >= rhs._value; } \
192 \
193 template< \
194 typename = std::enable_if_t< \
195 has_strictly_greater_than_operator_v \
196 > \
197 > \
198 bool operator>(const _name& rhs) const noexcept { return _value > rhs._value; } \
199 \
200 private: \
201 _name(wrapped_type value) : _value{std::move(value)} {} \
202 \
203 private: \
204 wrapped_type _value; \
205 }; \
206 \
207 inline std::optional<_name> _name::refine(wrapped_type value)
208

source code of qttools/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef.h