Skip to content

Commit

Permalink
Added formatter for bit_reference-like types (#3570)
Browse files Browse the repository at this point in the history
* Add test for std::vector<bool>::reference

Co-authored-by: Felix <[email protected]>
Signed-off-by: Vladislav Shchapov <[email protected]>

* Add test for std::bitset<N>::reference

Signed-off-by: Vladislav Shchapov <[email protected]>

* Add test for const std::bitset<N>::reference and const std::vector<bool>::reference

Signed-off-by: Vladislav Shchapov <[email protected]>

* Add bit_reference-like formatter

Signed-off-by: Vladislav Shchapov <[email protected]>

* Use std::addressof

Signed-off-by: Vladislav Shchapov <[email protected]>

---------

Signed-off-by: Vladislav Shchapov <[email protected]>
Co-authored-by: Felix <[email protected]>
  • Loading branch information
phprus and felix642 authored Aug 6, 2023
1 parent 96d1fa2 commit aeb6ad4
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
5 changes: 3 additions & 2 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cstring> // std::strlen
#include <iterator>
#include <limits>
#include <memory> // std::addressof
#include <string>
#include <type_traits>

Expand Down Expand Up @@ -1281,9 +1282,9 @@ 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) {
template <typename T> FMT_CONSTEXPR20 FMT_INLINE value(T& val) {
using value_type = remove_const_t<T>;
custom.value = const_cast<value_type*>(&val);
custom.value = const_cast<value_type*>(std::addressof(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`.
Expand Down
46 changes: 46 additions & 0 deletions include/fmt/std.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
#ifndef FMT_STD_H_
#define FMT_STD_H_

#include <bitset>
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>

#include "format.h"
#include "ostream.h"
Expand Down Expand Up @@ -389,6 +391,50 @@ struct formatter<
#endif
}
};

namespace detail {

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

template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};

template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};

#ifdef _LIBCPP_VERSION

// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};

#endif

} // namespace detail

// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};

FMT_END_NAMESPACE

#endif // FMT_STD_H_
15 changes: 15 additions & 0 deletions test/std-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "fmt/std.h"

#include <bitset>
#include <stdexcept>
#include <string>
#include <vector>
Expand Down Expand Up @@ -221,3 +222,17 @@ TEST(std_test, exception) {
}
#endif
}

TEST(std_test, format_bit_reference) {
std::bitset<2> bs(1);
EXPECT_EQ(fmt::format("{} {}", bs[0], bs[1]), "true false");
std::vector<bool> v = {true, false};
EXPECT_EQ(fmt::format("{} {}", v[0], v[1]), "true false");
}

TEST(std_test, format_const_bit_reference) {
const std::bitset<2> bs(1);
EXPECT_EQ(fmt::format("{} {}", bs[0], bs[1]), "true false");
const std::vector<bool> v = {true, false};
EXPECT_EQ(fmt::format("{} {}", v[0], v[1]), "true false");
}

0 comments on commit aeb6ad4

Please sign in to comment.