Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ranges, containers and types tuple interface... #735

Closed
wants to merge 13 commits into from
Prev Previous commit
Next Next commit
Changes code style to Google Style.
Renames namespace meta to internal and put copy functions int it.
Replaced constexpr by FMT_CONSTEXPR.
Replaced std::ptrdiff_t by std::size_t.
  • Loading branch information
Remotion committed May 12, 2018
commit a442c77c9a3a7e089537d08895610888fd71484e
293 changes: 144 additions & 149 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,201 +24,196 @@ namespace fmt {

template <typename Char>
struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { return ctx.begin(); }
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};

template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char>
{
Char prefix = '{';
Char delimiter = ',';
Char postfix = '}';
bool add_spaces = true;
struct formatting_range : formatting_base<Char> {
Char prefix = '{';
Char delimiter = ',';
Char postfix = '}';
static FMT_CONSTEXPR bool add_spaces = false;
};

template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char>
{
Char prefix = '[';
Char delimiter = ',';
Char postfix = ']';
bool add_spaces = true;
struct formatting_tuple : formatting_base<Char> {
Char prefix = '[';
Char delimiter = ',';
Char postfix = ']';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tuples are normally delimited by parentheses () rather than square brackets [].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using () in last commit now.

static FMT_CONSTEXPR bool add_spaces = false;
};

template<typename RangeT, typename OutputIterator>
namespace internal {

template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) {
for (const auto& it : range) {
*out++ = it;
}
for (const auto &it : range) {
*out++ = it;
}
}

template<typename OutputIterator>
template <typename OutputIterator>
void copy(const char *str, OutputIterator out) {
const char* p_curr = str;
while (*p_curr) {
*out++ = *p_curr++;
}
const char *p_curr = str;
while (*p_curr) {
*out++ = *p_curr++;
}
}

template<typename OutputIterator>
void copy(const char ch, OutputIterator out) {
*out++ = ch;
template <typename OutputIterator>
void copy(char ch, OutputIterator out) {
*out++ = ch;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest putting all of the above in the internal namespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


} // namespace fmt

} // namespace internal

namespace fmt {
namespace meta {
namespace internal {

/// Return true value if T has std::string interface, like std::string_view.
template<typename T>
template <typename T>
class is_like_std_string {
template<typename U> static auto check(U* p) -> decltype(
p->find('a')
, p->length()
, p->data()
, int());
template<typename > static void check(...);
public:
static const bool value = !std::is_void< decltype(check<T>(nullptr)) >::value;
template <typename U>
static auto check(U *p) -> decltype(p->find('a'), p->length(), p->data(), int());
template <typename>
static void check(...);

public:
static FMT_CONSTEXPR bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
};

template<typename T>
constexpr bool is_like_std_string_v = is_like_std_string<T>::value;
template <typename T>
FMT_CONSTEXPR bool is_like_std_string_v = is_like_std_string<T>::value;

template<typename... Ts>
template <typename... Ts>
struct conditional_helper {};

template<typename T, typename _ = void>
template <typename T, typename _ = void>
struct is_range_ : std::false_type {};

template<typename T>
struct is_range_<T,
std::conditional_t< false,
conditional_helper<
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())
>, void>
> : std::true_type {};

template<typename T>
constexpr bool is_range_v = is_range_<T>::value && !is_like_std_string<T>::value;
template <typename T>
struct is_range_<T, std::conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};

template <typename T>
FMT_CONSTEXPR bool is_range_v = is_range_<T>::value && !is_like_std_string<T>::value;

/// tuple_size and tuple_element check.
template<typename T>
template <typename T>
class is_tuple_like_ {
template<typename U> static auto check(U* p) -> decltype(
std::tuple_size< U >::value,
std::declval<typename std::tuple_element<0, U>::type>(),
int());
template<typename > static void check(...);
public:
static constexpr bool value = !std::is_void< decltype(check<T>(nullptr)) >::value;
template <typename U>
static auto check(U *p)
-> decltype(std::tuple_size<U>::value,
std::declval<typename std::tuple_element<0, U>::type>(), int());
template <typename>
static void check(...);

public:
static FMT_CONSTEXPR bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
};

template<typename T>
constexpr bool is_tuple_like_v = is_tuple_like_<T>::value && !is_range_<T>::value;

template <typename T>
FMT_CONSTEXPR bool is_tuple_like_v = is_tuple_like_<T>::value && !is_range_<T>::value;

//=--------------------------------------------------------------------------------------------------------------------
template<size_t... Is, class Tuple, class F>
void for_each(std::index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
using std::get;
// using free function get<I>(T) now.
const int _[] = { 0,
((void)f(get<Is>(tup)),
0)... };
(void)_; // blocks warnings
template <size_t... Is, class Tuple, class F>
void for_each(std::index_sequence<Is...>, Tuple &&tup, F &&f) noexcept {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
//=--------------------------------------------------------------------------------------------------------------------
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes(T const&) { return {}; }
template <class T>
FMT_CONSTEXPR std::make_index_sequence<std::tuple_size<T>::value>
get_indexes(T const &) { return {}; }

//=--------------------------------------------------------------------------------------------------------------------
template<class Tuple, class F>
void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
template <class Tuple, class F>
void for_each(Tuple &&tup, F &&f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}

} // namespace meta
} // namespace fmt

namespace fmt {
} // namespace internal

// =====================================================================================================================
template<typename TupleT, typename Char>
struct formatter< TupleT, Char
, std::enable_if_t<fmt::meta::is_tuple_like_v<TupleT>> >
{
fmt::formatting_tuple<Char> formating;

template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
return formating.parse(ctx);
}

template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::ptrdiff_t i = 0;
fmt::copy(formating.prefix, out);
fmt::meta::for_each(values, [&](const auto &v) {
if (i++ > 0) { fmt::copy(formating.delimiter, out); }
if (formating.add_spaces) { format_to(out, " {}", v); }
else { format_to(out, "{}", v); }
});
if (formating.add_spaces) { *out++ = ' '; }
fmt::copy(formating.postfix, out);

return ctx.out();
}
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, std::enable_if_t<fmt::internal::is_tuple_like_v<TupleT>>> {
fmt::formatting_tuple<Char> formating;

template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
return formating.parse(ctx);
}

template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formating.prefix, out);
internal::for_each(values, [&](const auto &v) {
if (i++ > 0) {
internal::copy(formating.delimiter, out);
}
if (formating.add_spaces) {
format_to(out, " {}", v);
} else {
format_to(out, "{}", v);
}
});
if (formating.add_spaces) {
*out++ = ' ';
}
internal::copy(formating.postfix, out);

return ctx.out();
}
};

} // namespace fmt



