Skip to content

Commit

Permalink
Improve addressof()
Browse files Browse the repository at this point in the history
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
  • Loading branch information
phprus committed Aug 4, 2023
1 parent f409fe8 commit ce40b01
Showing 1 changed file with 52 additions and 14 deletions.
66 changes: 52 additions & 14 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@
# define FMT_CONSTEXPR
#endif

#if FMT_MSC_VERSION && _MSVC_FULL_VER >= 190024215
# define FMT_HAS_BUILTIN_ADDRESSOF
#elif FMT_GCC_VERSION >= 700
# define FMT_HAS_BUILTIN_ADDRESSOF
#elif defined(__has_builtin)
# if __has_builtin(__builtin_addressof)
# define FMT_HAS_BUILTIN_ADDRESSOF
# endif
#endif

#if FMT_CPLUSPLUS >= 201703L && defined(FMT_HAS_BUILTIN_ADDRESSOF)
# define FMT_CONSTEXPR_BUILTIN_ADDRESSOF constexpr
#else
# define FMT_CONSTEXPR_BUILTIN_ADDRESSOF
#endif

#if ((FMT_CPLUSPLUS >= 202002L) && \
(!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
(FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
Expand Down Expand Up @@ -1233,6 +1249,23 @@ template <typename Context> struct custom_value {
void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
};

#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl {
using type = void;
};
template <typename... T> using void_t = typename void_t_impl<T...>::type;
#else
template <typename...> using void_t = void;
#endif

template <typename T, typename Enable = void>
struct has_address_operator : std::false_type {};

template <typename T>
struct has_address_operator<T, void_t<decltype(&std::declval<T>())>>
: std::true_type {};

// A formatting argument value.
template <typename Context> class value {
public:
Expand Down Expand Up @@ -1281,22 +1314,35 @@ template <typename Context> class value {
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
: named_args{args, size} {}

template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
// Workaround for types with overloaded operator&.
// std::addressof is non-constexpr before C++17.
template <typename T, FMT_ENABLE_IF(has_address_operator<T>::value)>
FMT_CONSTEXPR_BUILTIN_ADDRESSOF FMT_INLINE value(T& val) {
using value_type = remove_const_t<T>;
#if defined(_LIBCPP_VERSION)
// Workaround for formatter<std::vector<bool>::reference, Char>.
// libc++ std::vector<bool>::reference has an overloaded operator&.
// std::addressof is non-constexpr before C++17.
#ifdef FMT_HAS_BUILTIN_ADDRESSOF
custom.value = const_cast<value_type*>(__builtin_addressof(val));
#else
custom.value = const_cast<value_type*>(&val);
custom.value = reinterpret_cast<value_type*>(
const_cast<char*>(&reinterpret_cast<const volatile char&>(val)));
#endif
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`.
custom.format = format_custom_arg<
value_type, typename Context::template formatter_type<value_type>>;
}

template <typename T, FMT_ENABLE_IF(!has_address_operator<T>::value)>
FMT_CONSTEXPR FMT_INLINE value(T& val) {
using value_type = remove_const_t<T>;
custom.value = const_cast<value_type*>(&val);
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`.
custom.format = format_custom_arg<
value_type, typename Context::template formatter_type<value_type>>;
}

value(unformattable);
value(unformattable_char);
value(unformattable_pointer);
Expand Down Expand Up @@ -1509,14 +1555,6 @@ FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
return detail::copy_str<Char>(rng.begin(), rng.end(), out);
}

#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl { using type = void; };
template <typename... T> using void_t = typename void_t_impl<T...>::type;
#else
template <typename...> using void_t = void;
#endif

template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};

Expand Down

0 comments on commit ce40b01

Please sign in to comment.