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) | utils::UnderlyingValue(op2)};
88template <
class T,
class = void_t<>>
89struct InitializerListImpl {
91 using type = DoNotMatch;
104template <
class T,
class U>
105constexpr void CheckStrongCompare() {
107 std::is_same_v<
typename T::UnderlyingType,
typename U::UnderlyingType> &&
108 std::is_same_v<
typename T::TagType,
typename U::TagType> && T::kOps == U::kOps &&
110 "Comparing those StrongTypedefs is forbidden"
114template <
class Typedef>
115constexpr void CheckTransparentCompare() {
118 "Comparing this StrongTypedef to a raw value is forbidden"
122struct StrongTypedefTag {};
125using IsStrongTypedef =
126 std::conjunction<std::is_base_of<StrongTypedefTag, T>, std::is_convertible<T&, StrongTypedefTag&>>;
129const auto& UnwrapIfStrongTypedef(
const T& value) {
130 if constexpr (IsStrongTypedef<T>{}) {
131 return value.GetUnderlying();
139template <
typename T,
typename Void>
143template <
typename T,
typename Void>
147constexpr void CheckIfAllowsLogging() {
148 static_assert(IsStrongTypedef<T>::value);
151 static_assert(!
sizeof(T),
"Trying to print a non-loggable StrongTypedef");
155template <
class To,
class... Args>
156constexpr bool IsStrongToStrongConversion()
noexcept {
157 static_assert(IsStrongTypedef<To>::value);
159 if constexpr (
sizeof...(Args) == 1) {
160 using FromDecayed = std::decay_t<
decltype((std::declval<Args>(), ...))>;
161 if constexpr (IsStrongTypedef<FromDecayed>::value) {
163 return !std::is_same_v<FromDecayed, To> &&
164 (std::is_same_v<
typename FromDecayed::UnderlyingType,
typename To::UnderlyingType> ||
165 std::is_arithmetic_v<
typename To::UnderlyingType>);
177class StrongTypedef :
public impl::
strong_typedef::StrongTypedefTag {
178 static_assert(!std::is_reference<T>::value);
179 static_assert(!std::is_pointer<T>::value);
181 static_assert(!std::is_reference<Tag>::value);
182 static_assert(!std::is_pointer<Tag>::value);
185 using UnderlyingType = T;
189 StrongTypedef() =
default;
190 StrongTypedef(
const StrongTypedef&) =
default;
191 StrongTypedef(StrongTypedef&&)
noexcept =
default;
192 StrongTypedef& operator=(
const StrongTypedef&) =
default;
193 StrongTypedef& operator=(StrongTypedef&&)
noexcept =
default;
195 constexpr StrongTypedef(impl::strong_typedef::InitializerList<T> lst) : data_(lst) {}
197 template <
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
198 explicit constexpr StrongTypedef(Args&&... args)
noexcept(
noexcept(T(std::forward<Args>(args)...)))
199 : data_(std::forward<Args>(args)...) {
202 !IsStrongToStrongConversion<StrongTypedef, Args...>(),
203 "Attempt to convert one StrongTypedef to another. Use "
204 "utils::StrongCast to do that"
208 explicit constexpr operator
const T&()
const&
noexcept {
return data_; }
209 explicit constexpr operator T() &&
noexcept {
return std::move(data_); }
210 explicit constexpr operator T&() &
noexcept {
return data_; }
212 constexpr const T& GetUnderlying()
const&
noexcept {
return data_; }
213 constexpr T GetUnderlying() &&
noexcept {
return std::move(data_); }
214 constexpr T& GetUnderlying() &
noexcept {
return data_; }
216 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfRange<T, Void>>
218 return std::begin(data_);
221 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfRange<T, Void>>
223 return std::end(data_);
226 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
228 return std::begin(data_);
231 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
233 return std::end(data_);
236 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
237 auto cbegin()
const {
238 return std::cbegin(data_);
241 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfRange<
const T, Void>>
243 return std::cend(data_);
246 template <
typename Void =
void,
typename = impl::strong_typedef::EnableIfSizeable<
const T, Void>>
248 return std::size(data_);
251 auto empty()
const {
return data_.empty(); }
253 auto clear() {
return data_.clear(); }
256 decltype(
auto) operator[](Arg&& i) {
257 return data_[std::forward<Arg>(i)];
260 decltype(
auto) operator[](Arg&& i)
const {
261 return data_[std::forward<Arg>(i)];
271#define UTILS_STRONG_TYPEDEF_REL_OP(OPERATOR)
276 impl::strong_typedef::IsStrongTypedef<T>{} || impl::strong_typedef::IsStrongTypedef<U>{},
279 constexpr auto operator
OPERATOR(const T& lhs, const U& rhs)
280 ->decltype(impl::strong_typedef::UnwrapIfStrongTypedef(lhs)
281 OPERATOR impl::strong_typedef::UnwrapIfStrongTypedef(rhs)) {
282 if constexpr (impl::strong_typedef::IsStrongTypedef<T>{}) {
283 if constexpr (impl::strong_typedef::IsStrongTypedef<U>{}) {
284 impl::strong_typedef::CheckStrongCompare<T, U>();
285 return lhs.GetUnderlying() OPERATOR rhs.GetUnderlying();
287 impl::strong_typedef::CheckTransparentCompare<T>();
288 return lhs.GetUnderlying() OPERATOR rhs;
291 impl::strong_typedef::CheckTransparentCompare<U>();
292 return lhs OPERATOR rhs.GetUnderlying();
303#if __cpp_lib_three_way_comparison >= 201907L
304UTILS_STRONG_TYPEDEF_REL_OP(<=>)
307#undef UTILS_STRONG_TYPEDEF_REL_OP
312std::ostream& operator<<(std::ostream& os,
const StrongTypedef<Tag, T, Ops>& v) {
313 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, T, Ops>>();
314 return os << v.GetUnderlying();
318logging::LogHelper& operator<<(
logging::LogHelper& os,
const StrongTypedef<Tag, T, Ops>& v) {
319 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, T, Ops>>();
320 return os << v.GetUnderlying();
325constexpr decltype(
auto) UnderlyingValue(
const StrongTypedef<Tag, T, Ops>& v)
noexcept {
326 return v.GetUnderlying();
330constexpr T UnderlyingValue(StrongTypedef<Tag, T, Ops>&& v)
noexcept {
331 return std::move(v).GetUnderlying();
338template <
typename T,
typename Value>
339std::enable_if_t<formats::common::kIsFormatValue<Value> && IsStrongTypedef<T>{}, T>
340Parse(
const Value& source, formats::parse::To<T>) {
341 return T{source.
template As<
typename T::UnderlyingType>()};
344template <
typename T,
typename Value>
345std::enable_if_t<IsStrongTypedef<T>{}, Value> Serialize(
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, StringBuilder& sw) {
353 WriteToStream(object.GetUnderlying(), sw);
357std::string ToString(
const StrongTypedef<Tag, std::string, Ops>& object) {
358 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, std::string, Ops>>();
359 return object.GetUnderlying();
362template <
typename Tag,
typename T,
StrongTypedefOps Ops, std::enable_if_t<meta::kIsInteger<T>,
bool> =
true>
363std::string ToString(
const StrongTypedef<Tag, T, Ops>& object) {
364 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, std::string, Ops>>();
365 return std::to_string(object.GetUnderlying());
368template <
typename Tag,
typename T,
StrongTypedefOps Ops, std::enable_if_t<std::is_floating_point_v<T>,
bool> =
true>
369std::string ToString(
const StrongTypedef<Tag, T, Ops>& object) {
370 impl::strong_typedef::CheckIfAllowsLogging<StrongTypedef<Tag, std::string, Ops>>();
371 return fmt::format(
"{}", object.GetUnderlying());
379template <
typename Target,
typename Tag,
typename T,
StrongTypedefOps Ops,
typename Enable>
380constexpr Target
StrongCast(
const StrongTypedef<Tag, T, Ops, Enable>& src) {
381 static_assert(IsStrongTypedef<Target>{},
"Expected strong typedef as target type");
383 std::is_convertible_v<T,
typename Target::UnderlyingType>,
384 "Source strong typedef underlying type must be convertible to "
385 "target's underlying type"
387 return Target{src.GetUnderlying()};
390template <
typename Target,
typename Tag,
typename T,
StrongTypedefOps Ops,
typename Enable>
391constexpr Target StrongCast(StrongTypedef<Tag, T, Ops, Enable>&& src) {
392 static_assert(IsStrongTypedef<Target>{},
"Expected strong typedef as target type");
394 std::is_convertible_v<T,
typename Target::UnderlyingType>,
395 "Source strong typedef underlying type must be convertible to "
396 "target's underlying type"
398 return Target{std::move(src).GetUnderlying()};
402std::size_t hash_value(
const StrongTypedef<Tag, T, Ops>& v) {
403 return boost::hash<T>{}(v.GetUnderlying());
410template <
class Tag,
class T>
411using NonLoggable = StrongTypedef<Tag, T, StrongTypedefOps::kCompareStrong | StrongTypedefOps::kNonLoggable>;
418template <
class Tag,
class T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
419struct std::hash<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> : std::hash<T> {
420 std::size_t operator()(
const USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>& v)
const
421 noexcept(
noexcept(std::declval<
const std::hash<T>>()(std::declval<
const T&>()))) {
422 return std::hash<T>::operator()(v.GetUnderlying());
427template <
class T,
class Char>
428struct fmt::formatter<T, Char, std::enable_if_t<USERVER_NAMESPACE::utils::IsStrongTypedef<T>{}>>
429 : fmt::formatter<
typename T::UnderlyingType, Char> {
430 template <
typename FormatContext>
432 USERVER_NAMESPACE::utils::impl::strong_typedef::CheckIfAllowsLogging<T>();
433 return fmt::formatter<
typename T::UnderlyingType, Char>::format(v.GetUnderlying(), ctx);