11#include <fmt/format.h>
12#include <userver/utils/fmt_compat.hpp>
14#include <boost/functional/hash_fwd.hpp>
16#include <userver/formats/common/meta.hpp>
17#include <userver/utils/meta.hpp>
18#include <userver/utils/underlying_value.hpp>
19#include <userver/utils/void_t.hpp>
21USERVER_NAMESPACE_BEGIN
40 return utils::UnderlyingValue(op) & utils::UnderlyingValue(mask);
44 return StrongTypedefOps{utils::UnderlyingValue(op1) |
45 utils::UnderlyingValue(op2)};
83template <
class Tag,
class T,
91template <
class T,
class = void_t<>>
92struct InitializerListImpl {
94 using type = DoNotMatch;
113struct StrongTypedefTag {};
116using IsStrongTypedef =
117 std::conjunction<std::is_base_of<StrongTypedefTag, T>,
118 std::is_convertible<T&, StrongTypedefTag&>>;
122template <
typename T,
typename Void>
127template <
typename T,
typename Void>
132constexpr void CheckIfAllowsLogging() {
133 static_assert(IsStrongTypedef<T>::value);
136 static_assert(!
sizeof(T),
"Trying to print a non-loggable StrongTypedef");
140template <
class To,
class... Args>
141constexpr bool IsStrongToStrongConversion()
noexcept {
142 static_assert(IsStrongTypedef<To>::value);
144 if constexpr (
sizeof...(Args) == 1) {
145 using FromDecayed = std::decay_t<
decltype((std::declval<Args>(), ...))>;
146 if constexpr (IsStrongTypedef<FromDecayed>::value) {
148 return !std::is_same_v<FromDecayed, To> &&
149 (std::is_same_v<
typename FromDecayed::UnderlyingType,
150 typename To::UnderlyingType> ||
151 std::is_arithmetic_v<
typename To::UnderlyingType>);
164 static_assert(!std::is_reference<T>::value);
165 static_assert(!std::is_pointer<T>::value);
167 static_assert(!std::is_reference<Tag>::value);
168 static_assert(!std::is_pointer<Tag>::value);
171 using UnderlyingType = T;
175 StrongTypedef() =
default;
176 StrongTypedef(
const StrongTypedef&) =
default;
177 StrongTypedef(StrongTypedef&&)
noexcept =
default;
178 StrongTypedef& operator=(
const StrongTypedef&) =
default;
179 StrongTypedef& operator=(StrongTypedef&&)
noexcept =
default;
181 constexpr StrongTypedef(impl::strong_typedef::InitializerList<T> lst)
184 template <
typename... Args,
185 typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
186 explicit constexpr StrongTypedef(Args&&... args)
noexcept(
187 noexcept(T(std::forward<Args>(args)...)))
188 : data_(std::forward<Args>(args)...) {
190 static_assert(!IsStrongToStrongConversion<StrongTypedef, Args...>(),
191 "Attempt to convert one StrongTypedef to another. Use "
192 "utils::StrongCast to do that");
195 explicit constexpr operator
const T&()
const&
noexcept {
return data_; }
196 explicit constexpr operator T() &&
noexcept {
return std::move(data_); }
197 explicit constexpr operator T&() &
noexcept {
return data_; }
199 constexpr const T& GetUnderlying()
const&
noexcept {
return data_; }
200 constexpr T GetUnderlying() &&
noexcept {
return std::move(data_); }
201 constexpr T& GetUnderlying() &
noexcept {
return data_; }
203 template <
typename Void =
void,
204 typename = impl::strong_typedef::EnableIfRange<T, Void>>
206 return std::begin(data_);
209 template <
typename Void =
void,
210 typename = impl::strong_typedef::EnableIfRange<T, Void>>
212 return std::end(data_);
215 template <
typename Void =
void,
216 typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
218 return std::begin(data_);
221 template <
typename Void =
void,
222 typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
224 return std::end(data_);
227 template <
typename Void =
void,
228 typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
229 auto cbegin()
const {
230 return std::cbegin(data_);
233 template <
typename Void =
void,
234 typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
236 return std::cend(data_);
239 template <
typename Void =
void,
240 typename = impl::strong_typedef::EnableIfSizeable<
const T, Void>>
242 return std::size(data_);
245 auto empty()
const {
return data_.empty(); }
247 auto clear() {
return data_.clear(); }
250 decltype(
auto) operator[](Arg&& i) {
251 return data_[std::forward<Arg>(i)];
254 decltype(
auto) operator[](Arg&& i)
const {
255 return data_[std::forward<Arg>(i)];
265#define UTILS_STRONG_TYPEDEF_REL_OP(OPERATOR)
266 template <class Tag1, class T1, StrongTypedefOps Ops1, class Tag2, class T2,
267 StrongTypedefOps Ops2>
268 constexpr bool operator
OPERATOR(const StrongTypedef<Tag1, T1, Ops1>&,
269 const StrongTypedef<Tag2, T2, Ops2>&) {
270 static_assert(!sizeof(T1), "Comparing those StrongTypedefs is forbidden");
274 template <class Tag, class T, StrongTypedefOps Ops>
275 constexpr std::enable_if_t<Ops & StrongTypedefOps::kCompareStrong, bool>
276 operator
OPERATOR(const StrongTypedef<Tag, T, Ops>& lhs,
277 const StrongTypedef<Tag, T, Ops>& rhs) {
278 return lhs.GetUnderlying() OPERATOR rhs.GetUnderlying();
281 template <class Tag, class T, StrongTypedefOps Ops, class Other>
282 constexpr impl::strong_typedef::EnableTransparentCompare<Tag, T, Ops, Other>
283 operator
OPERATOR(const StrongTypedef<Tag, T, Ops>& lhs, const Other& rhs) {
284 return lhs.GetUnderlying() OPERATOR rhs;
287 template <class Tag, class T, StrongTypedefOps Ops, class Other>
288 constexpr impl::strong_typedef::EnableTransparentCompare<Tag, T, Ops, Other>
289 operator
OPERATOR(const Other& lhs, const StrongTypedef<Tag, T, Ops>& rhs) {
290 return lhs OPERATOR rhs.GetUnderlying();
300#undef UTILS_STRONG_TYPEDEF_REL_OP
305std::ostream& operator<<(std::ostream& os,
306 const StrongTypedef<Tag, T, Ops>& v) {
307 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, T, Ops>>();
308 return os << v.GetUnderlying();
313 const StrongTypedef<Tag, T, Ops>& v) {
314 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, T, Ops>>();
315 return os << v.GetUnderlying();
320constexpr decltype(
auto) UnderlyingValue(
321 const StrongTypedef<Tag, T, Ops>& v)
noexcept {
322 return v.GetUnderlying();
326constexpr T UnderlyingValue(StrongTypedef<Tag, T, Ops>&& v)
noexcept {
327 return std::move(v).GetUnderlying();
336template <
typename T,
typename Value>
337std::enable_if_t<formats::common::kIsFormatValue<Value> && IsStrongTypedef<T>{},
339Parse(
const Value& source, formats::parse::To<T>) {
340 return T{source.
template As<
typename T::UnderlyingType>()};
343template <
typename T,
typename Value>
344std::enable_if_t<IsStrongTypedef<T>{}, Value> Serialize(
345 const T& object, formats::serialize::To<Value>) {
347 return typename Value::Builder(object.GetUnderlying()).ExtractValue();
350template <
typename T,
typename StringBuilder>
351std::enable_if_t<IsStrongTypedef<T>{}> WriteToStream(
const T& object,
354 WriteToStream(object.GetUnderlying(), sw);
358std::string ToString(
const StrongTypedef<Tag, std::string, Ops>& object) {
359 impl::strong_typedef::CheckIfAllowsLogging<
360 StrongTypedef<Tag, std::string, Ops>>();
361 return object.GetUnderlying();
365 std::enable_if_t<meta::kIsInteger<T>,
bool> =
true>
366std::string ToString(
const StrongTypedef<Tag, T, Ops>& object) {
367 impl::strong_typedef::CheckIfAllowsLogging<
368 StrongTypedef<Tag, std::string, Ops>>();
369 return std::to_string(object.GetUnderlying());
373 std::enable_if_t<std::is_floating_point_v<T>,
bool> =
true>
374std::string ToString(
const StrongTypedef<Tag, T, Ops>& object) {
375 impl::strong_typedef::CheckIfAllowsLogging<
376 StrongTypedef<Tag, std::string, Ops>>();
377 return fmt::format(
"{}", object.GetUnderlying());
387constexpr Target
StrongCast(
const StrongTypedef<Tag, T, Ops, Enable>& src) {
388 static_assert(IsStrongTypedef<Target>{},
389 "Expected strong typedef as target type");
390 static_assert(std::is_convertible_v<T,
typename Target::UnderlyingType>,
391 "Source strong typedef underlying type must be convertible to "
392 "target's underlying type");
393 return Target{src.GetUnderlying()};
398constexpr Target StrongCast(StrongTypedef<Tag, T, Ops, Enable>&& src) {
399 static_assert(IsStrongTypedef<Target>{},
400 "Expected strong typedef as target type");
401 static_assert(std::is_convertible_v<T,
typename Target::UnderlyingType>,
402 "Source strong typedef underlying type must be convertible to "
403 "target's underlying type");
404 return Target{std::move(src).GetUnderlying()};
408std::size_t hash_value(
const StrongTypedef<Tag, T, Ops>& v) {
409 return boost::hash<T>{}(v.GetUnderlying());
416template <
class Tag,
class T>
417using NonLoggable = StrongTypedef<
418 Tag, T, StrongTypedefOps::kCompareStrong | StrongTypedefOps::kNonLoggable>;
425template <
class Tag,
class T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
426struct std::hash<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>
428 std::size_t operator()(
429 const USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>& v)
const
431 std::declval<
const std::hash<T>>()(std::declval<
const T&>()))) {
432 return std::hash<T>::operator()(v.GetUnderlying());
437template <
class T,
class Char>
438struct fmt::formatter<
439 T, Char, std::enable_if_t<USERVER_NAMESPACE::utils::IsStrongTypedef<T>{}>>
440 : fmt::formatter<
typename T::UnderlyingType, Char> {
441 template <
typename FormatContext>
443 USERVER_NAMESPACE::utils::impl::
strong_typedef::CheckIfAllowsLogging<T>();
444 return fmt::formatter<
typename T::UnderlyingType, Char>::format(
445 v.GetUnderlying(), ctx);