13#include <userver/compiler/impl/lifetime.hpp>
14#include <userver/formats/common/meta.hpp>
15#include <userver/utils/fmt_compat.hpp>
16#include <userver/utils/meta.hpp>
17#include <userver/utils/strong_typedef_fwd.hpp>
18#include <userver/utils/underlying_value.hpp>
21#include <fmt/format.h>
22#include <boost/functional/hash_fwd.hpp>
29std::string PrintToString(
const T& value);
33USERVER_NAMESPACE_BEGIN
41constexpr bool operator&(StrongTypedefOps op, StrongTypedefOps mask)
noexcept {
45constexpr auto operator|(StrongTypedefOps op1, StrongTypedefOps op2)
noexcept {
50namespace impl::strong_typedef {
53struct InitializerListImpl {
55 using type = DoNotMatch;
59requires requires {
typename T::value_type; }
60struct InitializerListImpl<T> {
61 using type = std::initializer_list<
typename T::value_type>;
67using InitializerList =
typename InitializerListImpl<T>::type;
69template <
class T,
class U>
70constexpr void CheckStrongCompare() {
72 std::is_same_v<
typename T::UnderlyingType,
typename U::UnderlyingType> &&
73 std::is_same_v<
typename T::TagType,
typename U::TagType> && T::kOps == U::kOps &&
74 (T::kOps & StrongTypedefOps::kCompareStrong),
75 "Comparing those StrongTypedefs is forbidden"
79template <
class Typedef>
80constexpr void CheckTransparentCompare() {
82 Typedef::kOps & StrongTypedefOps::kCompareTransparentOnly,
83 "Comparing this StrongTypedef to a raw value is forbidden"
88const auto& UnwrapIfStrongTypedef(
const T& value) {
89 if constexpr (IsStrongTypedef<T>) {
90 return value.GetUnderlying();
99concept Range = meta::
kIsRange<T> && !meta::kIsInstantiationOf<std::basic_string, std::remove_const_t<T>>;
102constexpr void CheckIfAllowsLogging() {
103 static_assert(IsStrongTypedef<T>);
105 if constexpr (T::kOps & StrongTypedefOps::kNonLoggable) {
106 static_assert(!
sizeof(T),
"Trying to print a non-loggable StrongTypedef");
110template <
class To,
class... Args>
111constexpr bool IsStrongToStrongConversion()
noexcept {
112 static_assert(IsStrongTypedef<To>);
114 if constexpr (
sizeof...(Args) == 1) {
115 using FromDecayed = std::decay_t<
decltype((std::declval<Args>(), ...))>;
116 if constexpr (IsStrongTypedef<FromDecayed>) {
118 return !std::is_same_v<FromDecayed, To> &&
119 (std::is_same_v<
typename FromDecayed::UnderlyingType,
typename To::UnderlyingType> ||
120 std::is_arithmetic_v<
typename To::UnderlyingType>);
155template <
class Tag,
class T, StrongTypedefOps Ops>
156class StrongTypedef :
public impl::strong_typedef::StrongTypedefTag {
157 static_assert(!std::is_reference_v<T>);
158 static_assert(!std::is_pointer_v<T>);
160 static_assert(!std::is_reference_v<Tag>);
161 static_assert(!std::is_pointer_v<Tag>);
164 using UnderlyingType = T;
166 static constexpr StrongTypedefOps kOps = Ops;
168 StrongTypedef() =
default;
169 StrongTypedef(
const StrongTypedef&) =
default;
170 StrongTypedef(StrongTypedef&&)
noexcept =
default;
171 StrongTypedef& operator=(
const StrongTypedef&) =
default;
172 StrongTypedef& operator=(StrongTypedef&&)
noexcept =
default;
174 constexpr StrongTypedef(impl::strong_typedef::InitializerList<T> lst)
178 template <
typename... Args>
179 requires std::is_constructible_v<T, Args...>
180 constexpr explicit StrongTypedef(Args&&... args)
noexcept(
noexcept(T(std::forward<Args>(args)...)))
181 : data_(std::forward<Args>(args)...)
183 using impl::strong_typedef::IsStrongToStrongConversion;
185 !IsStrongToStrongConversion<StrongTypedef, Args...>(),
186 "Attempt to convert one StrongTypedef to another. Use "
187 "utils::StrongCast to do that"
191 constexpr explicit operator
const T&()
const&
noexcept USERVER_IMPL_LIFETIME_BOUND {
return data_; }
192 constexpr explicit operator T() &&
noexcept {
return std::move(data_); }
193 constexpr explicit operator T&() &
noexcept USERVER_IMPL_LIFETIME_BOUND {
return data_; }
195 constexpr const T& GetUnderlying()
const&
noexcept USERVER_IMPL_LIFETIME_BOUND {
return data_; }
196 constexpr T GetUnderlying() &&
noexcept {
return std::move(data_); }
197 constexpr T& GetUnderlying() &
noexcept USERVER_IMPL_LIFETIME_BOUND {
return data_; }
200 requires impl::strong_typedef::Range<T>
202 return std::begin(data_);
206 requires impl::strong_typedef::Range<T>
208 return std::end(data_);
212 requires impl::strong_typedef::Range<T>
214 return std::begin(data_);
218 requires impl::strong_typedef::Range<T>
220 return std::end(data_);
224 requires impl::strong_typedef::Range<T>
226 return std::cbegin(data_);
230 requires impl::strong_typedef::Range<T>
232 return std::cend(data_);
238 return std::size(data_);
241 auto empty()
const {
return data_.empty(); }
243 auto clear() {
return data_.clear(); }
246 decltype(
auto) operator[](Arg&& i) {
247 return data_[std::forward<Arg>(i)];
250 decltype(
auto) operator[](Arg&& i)
const {
251 return data_[std::forward<Arg>(i)];
261#define UTILS_STRONG_TYPEDEF_REL_OP(OPERATOR)
266 impl::strong_typedef::IsStrongTypedef<T> || impl::strong_typedef::IsStrongTypedef<U>,
269 constexpr auto operator
OPERATOR(const T& lhs, const U& rhs)
270 ->decltype(impl::strong_typedef::UnwrapIfStrongTypedef(lhs
271 ) OPERATOR impl::strong_typedef::UnwrapIfStrongTypedef(rhs)) {
272 if constexpr (impl::strong_typedef::IsStrongTypedef<T>) {
273 if constexpr (impl::strong_typedef::IsStrongTypedef<U>) {
274 impl::strong_typedef::CheckStrongCompare<T, U>();
275 return lhs.GetUnderlying() OPERATOR rhs.GetUnderlying();
277 impl::strong_typedef::CheckTransparentCompare<T>();
278 return lhs.GetUnderlying() OPERATOR rhs;
281 impl::strong_typedef::CheckTransparentCompare<U>();
282 return lhs OPERATOR rhs.GetUnderlying();
298#undef UTILS_STRONG_TYPEDEF_REL_OP
302template <
class Tag,
class T, StrongTypedefOps Ops>
303std::ostream& operator<<(std::ostream& os,
const StrongTypedef<Tag, T, Ops>& v) {
304 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, T, Ops>>();
305 return os << v.GetUnderlying();
308template <
class Tag,
class T, StrongTypedefOps Ops>
309logging::LogHelper& operator<<(logging::LogHelper& os,
const StrongTypedef<Tag, T, Ops>& v) {
310 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, T, Ops>>();
311 return os << v.GetUnderlying();
315template <
class Tag,
class T, StrongTypedefOps Ops>
316constexpr decltype(
auto) UnderlyingValue(
const StrongTypedef<Tag, T, Ops>& v)
noexcept {
317 return v.GetUnderlying();
320template <
class Tag,
class T, StrongTypedefOps Ops>
321constexpr T UnderlyingValue(StrongTypedef<Tag, T, Ops>&& v)
noexcept {
322 return std::move(v).GetUnderlying();
325constexpr bool IsStrongTypedefLoggable(StrongTypedefOps ops) {
return !(ops & StrongTypedefOps::kNonLoggable); }
329template <impl::strong_typedef::IsStrongTypedef T, formats::common::IsFormatValue Value>
330T Parse(
const Value& source, formats::
parse::
To<T>) {
331 return T{source.
template As<
typename T::UnderlyingType>()};
334template <impl::strong_typedef::IsStrongTypedef T,
typename Value>
335Value Serialize(
const T& object, formats::
serialize::
To<Value>) {
336 impl::strong_typedef::CheckIfAllowsLogging<T>();
337 return typename Value::Builder(object.GetUnderlying()).ExtractValue();
340template <impl::strong_typedef::IsStrongTypedef T,
typename StringBuilder>
341void WriteToStream(
const T& object, StringBuilder& sw) {
342 impl::strong_typedef::CheckIfAllowsLogging<T>();
343 WriteToStream(object.GetUnderlying(), sw);
346template <
typename Tag, StrongTypedefOps Ops>
347std::string ToString(
const StrongTypedef<Tag, std::string, Ops>& object) {
348 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, std::string, Ops>>();
349 return object.GetUnderlying();
352template <
typename Tag, meta::kIsInteger T, StrongTypedefOps Ops>
353std::string ToString(
const StrongTypedef<Tag, T, Ops>& object) {
354 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, std::string, Ops>>();
355 return std::to_string(object.GetUnderlying());
358template <
typename Tag, std::floating_point T, StrongTypedefOps Ops>
359std::string ToString(
const StrongTypedef<Tag, T, Ops>& object) {
360 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, std::string, Ops>>();
361 return fmt::to_string(object.GetUnderlying());
369template <
typename Target,
typename Tag,
typename T, StrongTypedefOps Ops>
370constexpr Target
StrongCast(
const StrongTypedef<Tag, T, Ops>& src) {
371 static_assert(impl::strong_typedef::IsStrongTypedef<Target>,
"Expected strong typedef as target type");
373 std::is_convertible_v<T,
typename Target::UnderlyingType>,
374 "Source strong typedef underlying type must be convertible to "
375 "target's underlying type"
377 return Target{src.GetUnderlying()};
380template <
typename Target,
typename Tag,
typename T, StrongTypedefOps Ops>
381constexpr Target StrongCast(StrongTypedef<Tag, T, Ops>&& src) {
382 static_assert(impl::strong_typedef::IsStrongTypedef<Target>,
"Expected strong typedef as target type");
384 std::is_convertible_v<T,
typename Target::UnderlyingType>,
385 "Source strong typedef underlying type must be convertible to "
386 "target's underlying type"
388 return Target{std::move(src).GetUnderlying()};
391template <
class Tag,
class T, StrongTypedefOps Ops>
392std::size_t hash_value(
const StrongTypedef<Tag, T, Ops>& v) {
393 return boost::hash<T>{}(v.GetUnderlying());
397template <
class Tag,
class T, StrongTypedefOps Ops>
398void PrintTo(
const StrongTypedef<Tag, T, Ops>& v, std::ostream* os) {
399 *os <<
testing::PrintToString(v.GetUnderlying());
406template <
class Tag,
class T>
407using NonLoggable = StrongTypedef<Tag, T, StrongTypedefOps::kCompareStrong | StrongTypedefOps::kNonLoggable>;
414template <
class Tag,
class T, USERVER_NAMESPACE::
utils::StrongTypedefOps Ops>
415struct std::
hash<USERVER_NAMESPACE::
utils::StrongTypedef<Tag, T, Ops>> : std::hash<T> {
416 std::size_t operator()(
const USERVER_NAMESPACE::
utils::StrongTypedef<Tag, T, Ops>& v
417 )
const noexcept(
noexcept(std::declval<
const std::hash<T>>()(std::declval<
const T&>()))) {
418 return std::hash<T>::operator()(v.GetUnderlying());
423template <USERVER_NAMESPACE::utils::impl::strong_typedef::IsStrongTypedef T,
class Char>
424struct fmt::formatter<T, Char> : fmt::formatter<
typename T::UnderlyingType, Char> {
425 template <
typename FormatContext>
427 USERVER_NAMESPACE::
utils::impl::strong_typedef::CheckIfAllowsLogging<T>();
428 return fmt::formatter<
typename T::UnderlyingType, Char>::format(v.GetUnderlying(), ctx);