namespace fmt {


template<typename RangeT, typename Char>
struct formatter <RangeT, Char, std::enable_if_t<fmt::meta::is_range_v<RangeT>> >
{
static constexpr std::ptrdiff_t range_length_limit = FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.

fmt::formatting_range<Char> formating;

template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
return formating.parse(ctx);
}

template <typename FormatContext>
auto format(const RangeT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
fmt::copy(formating.prefix, out);
std::ptrdiff_t i = 0;
for (const auto& it : values) {
if (i > 0) { fmt::copy(formating.delimiter, out); }
if (formating.add_spaces) { format_to(out, " {}", it); }
else { format_to(out, "{}", it); }
if (++i > range_length_limit) {
format_to(out, " ... <other elements>");
break;
}
}
if (formating.add_spaces) { *out++ = ' '; }
fmt::copy(formating.postfix, out);
return ctx.out();
}
template <typename RangeT, typename Char>
struct formatter<RangeT, Char, std::enable_if_t<fmt::internal::is_range_v<RangeT>>> {
static FMT_CONSTEXPR std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.

fmt::formatting_range<Char> formating;

template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
return formating.parse(ctx);
}

template <typename FormatContext>
auto format(const RangeT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
internal::copy(formating.prefix, out);
std::size_t i = 0;
for (const auto &it : values) {
if (i > 0) {
internal::copy(formating.delimiter, out);
}
if (formating.add_spaces) {
format_to(out, " {}", it);
} else {
format_to(out, "{}", it);
}
if (++i > range_length_limit) {
format_to(out, " ... <other elements>");
break;
}
}
if (formating.add_spaces) {
*out++ = ' ';
}
internal::copy(formating.postfix, out);
return ctx.out();
}
};

} // namespace fmt

} // namespace fmt

#endif // FMT_RANGES_H_
Loading