mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-17 08:44:23 +00:00
Replace buffer with range
This commit is contained in:
parent
c3d6c5fc4c
commit
c095445394
@ -1,35 +1,16 @@
|
||||
/*
|
||||
Formatting library for C++
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
// Formatting library for C++ - the core API
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_CORE_H_
|
||||
#define FMT_CORE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
@ -92,36 +73,15 @@
|
||||
# define FMT_ASSERT(condition, message) assert((condition) && message)
|
||||
#endif
|
||||
|
||||
#define FMT_DELETED = delete
|
||||
|
||||
// A macro to disallow the copy construction and assignment.
|
||||
#define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName& operator=(const TypeName&) = delete
|
||||
#define FMT_DELETED_OR_UNDEFINED = delete
|
||||
#define FMT_DISALLOW_COPY_AND_ASSIGN(Type) \
|
||||
Type(const Type &) FMT_DELETED; \
|
||||
void operator=(const Type &) FMT_DELETED
|
||||
|
||||
namespace fmt {
|
||||
|
||||
template <typename T>
|
||||
class basic_buffer;
|
||||
|
||||
using buffer = basic_buffer<char>;
|
||||
using wbuffer = basic_buffer<wchar_t>;
|
||||
|
||||
template <typename Context>
|
||||
class basic_arg;
|
||||
|
||||
template <typename Context>
|
||||
class basic_format_args;
|
||||
|
||||
template <typename Char>
|
||||
class basic_context;
|
||||
|
||||
using context = basic_context<char>;
|
||||
using wcontext = basic_context<wchar_t>;
|
||||
|
||||
// A formatter for objects of type T.
|
||||
template <typename T, typename Char = char, typename Enable = void>
|
||||
struct formatter;
|
||||
|
||||
/**
|
||||
\rst
|
||||
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
|
||||
@ -138,10 +98,10 @@ class basic_string_view {
|
||||
using char_type = Char;
|
||||
using iterator = const Char *;
|
||||
|
||||
constexpr basic_string_view() noexcept : data_(0), size_(0) {}
|
||||
constexpr basic_string_view() FMT_NOEXCEPT : data_(0), size_(0) {}
|
||||
|
||||
/** Constructs a string reference object from a C string and a size. */
|
||||
constexpr basic_string_view(const Char *s, size_t size) noexcept
|
||||
constexpr basic_string_view(const Char *s, size_t size) FMT_NOEXCEPT
|
||||
: data_(s), size_(size) {}
|
||||
|
||||
/**
|
||||
@ -158,7 +118,7 @@ class basic_string_view {
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
constexpr basic_string_view(const std::basic_string<Char> &s) noexcept
|
||||
constexpr basic_string_view(const std::basic_string<Char> &s) FMT_NOEXCEPT
|
||||
: data_(s.c_str()), size_(s.size()) {}
|
||||
|
||||
/**
|
||||
@ -216,6 +176,96 @@ class basic_string_view {
|
||||
using string_view = basic_string_view<char>;
|
||||
using wstring_view = basic_string_view<wchar_t>;
|
||||
|
||||
/** A contiguous memory buffer with an optional growing ability. */
|
||||
template <typename T>
|
||||
class basic_buffer {
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer);
|
||||
|
||||
T *ptr_;
|
||||
std::size_t size_;
|
||||
std::size_t capacity_;
|
||||
|
||||
protected:
|
||||
basic_buffer() FMT_NOEXCEPT : ptr_(0), size_(0), capacity_(0) {}
|
||||
|
||||
/** Sets the buffer data and capacity. */
|
||||
void set(T *data, std::size_t capacity) FMT_NOEXCEPT {
|
||||
ptr_ = data;
|
||||
capacity_ = capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Increases the buffer capacity to hold at least *capacity* elements.
|
||||
\endrst
|
||||
*/
|
||||
virtual void grow(std::size_t capacity) = 0;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
virtual ~basic_buffer() {}
|
||||
|
||||
T *begin() FMT_NOEXCEPT { return ptr_; }
|
||||
T *end() FMT_NOEXCEPT { return ptr_ + size_; }
|
||||
|
||||
/** Returns the size of this buffer. */
|
||||
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
||||
|
||||
/** Returns the capacity of this buffer. */
|
||||
std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
|
||||
|
||||
/** Returns a pointer to the buffer data. */
|
||||
T *data() FMT_NOEXCEPT { return ptr_; }
|
||||
|
||||
/** Returns a pointer to the buffer data. */
|
||||
const T *data() const FMT_NOEXCEPT { return ptr_; }
|
||||
|
||||
/**
|
||||
Resizes the buffer. If T is a POD type new elements may not be initialized.
|
||||
*/
|
||||
void resize(std::size_t new_size) {
|
||||
reserve(new_size);
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Reserves space to store at least *capacity* elements.
|
||||
\endrst
|
||||
*/
|
||||
void reserve(std::size_t capacity) {
|
||||
if (capacity > capacity_)
|
||||
grow(capacity);
|
||||
}
|
||||
|
||||
void push_back(const T &value) {
|
||||
reserve(size_ + 1);
|
||||
ptr_[size_++] = value;
|
||||
}
|
||||
|
||||
/** Appends data to the end of the buffer. */
|
||||
template <typename U>
|
||||
void append(const U *begin, const U *end);
|
||||
|
||||
T &operator[](std::size_t index) { return ptr_[index]; }
|
||||
const T &operator[](std::size_t index) const { return ptr_[index]; }
|
||||
};
|
||||
|
||||
using buffer = basic_buffer<char>;
|
||||
using wbuffer = basic_buffer<wchar_t>;
|
||||
|
||||
template <typename Context>
|
||||
class basic_arg;
|
||||
|
||||
template <typename Context>
|
||||
class basic_format_args;
|
||||
|
||||
// A formatter for objects of type T.
|
||||
template <typename T, typename Char = char, typename Enable = void>
|
||||
struct formatter;
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
@ -286,14 +336,17 @@ FMT_DISABLE_CONVERSION_TO_INT(float);
|
||||
FMT_DISABLE_CONVERSION_TO_INT(double);
|
||||
FMT_DISABLE_CONVERSION_TO_INT(long double);
|
||||
|
||||
template <typename Context>
|
||||
template <typename Char>
|
||||
struct named_arg_base;
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct named_arg;
|
||||
|
||||
template <typename T>
|
||||
struct is_named_arg : std::false_type {};
|
||||
|
||||
template <typename Context>
|
||||
struct is_named_arg<named_arg<Context>> : std::true_type {};
|
||||
template <typename T, typename Char>
|
||||
struct is_named_arg<named_arg<T, Char>> : std::true_type {};
|
||||
|
||||
enum type {
|
||||
NONE, NAMED_ARG,
|
||||
@ -370,6 +423,9 @@ constexpr uint64_t get_types() {
|
||||
template <>
|
||||
constexpr uint64_t get_types<void>() { return 0; }
|
||||
|
||||
template <typename Context, typename T>
|
||||
constexpr basic_arg<Context> make_arg(const T &value);
|
||||
|
||||
template <typename Char>
|
||||
struct string_value {
|
||||
const Char *value;
|
||||
@ -378,12 +434,8 @@ struct string_value {
|
||||
|
||||
template <typename Context>
|
||||
struct custom_value {
|
||||
using format_func = void (*)(
|
||||
basic_buffer<typename Context::char_type> &buffer,
|
||||
const void *arg, Context &ctx);
|
||||
|
||||
const void *value;
|
||||
format_func format;
|
||||
void (*format)(const void *arg, Context &ctx);
|
||||
};
|
||||
|
||||
// A formatting argument value.
|
||||
@ -487,13 +539,17 @@ class value {
|
||||
custom.format = &format_custom_arg<T>;
|
||||
}
|
||||
|
||||
// Additional template param `Ctx` is needed here because get_type always
|
||||
// uses basic_context<char>.
|
||||
template <typename Ctx>
|
||||
value(const named_arg<Ctx> &value) {
|
||||
static_assert(
|
||||
get_type<const named_arg<Ctx> &>() == NAMED_ARG, "invalid type");
|
||||
pointer = &value;
|
||||
template <typename T>
|
||||
value(const named_arg<T, char_type> &na) {
|
||||
static_assert(get_type<const named_arg<T, char_type> &>() == NAMED_ARG,
|
||||
"invalid type");
|
||||
basic_arg<Context> arg = make_arg<Context>(na.value);
|
||||
std::memcpy(na.data, &arg, sizeof(arg));
|
||||
pointer = &na;
|
||||
}
|
||||
|
||||
const named_arg_base<char_type> &as_named_arg() {
|
||||
return *static_cast<const named_arg_base<char_type>*>(pointer);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -519,15 +575,14 @@ class value {
|
||||
|
||||
// Formats an argument of a custom type, such as a user-defined class.
|
||||
template <typename T>
|
||||
static void format_custom_arg(
|
||||
basic_buffer<char_type> &buffer, const void *arg, Context &ctx) {
|
||||
static void format_custom_arg(const void *arg, Context &ctx) {
|
||||
// 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`.
|
||||
typename Context::template formatter_type<T> f;
|
||||
auto &&parse_ctx = ctx.parse_context();
|
||||
parse_ctx.advance_to(f.parse(parse_ctx));
|
||||
f.format(buffer, *static_cast<const T*>(arg), ctx);
|
||||
f.format(*static_cast<const T*>(arg), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@ -536,9 +591,6 @@ enum { MAX_PACKED_ARGS = 15 };
|
||||
|
||||
template <typename Context>
|
||||
class arg_map;
|
||||
|
||||
template <typename Context, typename T>
|
||||
constexpr basic_arg<Context> make_arg(const T &value);
|
||||
}
|
||||
|
||||
// A formatting argument. It is a trivially copyable/constructible type to
|
||||
@ -574,7 +626,9 @@ class basic_arg {
|
||||
|
||||
constexpr basic_arg() : type_(internal::NONE) {}
|
||||
|
||||
explicit operator bool() const noexcept { return type_ != internal::NONE; }
|
||||
explicit operator bool() const FMT_NOEXCEPT {
|
||||
return type_ != internal::NONE;
|
||||
}
|
||||
|
||||
internal::type type() const { return type_; }
|
||||
|
||||
@ -648,27 +702,16 @@ constexpr basic_arg<Context> make_arg(const T &value) {
|
||||
|
||||
template <bool IS_PACKED, typename Context, typename T>
|
||||
inline typename std::enable_if<IS_PACKED, value<Context>>::type
|
||||
make_arg(const T& value) {
|
||||
make_arg(const T &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <bool IS_PACKED, typename Context, typename T>
|
||||
inline typename std::enable_if<!IS_PACKED, basic_arg<Context>>::type
|
||||
make_arg(const T& value) {
|
||||
make_arg(const T &value) {
|
||||
return make_arg<Context>(value);
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
struct named_arg : basic_arg<Context> {
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
basic_string_view<char_type> name;
|
||||
|
||||
template <typename T>
|
||||
named_arg(basic_string_view<char_type> argname, const T &value)
|
||||
: basic_arg<Context>(make_arg<Context>(value)), name(argname) {}
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
class arg_map {
|
||||
private:
|
||||
@ -676,16 +719,17 @@ class arg_map {
|
||||
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
struct arg {
|
||||
struct entry {
|
||||
basic_string_view<char_type> name;
|
||||
basic_arg<Context> value;
|
||||
basic_arg<Context> arg;
|
||||
};
|
||||
|
||||
arg *map_ = nullptr;
|
||||
entry *map_ = nullptr;
|
||||
unsigned size_ = 0;
|
||||
|
||||
void push_back(arg a) {
|
||||
map_[size_] = a;
|
||||
void push_back(value<Context> val) {
|
||||
const internal::named_arg_base<char_type> &named = val.as_named_arg();
|
||||
map_[size_] = entry{named.name, named.template deserialize<Context>()};
|
||||
++size_;
|
||||
}
|
||||
|
||||
@ -694,29 +738,29 @@ class arg_map {
|
||||
void init(const basic_format_args<Context> &args);
|
||||
~arg_map() { delete [] map_; }
|
||||
|
||||
const basic_arg<Context>
|
||||
*find(const basic_string_view<char_type> &name) const {
|
||||
basic_arg<Context> find(basic_string_view<char_type> name) const {
|
||||
// The list is unsorted, so just return the first matching name.
|
||||
for (auto it = map_, end = map_ + size_; it != end; ++it) {
|
||||
if (it->name == name)
|
||||
return &it->value;
|
||||
return it->arg;
|
||||
}
|
||||
return 0;
|
||||
return basic_arg<Context>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Context>
|
||||
class context_base : public basic_parse_context<Char>{
|
||||
template <typename Range, typename Context>
|
||||
class context_base : public basic_parse_context<typename Range::value_type> {
|
||||
private:
|
||||
Range &range_;
|
||||
basic_format_args<Context> args_;
|
||||
|
||||
protected:
|
||||
using char_type = typename Range::value_type;
|
||||
using format_arg = basic_arg<Context>;
|
||||
|
||||
context_base(basic_string_view<Char> format_str,
|
||||
context_base(Range &range, basic_string_view<char_type> format_str,
|
||||
basic_format_args<Context> args)
|
||||
: basic_parse_context<Char>(format_str), args_(args) {}
|
||||
~context_base() {}
|
||||
: basic_parse_context<char_type>(format_str), range_(range), args_(args) {}
|
||||
|
||||
basic_format_args<Context> args() const { return args_; }
|
||||
|
||||
@ -736,29 +780,30 @@ class context_base : public basic_parse_context<Char>{
|
||||
}
|
||||
|
||||
public:
|
||||
basic_parse_context<Char> &parse_context() { return *this; }
|
||||
basic_parse_context<char_type> &parse_context() { return *this; }
|
||||
Range &range() { return range_; }
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename Char>
|
||||
template <typename Range>
|
||||
class basic_context :
|
||||
public internal::context_base<Char, basic_context<Char>> {
|
||||
public internal::context_base<Range, basic_context<Range>> {
|
||||
public:
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
using char_type = typename Range::value_type;
|
||||
|
||||
template <typename T>
|
||||
using formatter_type = formatter<T, Char>;
|
||||
using formatter_type = formatter<T, char_type>;
|
||||
|
||||
private:
|
||||
internal::arg_map<basic_context<Char>> map_;
|
||||
internal::arg_map<basic_context> map_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(basic_context);
|
||||
|
||||
using Base = internal::context_base<Char, basic_context<Char>>;
|
||||
using base = internal::context_base<Range, basic_context>;
|
||||
|
||||
using format_arg = typename Base::format_arg;
|
||||
using Base::get_arg;
|
||||
using format_arg = typename base::format_arg;
|
||||
using base::get_arg;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -767,18 +812,21 @@ class basic_context :
|
||||
stored in the object so make sure they have appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_context(
|
||||
basic_string_view<Char> format_str, basic_format_args<basic_context> args)
|
||||
: Base(format_str, args) {}
|
||||
basic_context(Range &range, basic_string_view<char_type> format_str,
|
||||
basic_format_args<basic_context> args)
|
||||
: base(range, format_str, args) {}
|
||||
|
||||
format_arg next_arg() { return this->do_get_arg(this->next_arg_id()); }
|
||||
format_arg get_arg(unsigned arg_id) { return this->do_get_arg(arg_id); }
|
||||
|
||||
// Checks if manual indexing is used and returns the argument with
|
||||
// specified name.
|
||||
format_arg get_arg(basic_string_view<Char> name);
|
||||
format_arg get_arg(basic_string_view<char_type> name);
|
||||
};
|
||||
|
||||
using context = basic_context<buffer>;
|
||||
using wcontext = basic_context<wbuffer>;
|
||||
|
||||
template <typename Context, typename ...Args>
|
||||
class arg_store {
|
||||
private:
|
||||
@ -876,7 +924,7 @@ class basic_format_args {
|
||||
format_arg operator[](size_type index) const {
|
||||
format_arg arg = get(index);
|
||||
return arg.type_ == internal::NAMED_ARG ?
|
||||
*static_cast<const format_arg*>(arg.value_.pointer) : arg;
|
||||
arg.value_.as_named_arg().template deserialize<Context>() : arg;
|
||||
}
|
||||
|
||||
unsigned max_size() const {
|
||||
@ -888,6 +936,31 @@ class basic_format_args {
|
||||
using format_args = basic_format_args<context>;
|
||||
using wformat_args = basic_format_args<wcontext>;
|
||||
|
||||
namespace internal {
|
||||
template <typename Char>
|
||||
struct named_arg_base {
|
||||
basic_string_view<Char> name;
|
||||
|
||||
// Serialized value<context>.
|
||||
mutable char data[sizeof(basic_arg<context>)];
|
||||
|
||||
template <typename Context>
|
||||
basic_arg<Context> deserialize() const {
|
||||
basic_arg<Context> arg;
|
||||
std::memcpy(&arg, data, sizeof(basic_arg<Context>));
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct named_arg : named_arg_base<Char> {
|
||||
const T &value;
|
||||
|
||||
named_arg(basic_string_view<Char> name, const T &val)
|
||||
: named_arg_base<Char>{name}, value(val) {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns a named argument for formatting functions.
|
||||
@ -895,25 +968,24 @@ using wformat_args = basic_format_args<wcontext>;
|
||||
**Example**::
|
||||
|
||||
print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
|
||||
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
inline internal::named_arg<context> arg(string_view name, const T &arg) {
|
||||
return internal::named_arg<context>(name, arg);
|
||||
inline internal::named_arg<T, char> arg(string_view name, const T &arg) {
|
||||
return internal::named_arg<T, char>(name, arg);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline internal::named_arg<wcontext> arg(wstring_view name, const T &arg) {
|
||||
return internal::named_arg<wcontext>(name, arg);
|
||||
inline internal::named_arg<T, wchar_t> arg(wstring_view name, const T &arg) {
|
||||
return internal::named_arg<T, wchar_t>(name, arg);
|
||||
}
|
||||
|
||||
// The following two functions are deleted intentionally to disable
|
||||
// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
|
||||
template <typename Context>
|
||||
void arg(string_view, internal::named_arg<Context>) FMT_DELETED_OR_UNDEFINED;
|
||||
template <typename Context>
|
||||
void arg(wstring_view, internal::named_arg<Context>) FMT_DELETED_OR_UNDEFINED;
|
||||
template <typename T>
|
||||
void arg(string_view, internal::named_arg<T, char>) FMT_DELETED;
|
||||
template <typename T>
|
||||
void arg(wstring_view, internal::named_arg<T, wchar_t>) FMT_DELETED;
|
||||
|
||||
enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
|
||||
|
||||
|
@ -1,29 +1,9 @@
|
||||
/*
|
||||
Formatting library for C++
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
// Formatting library for C++
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/locale.h"
|
||||
@ -348,18 +328,18 @@ FMT_FUNC void windows_error::init(
|
||||
FMT_FUNC void internal::format_windows_error(
|
||||
buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
wmemory_buffer buffer;
|
||||
buffer.resize(INLINE_BUFFER_SIZE);
|
||||
wmemory_buffer buf;
|
||||
buf.resize(INLINE_BUFFER_SIZE);
|
||||
for (;;) {
|
||||
wchar_t *system_message = &buffer[0];
|
||||
wchar_t *system_message = &buf[0];
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
system_message, static_cast<uint32_t>(buffer.size()), 0);
|
||||
system_message, static_cast<uint32_t>(buf.size()), 0);
|
||||
if (result != 0) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||
basic_writer<char> w(out);
|
||||
basic_writer<buffer> w(out);
|
||||
w.write(message);
|
||||
w.write(": ");
|
||||
w.write(utf8_message);
|
||||
@ -369,10 +349,10 @@ FMT_FUNC void internal::format_windows_error(
|
||||
}
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
break; // Can't get error message, report error code instead.
|
||||
buffer.resize(buffer.size() * 2);
|
||||
buf.resize(buf.size() * 2);
|
||||
}
|
||||
} FMT_CATCH(...) {}
|
||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
||||
format_error_code(out, error_code, message);
|
||||
}
|
||||
|
||||
#endif // FMT_USE_WINDOWS_H
|
||||
@ -467,8 +447,6 @@ template int internal::char_traits<char>::format_float(
|
||||
|
||||
template wchar_t internal::thousands_sep(locale_provider *lp);
|
||||
|
||||
template class basic_context<wchar_t>;
|
||||
|
||||
template void basic_fixed_buffer<wchar_t>::grow(std::size_t);
|
||||
|
||||
template void internal::arg_map<wcontext>::init(const wformat_args &args);
|
||||
|
@ -113,9 +113,6 @@
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_USER_DEFINED_LITERALS
|
||||
// All compilers which support UDLs also support variadic templates. This
|
||||
// makes the fmt::literals implementation easier. However, an explicit check
|
||||
// for variadic templates is added here just in case.
|
||||
// For Intel's compiler both it and the system gcc/msc must support UDLs.
|
||||
# if (FMT_HAS_FEATURE(cxx_user_literals) || \
|
||||
FMT_GCC_VERSION >= 407 || FMT_MSC_VER >= 1900) && \
|
||||
@ -261,7 +258,7 @@ class numeric_limits<fmt::internal::dummy_int> :
|
||||
|
||||
namespace fmt {
|
||||
|
||||
template <typename Char>
|
||||
template <typename Range>
|
||||
class basic_writer;
|
||||
|
||||
/** A formatting error such as invalid format string. */
|
||||
@ -308,84 +305,7 @@ class locale;
|
||||
class locale_provider {
|
||||
public:
|
||||
virtual ~locale_provider() {}
|
||||
virtual locale locale();
|
||||
};
|
||||
|
||||
/** A contiguous memory buffer with an optional growing ability. */
|
||||
template <typename T>
|
||||
class basic_buffer {
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer);
|
||||
|
||||
T *ptr_;
|
||||
std::size_t size_;
|
||||
std::size_t capacity_;
|
||||
|
||||
protected:
|
||||
basic_buffer() FMT_NOEXCEPT : ptr_(0), size_(0), capacity_(0) {}
|
||||
|
||||
/** Sets the buffer data and capacity. */
|
||||
void set(T* data, std::size_t capacity) FMT_NOEXCEPT {
|
||||
ptr_ = data;
|
||||
capacity_ = capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Increases the buffer capacity to hold at least *capacity* elements.
|
||||
\endrst
|
||||
*/
|
||||
virtual void grow(std::size_t capacity) = 0;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
virtual ~basic_buffer() {}
|
||||
|
||||
T *begin() FMT_NOEXCEPT { return ptr_; }
|
||||
T *end() FMT_NOEXCEPT { return ptr_ + capacity_; }
|
||||
|
||||
/** Returns the size of this buffer. */
|
||||
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
||||
|
||||
/** Returns the capacity of this buffer. */
|
||||
std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
|
||||
|
||||
/** Returns a pointer to the buffer data. */
|
||||
T *data() FMT_NOEXCEPT { return ptr_; }
|
||||
|
||||
/** Returns a pointer to the buffer data. */
|
||||
const T *data() const FMT_NOEXCEPT { return ptr_; }
|
||||
|
||||
/**
|
||||
Resizes the buffer. If T is a POD type new elements may not be initialized.
|
||||
*/
|
||||
void resize(std::size_t new_size) {
|
||||
reserve(new_size);
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Reserves space to store at least *capacity* elements.
|
||||
\endrst
|
||||
*/
|
||||
void reserve(std::size_t capacity) {
|
||||
if (capacity > capacity_)
|
||||
grow(capacity);
|
||||
}
|
||||
|
||||
void push_back(const T &value) {
|
||||
reserve(size_ + 1);
|
||||
ptr_[size_++] = value;
|
||||
}
|
||||
|
||||
/** Appends data to the end of the buffer. */
|
||||
template <typename U>
|
||||
void append(const U *begin, const U *end);
|
||||
|
||||
T &operator[](std::size_t index) { return ptr_[index]; }
|
||||
const T &operator[](std::size_t index) const { return ptr_[index]; }
|
||||
virtual fmt::locale locale();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -690,6 +610,33 @@ constexpr const Char *pointer_from(null_terminating_iterator<Char> it) {
|
||||
return it.ptr_;
|
||||
}
|
||||
|
||||
// A range that can grow dynamically.
|
||||
template <typename Container>
|
||||
class dynamic_range {
|
||||
private:
|
||||
Container &container_;
|
||||
|
||||
public:
|
||||
using iterator = decltype(container_.begin());
|
||||
using value_type = typename Container::value_type;
|
||||
|
||||
struct sentinel {
|
||||
friend bool operator!=(sentinel, iterator) { return false; }
|
||||
friend bool operator!=(iterator, sentinel) { return false; }
|
||||
};
|
||||
|
||||
explicit dynamic_range(Container &c) : container_(c) {}
|
||||
|
||||
iterator begin() const { return container_.begin(); }
|
||||
sentinel end() const { return sentinel(); }
|
||||
|
||||
friend iterator grow(dynamic_range r, size_t n) {
|
||||
auto size = r.container_.size();
|
||||
r.container_.resize(size + n);
|
||||
return r.container_.begin() + size;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true if value is negative, false otherwise.
|
||||
// Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
|
||||
template <typename T>
|
||||
@ -1255,11 +1202,8 @@ template <typename Context>
|
||||
void arg_map<Context>::init(const basic_format_args<Context> &args) {
|
||||
if (map_)
|
||||
return;
|
||||
map_ = new arg[args.max_size()];
|
||||
typedef internal::named_arg<Context> NamedArg;
|
||||
const NamedArg *named_arg = 0;
|
||||
bool use_values =
|
||||
args.type(MAX_PACKED_ARGS - 1) == internal::NONE;
|
||||
map_ = new entry[args.max_size()];
|
||||
bool use_values = args.type(MAX_PACKED_ARGS - 1) == internal::NONE;
|
||||
if (use_values) {
|
||||
for (unsigned i = 0;/*nothing*/; ++i) {
|
||||
internal::type arg_type = args.type(i);
|
||||
@ -1267,8 +1211,7 @@ void arg_map<Context>::init(const basic_format_args<Context> &args) {
|
||||
case internal::NONE:
|
||||
return;
|
||||
case internal::NAMED_ARG:
|
||||
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
|
||||
push_back(arg{named_arg->name, *named_arg});
|
||||
push_back(args.values_[i]);
|
||||
break;
|
||||
default:
|
||||
break; // Do nothing.
|
||||
@ -1277,19 +1220,15 @@ void arg_map<Context>::init(const basic_format_args<Context> &args) {
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) {
|
||||
internal::type arg_type = args.type(i);
|
||||
if (arg_type == internal::NAMED_ARG) {
|
||||
named_arg = static_cast<const NamedArg*>(args.args_[i].value_.pointer);
|
||||
push_back(arg{named_arg->name, *named_arg});
|
||||
}
|
||||
if (args.type(i) == internal::NAMED_ARG)
|
||||
push_back(args.args_[i].value_);
|
||||
}
|
||||
for (unsigned i = MAX_PACKED_ARGS; ; ++i) {
|
||||
switch (args.args_[i].type_) {
|
||||
case internal::NONE:
|
||||
return;
|
||||
case internal::NAMED_ARG:
|
||||
named_arg = static_cast<const NamedArg*>(args.args_[i].value_.pointer);
|
||||
push_back(arg{named_arg->name, *named_arg});
|
||||
push_back(args.args_[i].value_);
|
||||
break;
|
||||
default:
|
||||
break; // Do nothing.
|
||||
@ -1434,15 +1373,13 @@ constexpr unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
|
||||
template <typename Char, typename Context>
|
||||
class custom_formatter {
|
||||
private:
|
||||
basic_buffer<Char> &buffer_;
|
||||
Context &ctx_;
|
||||
|
||||
public:
|
||||
custom_formatter(basic_buffer<Char> &buffer, Context &ctx)
|
||||
: buffer_(buffer), ctx_(ctx) {}
|
||||
explicit custom_formatter(Context &ctx): ctx_(ctx) {}
|
||||
|
||||
bool operator()(typename basic_arg<Context>::handle h) {
|
||||
h.format(buffer_, ctx_);
|
||||
h.format(ctx_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2031,17 +1968,18 @@ struct format_type : std::integral_constant<bool, get_type<T>() != CUSTOM> {};
|
||||
template <typename T, typename Enable = void>
|
||||
struct format_enum : std::integral_constant<bool, std::is_enum<T>::value> {};
|
||||
|
||||
template <template <typename> class Handler, typename Spec, typename Char>
|
||||
template <template <typename> class Handler, typename Spec, typename Context>
|
||||
void handle_dynamic_spec(
|
||||
Spec &value, arg_ref<Char> ref, basic_context<Char> &ctx) {
|
||||
Spec &value, arg_ref<typename Context::char_type> ref, Context &ctx) {
|
||||
using char_type = typename Context::char_type;
|
||||
switch (ref.kind) {
|
||||
case arg_ref<Char>::NONE:
|
||||
case arg_ref<char_type>::NONE:
|
||||
break;
|
||||
case arg_ref<Char>::INDEX:
|
||||
case arg_ref<char_type>::INDEX:
|
||||
internal::set_dynamic_spec<Handler>(
|
||||
value, ctx.get_arg(ref.index), ctx.error_handler());
|
||||
break;
|
||||
case arg_ref<Char>::NAME:
|
||||
case arg_ref<char_type>::NAME:
|
||||
internal::set_dynamic_spec<Handler>(
|
||||
value, ctx.get_arg(ref.name), ctx.error_handler());
|
||||
break;
|
||||
@ -2050,15 +1988,17 @@ void handle_dynamic_spec(
|
||||
} // namespace internal
|
||||
|
||||
/** The default argument formatter. */
|
||||
template <typename Char>
|
||||
class arg_formatter : public internal::arg_formatter_base<Char> {
|
||||
template <typename Range>
|
||||
class arg_formatter :
|
||||
public internal::arg_formatter_base<typename Range::value_type> {
|
||||
private:
|
||||
basic_context<Char> &ctx_;
|
||||
basic_context<Range> &ctx_;
|
||||
|
||||
typedef internal::arg_formatter_base<Char> Base;
|
||||
using char_type = typename Range::value_type;
|
||||
typedef internal::arg_formatter_base<char_type> Base;
|
||||
|
||||
public:
|
||||
typedef typename Base::format_specs format_specs;
|
||||
using format_specs = typename Base::format_specs;
|
||||
|
||||
/**
|
||||
\rst
|
||||
@ -2068,15 +2008,15 @@ class arg_formatter : public internal::arg_formatter_base<Char> {
|
||||
format specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
arg_formatter(basic_buffer<Char> &buffer, basic_context<Char> &ctx,
|
||||
arg_formatter(basic_buffer<char_type> &buffer, basic_context<Range> &ctx,
|
||||
format_specs &spec)
|
||||
: internal::arg_formatter_base<Char>(buffer, spec), ctx_(ctx) {}
|
||||
: internal::arg_formatter_base<char_type>(buffer, spec), ctx_(ctx) {}
|
||||
|
||||
using internal::arg_formatter_base<Char>::operator();
|
||||
using internal::arg_formatter_base<char_type>::operator();
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
void operator()(typename basic_arg<basic_context<Char>>::handle handle) {
|
||||
handle.format(this->writer().buffer(), ctx_);
|
||||
void operator()(typename basic_arg<basic_context<Range>>::handle handle) {
|
||||
handle.format(ctx_);
|
||||
}
|
||||
};
|
||||
|
||||
@ -2169,8 +2109,8 @@ class basic_writer {
|
||||
using iterator = decltype(std::declval<Range>().begin());
|
||||
|
||||
// Output range.
|
||||
iterator begin_;
|
||||
decltype(std::declval<Range>().end()) end_;
|
||||
internal::dynamic_range<Range> range_;
|
||||
iterator out_;
|
||||
|
||||
std::unique_ptr<locale_provider> locale_;
|
||||
|
||||
@ -2185,17 +2125,9 @@ class basic_writer {
|
||||
static char_type *get(char_type *p) { return p; }
|
||||
#endif
|
||||
|
||||
template <typename Category>
|
||||
void do_reserve(std::size_t, Category) {}
|
||||
|
||||
void do_reserve(std::size_t n, std::random_access_iterator_tag) {
|
||||
(void)(begin_ + n);
|
||||
}
|
||||
|
||||
// Attempts to reserve space for n characters in the output range.
|
||||
void reserve(std::size_t n) {
|
||||
using category = typename std::iterator_traits<iterator>::iterator_category;
|
||||
do_reserve(n, category());
|
||||
out_ = grow(range_, n);
|
||||
}
|
||||
|
||||
// Writes a value in the format
|
||||
@ -2218,12 +2150,15 @@ class basic_writer {
|
||||
padding = spec.width() - size;
|
||||
size = spec.width();
|
||||
}
|
||||
} else if (spec.precision() > num_digits) {
|
||||
} else if (spec.precision() > static_cast<int>(num_digits)) {
|
||||
size = prefix.size() + spec.precision();
|
||||
padding = spec.precision() - num_digits;
|
||||
fill = '0';
|
||||
}
|
||||
write_padded(size, spec, [prefix, fill, padding, f](auto &it) {
|
||||
align_spec as = spec;
|
||||
if (spec.align() == ALIGN_DEFAULT)
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
write_padded(size, as, [prefix, fill, padding, f](auto &it) {
|
||||
if (prefix.size() != 0)
|
||||
it = std::uninitialized_copy_n(prefix.data(), prefix.size(), it);
|
||||
it = std::uninitialized_fill_n(it, padding, fill);
|
||||
@ -2242,8 +2177,8 @@ class basic_writer {
|
||||
unsigned num_digits = internal::count_digits(abs_value);
|
||||
reserve((is_negative ? 1 : 0) + num_digits);
|
||||
if (is_negative)
|
||||
*begin_++ = '-';
|
||||
internal::format_decimal(begin_, abs_value, num_digits);
|
||||
*out_++ = '-';
|
||||
internal::format_decimal(out_, abs_value, num_digits);
|
||||
}
|
||||
|
||||
// The handle_int_type_spec handler that writes an integer.
|
||||
@ -2286,7 +2221,7 @@ class basic_writer {
|
||||
unsigned num_digits = internal::count_digits(abs_value);
|
||||
writer.write_int(num_digits, get_prefix(), spec,
|
||||
[this, num_digits](auto &it) {
|
||||
internal::format_decimal(it, abs_value, 0);
|
||||
internal::format_decimal(it, abs_value, num_digits);
|
||||
it += num_digits;
|
||||
});
|
||||
}
|
||||
@ -2296,12 +2231,16 @@ class basic_writer {
|
||||
prefix[prefix_size++] = '0';
|
||||
prefix[prefix_size++] = spec.type();
|
||||
}
|
||||
writer.write_int(count_digits<4>(), get_prefix(), spec, [this](auto &it) {
|
||||
unsigned num_digits = count_digits<4>();
|
||||
writer.write_int(num_digits, get_prefix(), spec,
|
||||
[this, num_digits](auto &it) {
|
||||
it += num_digits;
|
||||
auto out = it;
|
||||
auto n = abs_value;
|
||||
const char *digits = spec.type() == 'x' ?
|
||||
"0123456789abcdef" : "0123456789ABCDEF";
|
||||
do {
|
||||
*it-- = digits[n & 0xf];
|
||||
*--out = digits[n & 0xf];
|
||||
} while ((n >>= 4) != 0);
|
||||
});
|
||||
}
|
||||
@ -2311,35 +2250,46 @@ class basic_writer {
|
||||
prefix[prefix_size++] = '0';
|
||||
prefix[prefix_size++] = spec.type();
|
||||
}
|
||||
writer.write_int(count_digits<1>(), get_prefix(), spec, [this](auto &it) {
|
||||
unsigned num_digits = count_digits<1>();
|
||||
writer.write_int(num_digits, get_prefix(), spec,
|
||||
[this, num_digits](auto &it) {
|
||||
it += num_digits;
|
||||
auto out = it;
|
||||
auto n = abs_value;
|
||||
do {
|
||||
*it-- = static_cast<char_type>('0' + (n & 1));
|
||||
*--out = static_cast<char_type>('0' + (n & 1));
|
||||
} while ((n >>= 1) != 0);
|
||||
});
|
||||
}
|
||||
|
||||
void on_oct() {
|
||||
if (spec.flag(HASH_FLAG))
|
||||
unsigned num_digits = count_digits<3>();
|
||||
if (spec.flag(HASH_FLAG) &&
|
||||
spec.precision() <= static_cast<int>(num_digits)) {
|
||||
// Octal prefix '0' is counted as a digit, so only add it if precision
|
||||
// is not greater than the number of digits.
|
||||
prefix[prefix_size++] = '0';
|
||||
writer.write_int(count_digits<3>(), get_prefix(), spec, [this](auto &it) {
|
||||
}
|
||||
writer.write_int(num_digits, get_prefix(), spec,
|
||||
[this, num_digits](auto &it) {
|
||||
it += num_digits;
|
||||
auto out = it;
|
||||
auto n = abs_value;
|
||||
do {
|
||||
*it-- = static_cast<char_type>('0' + (n & 7));
|
||||
*--out = static_cast<char_type>('0' + (n & 7));
|
||||
} while ((n >>= 3) != 0);
|
||||
});
|
||||
}
|
||||
|
||||
void on_num() {
|
||||
unsigned num_digits = internal::count_digits(abs_value);
|
||||
char_type thousands_sep =
|
||||
internal::thousands_sep<char_type>(writer.locale_.get());
|
||||
basic_string_view<char_type> sep(&thousands_sep, 1);
|
||||
unsigned size = static_cast<unsigned>(
|
||||
num_digits + sep.size() * ((num_digits - 1) / 3));
|
||||
char_type sep = internal::thousands_sep<char_type>(writer.locale_.get());
|
||||
static constexpr unsigned SEP_SIZE = 1;
|
||||
unsigned size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
|
||||
writer.write_int(size, get_prefix(), spec, [this, size, sep](auto &it) {
|
||||
internal::format_decimal(it, abs_value, 0,
|
||||
internal::add_thousands_sep<char_type>(sep));
|
||||
basic_string_view<char_type> s(&sep, SEP_SIZE);
|
||||
internal::format_decimal(it, abs_value, size,
|
||||
internal::add_thousands_sep<char_type>(s));
|
||||
it += size;
|
||||
});
|
||||
}
|
||||
@ -2385,7 +2335,7 @@ class basic_writer {
|
||||
|
||||
public:
|
||||
/** Constructs a ``basic_writer`` object. */
|
||||
explicit basic_writer(Range &r) : begin_(r.begin()), end_(r.end()) {}
|
||||
explicit basic_writer(Range &r): range_(r), out_(r.begin()) {}
|
||||
|
||||
void write(int value) {
|
||||
write_decimal(value);
|
||||
@ -2405,7 +2355,9 @@ class basic_writer {
|
||||
template <typename T, typename... FormatSpecs>
|
||||
typename std::enable_if<std::is_integral<T>::value, void>::type
|
||||
write(T value, FormatSpecs... specs) {
|
||||
write_int(value, format_specs(specs...));
|
||||
format_specs s(specs...);
|
||||
s.align_ = ALIGN_RIGHT;
|
||||
write_int(value, s);
|
||||
}
|
||||
|
||||
void write(double value) {
|
||||
@ -2424,12 +2376,14 @@ class basic_writer {
|
||||
|
||||
/** Writes a character to the buffer. */
|
||||
void write(char value) {
|
||||
*begin_++ = value;
|
||||
reserve(1);
|
||||
*out_++ = value;
|
||||
}
|
||||
|
||||
void write(wchar_t value) {
|
||||
internal::require_wchar<char_type>();
|
||||
*begin_++ = value;
|
||||
reserve(1);
|
||||
*out_++ = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2438,12 +2392,14 @@ class basic_writer {
|
||||
\endrst
|
||||
*/
|
||||
void write(string_view value) {
|
||||
begin_ = std::uninitialized_copy(value.begin(), value.end(), begin_);
|
||||
reserve(value.size());
|
||||
out_ = std::uninitialized_copy(value.begin(), value.end(), out_);
|
||||
}
|
||||
|
||||
void write(wstring_view value) {
|
||||
internal::require_wchar<char_type>();
|
||||
begin_ = std::uninitialized_copy(value.begin(), value.end(), begin_);
|
||||
reserve(value.size());
|
||||
out_ = std::uninitialized_copy(value.begin(), value.end(), out_);
|
||||
}
|
||||
|
||||
template <typename... FormatSpecs>
|
||||
@ -2459,22 +2415,22 @@ void basic_writer<Range>::write_padded(
|
||||
unsigned width = spec.width();
|
||||
if (width <= size) {
|
||||
reserve(size);
|
||||
return f(begin_);
|
||||
return f(out_);
|
||||
}
|
||||
reserve(width);
|
||||
char_type fill = internal::char_traits<char_type>::cast(spec.fill());
|
||||
std::size_t padding = width - size;
|
||||
if (spec.align() == ALIGN_RIGHT) {
|
||||
begin_ = std::uninitialized_fill_n(begin_, padding, fill);
|
||||
f(begin_);
|
||||
out_ = std::uninitialized_fill_n(out_, padding, fill);
|
||||
f(out_);
|
||||
} else if (spec.align() == ALIGN_CENTER) {
|
||||
std::size_t left_padding = padding / 2;
|
||||
begin_ = std::uninitialized_fill_n(begin_, left_padding, fill);
|
||||
f(begin_);
|
||||
begin_ = std::uninitialized_fill_n(begin_, padding - left_padding, fill);
|
||||
out_ = std::uninitialized_fill_n(out_, left_padding, fill);
|
||||
f(out_);
|
||||
out_ = std::uninitialized_fill_n(out_, padding - left_padding, fill);
|
||||
} else {
|
||||
f(begin_);
|
||||
std::uninitialized_fill_n(begin_, padding, fill);
|
||||
f(out_);
|
||||
out_ = std::uninitialized_fill_n(out_, padding, fill);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2549,41 +2505,29 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
|
||||
}
|
||||
|
||||
if (internal::fputil::isnotanumber(value)) {
|
||||
// Format NaN ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
const char *nan = handler.upper ? "NAN" : "nan";
|
||||
std::size_t nan_size = 3;
|
||||
reserve(nan_size + (sign ? 1 : 0));
|
||||
if (sign)
|
||||
*begin_++ = sign;
|
||||
begin_ = std::uninitialized_copy_n(nan, nan_size, begin_);
|
||||
return;
|
||||
}
|
||||
auto write_inf_or_nan = [this, &spec, sign](const char *str) {
|
||||
static constexpr std::size_t SIZE = 3;
|
||||
write_padded(SIZE + (sign ? 1 : 0), spec, [sign, str](auto &it) {
|
||||
if (sign)
|
||||
*it++ = sign;
|
||||
it = std::uninitialized_copy_n(str, SIZE, it);
|
||||
});
|
||||
};
|
||||
|
||||
if (internal::fputil::isinfinity(value)) {
|
||||
// Format infinity ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
const char *inf = handler.upper ? "INF" : "inf";
|
||||
std::size_t inf_size = 3;
|
||||
reserve(inf_size + (sign ? 1 : 0));
|
||||
if (sign)
|
||||
*begin_++ = sign;
|
||||
begin_ = std::uninitialized_copy_n(inf, inf_size, begin_);
|
||||
return;
|
||||
}
|
||||
// Format NaN and ininity ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
if (internal::fputil::isnotanumber(value))
|
||||
return write_inf_or_nan(handler.upper ? "NAN" : "nan");
|
||||
if (internal::fputil::isinfinity(value))
|
||||
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
||||
|
||||
// TODO: buffered_range that wraps a range and adds necessary buffering if the
|
||||
// latter is not contiguous.
|
||||
basic_memory_buffer<char_type> buffer;
|
||||
|
||||
std::size_t offset = 0;
|
||||
unsigned width = spec.width();
|
||||
if (sign) {
|
||||
buffer.reserve(width > 1u ? width : 1u);
|
||||
if (width > 0)
|
||||
--width;
|
||||
++offset;
|
||||
}
|
||||
|
||||
// Build format string.
|
||||
@ -2594,14 +2538,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
unsigned width_for_sprintf = width;
|
||||
if (spec.flag(HASH_FLAG))
|
||||
*format_ptr++ = '#';
|
||||
if (spec.align() == ALIGN_CENTER) {
|
||||
width_for_sprintf = 0;
|
||||
} else {
|
||||
if (spec.align() == ALIGN_LEFT)
|
||||
*format_ptr++ = '-';
|
||||
if (width != 0)
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
width_for_sprintf = 0;
|
||||
if (spec.precision() >= 0) {
|
||||
*format_ptr++ = '.';
|
||||
*format_ptr++ = '*';
|
||||
@ -2616,33 +2553,46 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
unsigned n = 0;
|
||||
char_type *start = 0;
|
||||
for (;;) {
|
||||
std::size_t buffer_size = buffer.capacity() - offset;
|
||||
std::size_t buffer_size = buffer.capacity();
|
||||
#if FMT_MSC_VER
|
||||
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
|
||||
// space for at least one extra character to make the size non-zero.
|
||||
// Note that the buffer's capacity may increase by more than 1.
|
||||
if (buffer_size == 0) {
|
||||
buffer.reserve(offset + 1);
|
||||
buffer_size = buffer.capacity() - offset;
|
||||
buffer.reserve(1);
|
||||
buffer_size = buffer.capacity();
|
||||
}
|
||||
#endif
|
||||
start = &buffer[offset];
|
||||
start = &buffer[0];
|
||||
int result = internal::char_traits<char_type>::format_float(
|
||||
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
||||
if (result >= 0) {
|
||||
n = internal::to_unsigned(result);
|
||||
if (offset + n < buffer.capacity())
|
||||
if (n < buffer.capacity())
|
||||
break; // The buffer is large enough - continue with formatting.
|
||||
buffer.reserve(offset + n + 1);
|
||||
buffer.reserve(n + 1);
|
||||
} else {
|
||||
// If result is negative we ask to increase the capacity by at least 1,
|
||||
// but as std::vector, the buffer grows exponentially.
|
||||
buffer.reserve(buffer.capacity() + 1);
|
||||
}
|
||||
}
|
||||
if (sign)
|
||||
++n;
|
||||
write_padded(n, spec, [n, sign, &buffer](auto &it) mutable {
|
||||
align_spec as = spec;
|
||||
if (spec.align() == ALIGN_NUMERIC) {
|
||||
if (sign) {
|
||||
reserve(1);
|
||||
*out_++ = sign;
|
||||
sign = 0;
|
||||
--as.width_;
|
||||
}
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
} else {
|
||||
if (spec.align() == ALIGN_DEFAULT)
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
if (sign)
|
||||
++n;
|
||||
}
|
||||
write_padded(n, as, [n, sign, &buffer](auto &it) mutable {
|
||||
if (sign) {
|
||||
*it++ = sign;
|
||||
--n;
|
||||
@ -2789,7 +2739,7 @@ class FormatInt {
|
||||
// write a terminating null character.
|
||||
template <typename T>
|
||||
inline void format_decimal(char *&buffer, T value) {
|
||||
typedef typename internal::int_traits<T>::main_type main_type;
|
||||
using main_type = typename internal::int_traits<T>::main_type;
|
||||
main_type abs_value = static_cast<main_type>(value);
|
||||
if (internal::is_negative(value)) {
|
||||
*buffer++ = '-';
|
||||
@ -2867,13 +2817,14 @@ struct formatter<
|
||||
return pointer_from(it);
|
||||
}
|
||||
|
||||
void format(basic_buffer<Char> &buf, const T &val, basic_context<Char> &ctx) {
|
||||
template <typename Range>
|
||||
void format(const T &val, basic_context<Range> &ctx) {
|
||||
internal::handle_dynamic_spec<internal::width_checker>(
|
||||
specs_.width_, specs_.width_ref, ctx);
|
||||
internal::handle_dynamic_spec<internal::precision_checker>(
|
||||
specs_.precision_, specs_.precision_ref, ctx);
|
||||
visit(arg_formatter<Char>(buf, ctx, specs_),
|
||||
internal::make_arg<basic_context<Char>>(val));
|
||||
visit(arg_formatter<Range>(ctx.range(), ctx, specs_),
|
||||
internal::make_arg<basic_context<Range>>(val));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -2912,7 +2863,8 @@ struct dynamic_formatter {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void format(basic_buffer<Char> &buf, const T &val, basic_context<Char> &ctx) {
|
||||
void format(basic_buffer<Char> &buf, const T &val,
|
||||
basic_context<basic_buffer<Char>> &ctx) {
|
||||
handle_specs(ctx);
|
||||
struct null_handler : internal::error_handler {
|
||||
void on_align(alignment) {}
|
||||
@ -2938,12 +2890,13 @@ struct dynamic_formatter {
|
||||
}
|
||||
if (specs_.precision_ != -1)
|
||||
checker.end_precision();
|
||||
visit(arg_formatter<Char>(buf, ctx, specs_),
|
||||
internal::make_arg<basic_context<Char>>(val));
|
||||
visit(arg_formatter<basic_buffer<Char>>(buf, ctx, specs_),
|
||||
internal::make_arg<basic_context<basic_buffer<Char>>>(val));
|
||||
}
|
||||
|
||||
private:
|
||||
void handle_specs(basic_context<Char> &ctx) {
|
||||
template <typename Context>
|
||||
void handle_specs(Context &ctx) {
|
||||
internal::handle_dynamic_spec<internal::width_checker>(
|
||||
specs_.width_, specs_.width_ref, ctx);
|
||||
internal::handle_dynamic_spec<internal::precision_checker>(
|
||||
@ -2953,14 +2906,14 @@ struct dynamic_formatter {
|
||||
internal::dynamic_format_specs<Char> specs_;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
inline typename basic_context<Char>::format_arg
|
||||
basic_context<Char>::get_arg(basic_string_view<Char> name) {
|
||||
template <typename Range>
|
||||
typename basic_context<Range>::format_arg
|
||||
basic_context<Range>::get_arg(basic_string_view<char_type> name) {
|
||||
map_.init(this->args());
|
||||
if (const format_arg *arg = map_.find(name))
|
||||
return *arg;
|
||||
this->on_error("argument not found");
|
||||
return format_arg();
|
||||
format_arg arg = map_.find(name);
|
||||
if (arg.type() == internal::NONE)
|
||||
this->on_error("argument not found");
|
||||
return arg;
|
||||
}
|
||||
|
||||
/** Formats arguments and writes the output to the buffer. */
|
||||
@ -2972,7 +2925,7 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
||||
struct handler : internal::error_handler {
|
||||
handler(basic_buffer<Char> &b, basic_string_view<Char> str,
|
||||
basic_format_args<Context> format_args)
|
||||
: buffer(b), context(str, format_args) {}
|
||||
: buffer(b), context(b, str, format_args) {}
|
||||
|
||||
void on_text(iterator begin, iterator end) {
|
||||
buffer.append(pointer_from(begin), pointer_from(end));
|
||||
@ -2990,7 +2943,7 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
||||
void on_replacement_field(iterator it) {
|
||||
context.advance_to(pointer_from(it));
|
||||
using internal::custom_formatter;
|
||||
if (visit(custom_formatter<Char, Context>(buffer, context), arg))
|
||||
if (visit(custom_formatter<Char, Context>(context), arg))
|
||||
return;
|
||||
basic_format_specs<Char> specs;
|
||||
visit(ArgFormatter(buffer, context, specs), arg);
|
||||
@ -2999,7 +2952,7 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
||||
iterator on_format_specs(iterator it) {
|
||||
context.advance_to(pointer_from(it));
|
||||
using internal::custom_formatter;
|
||||
if (visit(custom_formatter<Char, Context>(buffer, context), arg))
|
||||
if (visit(custom_formatter<Char, Context>(context), arg))
|
||||
return iterator(context);
|
||||
basic_format_specs<Char> specs;
|
||||
using internal::specs_handler;
|
||||
@ -3014,7 +2967,7 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
||||
}
|
||||
|
||||
basic_buffer<Char> &buffer;
|
||||
basic_context<Char> context;
|
||||
Context context;
|
||||
basic_arg<Context> arg;
|
||||
};
|
||||
parse_format_string(iterator(format_str.begin(), format_str.end()),
|
||||
@ -3056,12 +3009,12 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
|
||||
basic_format_args<Context> args);
|
||||
|
||||
inline void vformat_to(buffer &buf, string_view format_str, format_args args) {
|
||||
vformat_to<arg_formatter<char>>(buf, format_str, args);
|
||||
vformat_to<arg_formatter<buffer>>(buf, format_str, args);
|
||||
}
|
||||
|
||||
inline void vformat_to(wbuffer &buf, wstring_view format_str,
|
||||
wformat_args args) {
|
||||
vformat_to<arg_formatter<wchar_t>>(buf, format_str, args);
|
||||
vformat_to<arg_formatter<wbuffer>>(buf, format_str, args);
|
||||
}
|
||||
|
||||
inline std::string vformat(string_view format_str, format_args args) {
|
||||
@ -3124,7 +3077,7 @@ struct udl_arg {
|
||||
const Char *str;
|
||||
|
||||
template <typename T>
|
||||
named_arg<basic_context<Char>> operator=(T &&value) const {
|
||||
named_arg<T, Char> operator=(T &&value) const {
|
||||
return {str, std::forward<T>(value)};
|
||||
}
|
||||
};
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
Formatting library for C++ - locale support
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// Formatting library for C++ - locale support
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include <locale>
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
Formatting library for C++ - std::ostream support
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// Formatting library for C++ - std::ostream support
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
@ -104,12 +102,12 @@ struct formatter<T, Char,
|
||||
typename std::enable_if<!internal::format_type<T>::value>::type>
|
||||
: formatter<basic_string_view<Char>, Char> {
|
||||
|
||||
void format(basic_buffer<Char> &buf, const T &value,
|
||||
basic_context<Char> &ctx) {
|
||||
template <typename Context>
|
||||
void format(const T &value, Context &ctx) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::format_value(buffer, value);
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
formatter<basic_string_view<Char>, Char>::format(buf, str, ctx);
|
||||
formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
A C++ interface to POSIX functions.
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// A C++ interface to POSIX functions.
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
A C++ interface to POSIX functions.
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// A C++ interface to POSIX functions.
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_POSIX_H_
|
||||
#define FMT_POSIX_H_
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
Formatting library for C++
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// Formatting library for C++
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_PRINTF_H_
|
||||
#define FMT_PRINTF_H_
|
||||
@ -200,11 +198,10 @@ class PrintfWidthHandler {
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename Char>
|
||||
template <typename Range>
|
||||
class printf_arg_formatter;
|
||||
|
||||
template <typename Char,
|
||||
typename ArgFormatter = printf_arg_formatter<Char> >
|
||||
template <typename Range, typename ArgFormatter = printf_arg_formatter<Range>>
|
||||
class printf_context;
|
||||
|
||||
/**
|
||||
@ -212,20 +209,22 @@ class printf_context;
|
||||
The ``printf`` argument formatter.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char>
|
||||
class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
||||
template <typename Range>
|
||||
class printf_arg_formatter :
|
||||
public internal::arg_formatter_base<typename Range::value_type> {
|
||||
private:
|
||||
printf_context<Char>& context_;
|
||||
printf_context<Range>& context_;
|
||||
|
||||
void write_null_pointer() {
|
||||
this->spec().type_ = 0;
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
typedef internal::arg_formatter_base<Char> Base;
|
||||
using char_type = typename Range::value_type;
|
||||
using base = internal::arg_formatter_base<char_type>;
|
||||
|
||||
public:
|
||||
typedef typename Base::format_specs format_specs;
|
||||
typedef typename base::format_specs format_specs;
|
||||
|
||||
/**
|
||||
\rst
|
||||
@ -234,11 +233,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
||||
specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
printf_arg_formatter(
|
||||
basic_buffer<Char> &buffer, format_specs &spec, printf_context<Char> &ctx)
|
||||
: internal::arg_formatter_base<Char>(buffer, spec), context_(ctx) {}
|
||||
printf_arg_formatter(basic_buffer<char_type> &buffer, format_specs &spec,
|
||||
printf_context<Range> &ctx)
|
||||
: internal::arg_formatter_base<char_type>(buffer, spec), context_(ctx) {}
|
||||
|
||||
using Base::operator();
|
||||
using base::operator();
|
||||
|
||||
/** Formats an argument of type ``bool``. */
|
||||
void operator()(bool value) {
|
||||
@ -250,19 +249,19 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
||||
}
|
||||
|
||||
/** Formats a character. */
|
||||
void operator()(Char value) {
|
||||
void operator()(char_type value) {
|
||||
format_specs &fmt_spec = this->spec();
|
||||
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
|
||||
return (*this)(static_cast<int>(value));
|
||||
fmt_spec.flags_ = 0;
|
||||
fmt_spec.align_ = ALIGN_RIGHT;
|
||||
Base::operator()(value);
|
||||
base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
void operator()(const char *value) {
|
||||
if (value)
|
||||
Base::operator()(value);
|
||||
base::operator()(value);
|
||||
else if (this->spec().type_ == 'p')
|
||||
write_null_pointer();
|
||||
else
|
||||
@ -272,44 +271,44 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
||||
/** Formats a pointer. */
|
||||
void operator()(const void *value) {
|
||||
if (value)
|
||||
return Base::operator()(value);
|
||||
return base::operator()(value);
|
||||
this->spec().type_ = 0;
|
||||
write_null_pointer();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
void operator()(typename basic_arg<printf_context<Char>>::handle handle) {
|
||||
handle.format(this->writer().buffer(), context_);
|
||||
void operator()(typename basic_arg<printf_context<Range>>::handle handle) {
|
||||
handle.format(context_);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char = char>
|
||||
template <typename T>
|
||||
struct printf_formatter {
|
||||
template <typename ParseContext>
|
||||
const Char *parse(ParseContext& ctx) { return ctx.begin(); }
|
||||
auto parse(ParseContext& ctx) { return ctx.begin(); }
|
||||
|
||||
void format(basic_buffer<Char> &buf, const T &value, printf_context<Char> &) {
|
||||
internal::format_value(buf, value);
|
||||
template <typename Range>
|
||||
void format(const T &value, printf_context<Range> &ctx) {
|
||||
internal::format_value(ctx.range(), value);
|
||||
}
|
||||
};
|
||||
|
||||
/** This template formats data and writes the output to a writer. */
|
||||
template <typename Char, typename ArgFormatter>
|
||||
template <typename Range, typename ArgFormatter>
|
||||
class printf_context :
|
||||
private internal::context_base<
|
||||
Char, printf_context<Char, ArgFormatter>> {
|
||||
private internal::context_base<Range, printf_context<Range, ArgFormatter>> {
|
||||
public:
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
using char_type = typename Range::value_type;
|
||||
|
||||
template <typename T>
|
||||
using formatter_type = printf_formatter<T>;
|
||||
|
||||
private:
|
||||
typedef internal::context_base<Char, printf_context> Base;
|
||||
typedef typename Base::format_arg format_arg;
|
||||
typedef basic_format_specs<Char> format_specs;
|
||||
typedef internal::null_terminating_iterator<Char> iterator;
|
||||
typedef internal::context_base<Range, printf_context> Base;
|
||||
using format_arg = typename Base::format_arg;
|
||||
using format_specs = basic_format_specs<char_type>;
|
||||
using iterator = internal::null_terminating_iterator<char_type>;
|
||||
|
||||
void parse_flags(format_specs &spec, iterator &it);
|
||||
|
||||
@ -330,18 +329,19 @@ class printf_context :
|
||||
appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
printf_context(basic_string_view<Char> format_str,
|
||||
printf_context(Range &range, basic_string_view<char_type> format_str,
|
||||
basic_format_args<printf_context> args)
|
||||
: Base(format_str, args) {}
|
||||
: Base(range, format_str, args) {}
|
||||
|
||||
using Base::parse_context;
|
||||
using Base::range;
|
||||
|
||||
/** Formats stored arguments and writes the output to the buffer. */
|
||||
FMT_API void format(basic_buffer<Char> &buffer);
|
||||
/** Formats stored arguments and writes the output to the range. */
|
||||
FMT_API void format();
|
||||
};
|
||||
|
||||
template <typename Char, typename AF>
|
||||
void printf_context<Char, AF>::parse_flags(format_specs &spec, iterator &it) {
|
||||
template <typename Range, typename AF>
|
||||
void printf_context<Range, AF>::parse_flags(format_specs &spec, iterator &it) {
|
||||
for (;;) {
|
||||
switch (*it++) {
|
||||
case '-':
|
||||
@ -366,20 +366,20 @@ void printf_context<Char, AF>::parse_flags(format_specs &spec, iterator &it) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename AF>
|
||||
typename printf_context<Char, AF>::format_arg printf_context<Char, AF>::get_arg(
|
||||
iterator it, unsigned arg_index) {
|
||||
template <typename Range, typename AF>
|
||||
typename printf_context<Range, AF>::format_arg
|
||||
printf_context<Range, AF>::get_arg(iterator it, unsigned arg_index) {
|
||||
(void)it;
|
||||
if (arg_index == std::numeric_limits<unsigned>::max())
|
||||
return this->do_get_arg(this->next_arg_id());
|
||||
return Base::get_arg(arg_index - 1);
|
||||
}
|
||||
|
||||
template <typename Char, typename AF>
|
||||
unsigned printf_context<Char, AF>::parse_header(
|
||||
template <typename Range, typename AF>
|
||||
unsigned printf_context<Range, AF>::parse_header(
|
||||
iterator &it, format_specs &spec) {
|
||||
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
||||
Char c = *it;
|
||||
char_type c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
@ -406,19 +406,21 @@ unsigned printf_context<Char, AF>::parse_header(
|
||||
spec.width_ = parse_nonnegative_int(it, eh);
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
spec.width_ = visit(internal::PrintfWidthHandler<Char>(spec), get_arg(it));
|
||||
spec.width_ =
|
||||
visit(internal::PrintfWidthHandler<char_type>(spec), get_arg(it));
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
template <typename Char, typename AF>
|
||||
void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
|
||||
template <typename Range, typename AF>
|
||||
void printf_context<Range, AF>::format() {
|
||||
Range &buffer = this->range();
|
||||
Base &base = *this;
|
||||
auto start = iterator(base);
|
||||
auto it = start;
|
||||
using internal::pointer_from;
|
||||
while (*it) {
|
||||
Char c = *it++;
|
||||
char_type c = *it++;
|
||||
if (c != '%') continue;
|
||||
if (*it == c) {
|
||||
buffer.append(pointer_from(start), pointer_from(it));
|
||||
@ -501,7 +503,7 @@ void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
|
||||
break;
|
||||
case 'c':
|
||||
// TODO: handle wchar_t
|
||||
visit(internal::CharConverter<printf_context<Char, AF>>(arg), arg);
|
||||
visit(internal::CharConverter<printf_context<Range, AF>>(arg), arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -516,11 +518,11 @@ void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
|
||||
|
||||
template <typename Char>
|
||||
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
|
||||
basic_format_args<printf_context<Char>> args) {
|
||||
printf_context<Char>(format, args).format(buf);
|
||||
basic_format_args<printf_context<basic_buffer<Char>>> args) {
|
||||
printf_context<basic_buffer<Char>>(buf, format, args).format();
|
||||
}
|
||||
|
||||
typedef basic_format_args<printf_context<char>> printf_args;
|
||||
typedef basic_format_args<printf_context<buffer>> printf_args;
|
||||
|
||||
inline std::string vsprintf(string_view format, printf_args args) {
|
||||
memory_buffer buffer;
|
||||
@ -539,11 +541,11 @@ inline std::string vsprintf(string_view format, printf_args args) {
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline std::string sprintf(string_view format_str, const Args & ... args) {
|
||||
return vsprintf(format_str, make_args<printf_context<char>>(args...));
|
||||
return vsprintf(format_str, make_args<printf_context<buffer>>(args...));
|
||||
}
|
||||
|
||||
inline std::wstring vsprintf(
|
||||
wstring_view format, basic_format_args<printf_context<wchar_t>> args) {
|
||||
wstring_view format, basic_format_args<printf_context<wbuffer>> args) {
|
||||
wmemory_buffer buffer;
|
||||
printf(buffer, format, args);
|
||||
return to_string(buffer);
|
||||
@ -551,7 +553,7 @@ inline std::wstring vsprintf(
|
||||
|
||||
template <typename... Args>
|
||||
inline std::wstring sprintf(wstring_view format_str, const Args & ... args) {
|
||||
auto vargs = make_args<printf_context<wchar_t>>(args...);
|
||||
auto vargs = make_args<printf_context<wbuffer>>(args...);
|
||||
return vsprintf(format_str, vargs);
|
||||
}
|
||||
|
||||
@ -574,7 +576,7 @@ inline int vfprintf(std::FILE *f, string_view format, printf_args args) {
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) {
|
||||
auto vargs = make_args<printf_context<char>>(args...);
|
||||
auto vargs = make_args<printf_context<buffer>>(args...);
|
||||
return vfprintf(f, format_str, vargs);
|
||||
}
|
||||
|
||||
@ -593,10 +595,11 @@ inline int vprintf(string_view format, printf_args args) {
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline int printf(string_view format_str, const Args & ... args) {
|
||||
return vprintf(format_str, make_args<printf_context<char>>(args...));
|
||||
return vprintf(format_str, make_args<printf_context<buffer>>(args...));
|
||||
}
|
||||
|
||||
inline int vfprintf(std::ostream &os, string_view format_str, printf_args args) {
|
||||
inline int vfprintf(std::ostream &os, string_view format_str,
|
||||
printf_args args) {
|
||||
memory_buffer buffer;
|
||||
printf(buffer, format_str, args);
|
||||
internal::write(os, buffer);
|
||||
@ -615,7 +618,7 @@ inline int vfprintf(std::ostream &os, string_view format_str, printf_args args)
|
||||
template <typename... Args>
|
||||
inline int fprintf(std::ostream &os, string_view format_str,
|
||||
const Args & ... args) {
|
||||
auto vargs = make_args<printf_context<char>>(args...);
|
||||
auto vargs = make_args<printf_context<buffer>>(args...);
|
||||
return vfprintf(os, format_str, vargs);
|
||||
}
|
||||
} // namespace fmt
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
Formatting library for C++ - string utilities
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// Formatting library for C++ - string utilities
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_STRING_H_
|
||||
#define FMT_STRING_H_
|
||||
|
@ -1,11 +1,9 @@
|
||||
/*
|
||||
Formatting library for C++ - time formatting
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
// Formatting library for C++ - time formatting
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_TIME_H_
|
||||
#define FMT_TIME_H_
|
||||
@ -32,7 +30,8 @@ struct formatter<std::tm> {
|
||||
return pointer_from(end);
|
||||
}
|
||||
|
||||
void format(buffer &buf, const std::tm &tm, context &) {
|
||||
void format(const std::tm &tm, context &ctx) {
|
||||
buffer &buf = ctx.range();
|
||||
std::size_t start = buf.size();
|
||||
for (;;) {
|
||||
std::size_t size = buf.capacity() - start;
|
||||
|
@ -14,18 +14,18 @@ using fmt::printf_arg_formatter;
|
||||
|
||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
||||
// rounded to 0.
|
||||
class CustomArgFormatter : public fmt::arg_formatter<char> {
|
||||
class CustomArgFormatter : public fmt::arg_formatter<fmt::buffer> {
|
||||
public:
|
||||
CustomArgFormatter(fmt::buffer &buf, fmt::basic_context<char> &ctx,
|
||||
CustomArgFormatter(fmt::buffer &buf, fmt::basic_context<fmt::buffer> &ctx,
|
||||
fmt::format_specs &s)
|
||||
: fmt::arg_formatter<char>(buf, ctx, s) {}
|
||||
: fmt::arg_formatter<fmt::buffer>(buf, ctx, s) {}
|
||||
|
||||
using fmt::arg_formatter<char>::operator();
|
||||
using fmt::arg_formatter<fmt::buffer>::operator();
|
||||
|
||||
void operator()(double value) {
|
||||
if (round(value * pow(10, spec().precision())) == 0)
|
||||
value = 0;
|
||||
fmt::arg_formatter<char>::operator()(value);
|
||||
fmt::arg_formatter<fmt::buffer>::operator()(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
@ -91,7 +90,7 @@ template <typename Char, typename T>
|
||||
fmt::basic_memory_buffer<Char> buffer;
|
||||
fmt::basic_writer<fmt::basic_buffer<Char>> writer(buffer);
|
||||
writer.write(value);
|
||||
std::basic_string<Char> actual = writer.str();
|
||||
std::basic_string<Char> actual = to_string(buffer);
|
||||
std::basic_string<Char> expected;
|
||||
std_format(value, expected);
|
||||
if (expected == actual)
|
||||
@ -149,19 +148,11 @@ TEST(WriterTest, NotCopyAssignable) {
|
||||
EXPECT_FALSE(std::is_copy_assignable<basic_writer<fmt::buffer>>::value);
|
||||
}
|
||||
|
||||
TEST(WriterTest, Ctor) {
|
||||
memory_buffer buf;
|
||||
fmt::basic_writer<fmt::buffer> w(buf);
|
||||
EXPECT_EQ(0u, w.size());
|
||||
EXPECT_STREQ("", w.c_str());
|
||||
EXPECT_EQ("", w.str());
|
||||
}
|
||||
|
||||
TEST(WriterTest, Data) {
|
||||
memory_buffer buf;
|
||||
fmt::basic_writer<fmt::buffer> w(buf);
|
||||
w.write(42);
|
||||
EXPECT_EQ("42", std::string(w.data(), w.size()));
|
||||
EXPECT_EQ("42", to_string(buf));
|
||||
}
|
||||
|
||||
TEST(WriterTest, WriteInt) {
|
||||
@ -224,7 +215,9 @@ TEST(WriterTest, WriteDoubleWithFilledBuffer) {
|
||||
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE; ++i)
|
||||
writer.write(' ');
|
||||
writer.write(1.2);
|
||||
EXPECT_STREQ("1.2", writer.c_str() + fmt::internal::INLINE_BUFFER_SIZE);
|
||||
fmt::string_view sv(buf.data(), buf.size());
|
||||
sv.remove_prefix(fmt::internal::INLINE_BUFFER_SIZE);
|
||||
EXPECT_EQ("1.2", sv);
|
||||
}
|
||||
|
||||
TEST(WriterTest, WriteChar) {
|
||||
@ -239,31 +232,29 @@ TEST(WriterTest, WriteString) {
|
||||
CHECK_WRITE_CHAR("abc");
|
||||
CHECK_WRITE_WCHAR("abc");
|
||||
// The following line shouldn't compile:
|
||||
//MemoryWriter() << L"abc";
|
||||
//std::declval<fmt::basic_writer<fmt::buffer>>().write(L"abc");
|
||||
}
|
||||
|
||||
TEST(WriterTest, WriteWideString) {
|
||||
CHECK_WRITE_WCHAR(L"abc");
|
||||
// The following line shouldn't compile:
|
||||
//fmt::WMemoryWriter() << "abc";
|
||||
//std::declval<fmt::basic_writer<fmt::wbuffer>>().write("abc");
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::string write_str(T... args) {
|
||||
memory_buffer buf;
|
||||
fmt::basic_writer<fmt::buffer> writer(buf);
|
||||
using namespace fmt;
|
||||
writer.write(args...);
|
||||
return writer.str();
|
||||
return to_string(buf);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::wstring write_wstr(T... args) {
|
||||
wmemory_buffer buf;
|
||||
fmt::basic_writer<fmt::wbuffer> writer(buf);
|
||||
using namespace fmt;
|
||||
writer.write(args...);
|
||||
return writer.str();
|
||||
return to_string(buf);
|
||||
}
|
||||
|
||||
TEST(WriterTest, bin) {
|
||||
@ -350,17 +341,20 @@ TEST(WriterTest, pad) {
|
||||
EXPECT_EQ(" 33", write_str(33ll, width=7));
|
||||
EXPECT_EQ(" 44", write_str(44ull, width=7));
|
||||
|
||||
memory_buffer buf;
|
||||
fmt::basic_writer<fmt::buffer> w(buf);
|
||||
w.clear();
|
||||
w.write(42, fmt::width=5, fmt::fill='0');
|
||||
EXPECT_EQ("00042", w.str());
|
||||
w.clear();
|
||||
w << Date(2012, 12, 9);
|
||||
EXPECT_EQ("2012-12-9", w.str());
|
||||
w.clear();
|
||||
w << iso8601(Date(2012, 1, 9));
|
||||
EXPECT_EQ("2012-01-09", w.str());
|
||||
EXPECT_EQ("00042", write_str(42, fmt::width=5, fmt::fill='0'));
|
||||
|
||||
{
|
||||
memory_buffer buf;
|
||||
fmt::basic_writer<fmt::buffer> w(buf);
|
||||
w << Date(2012, 12, 9);
|
||||
EXPECT_EQ("2012-12-9", to_string(buf));
|
||||
}
|
||||
{
|
||||
memory_buffer buf;
|
||||
fmt::basic_writer<fmt::buffer> w(buf);
|
||||
w << iso8601(Date(2012, 1, 9));
|
||||
EXPECT_EQ("2012-01-09", to_string(buf));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(WriterTest, PadString) {
|
||||
@ -1228,8 +1222,8 @@ struct formatter<Date> {
|
||||
return it;
|
||||
}
|
||||
|
||||
void format(buffer &buf, const Date &d, context &) {
|
||||
format_to(buf, "{}-{}-{}", d.year(), d.month(), d.day());
|
||||
void format(const Date &d, context &ctx) {
|
||||
format_to(ctx.range(), "{}-{}-{}", d.year(), d.month(), d.day());
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1245,8 +1239,8 @@ class Answer {};
|
||||
namespace fmt {
|
||||
template <>
|
||||
struct formatter<Answer> : formatter<int> {
|
||||
void format(fmt::buffer &buf, Answer, fmt::context &ctx) {
|
||||
formatter<int>::format(buf, 42, ctx);
|
||||
void format(Answer, fmt::context &ctx) {
|
||||
formatter<int>::format(42, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1535,11 +1529,11 @@ struct variant {
|
||||
namespace fmt {
|
||||
template <>
|
||||
struct formatter<variant> : dynamic_formatter<> {
|
||||
void format(buffer& buf, variant value, context& ctx) {
|
||||
void format(variant value, context& ctx) {
|
||||
if (value.type == variant::INT)
|
||||
dynamic_formatter::format(buf, 42, ctx);
|
||||
dynamic_formatter::format(ctx.range(), 42, ctx);
|
||||
else
|
||||
dynamic_formatter::format(buf, "foo", ctx);
|
||||
dynamic_formatter::format(ctx.range(), "foo", ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -58,14 +58,14 @@ TEST(OStreamTest, Enum) {
|
||||
EXPECT_EQ("0", fmt::format("{}", A));
|
||||
}
|
||||
|
||||
struct TestArgFormatter : fmt::arg_formatter<char> {
|
||||
struct TestArgFormatter : fmt::arg_formatter<fmt::buffer> {
|
||||
TestArgFormatter(fmt::buffer &buf, fmt::context &ctx, fmt::format_specs &s)
|
||||
: fmt::arg_formatter<char>(buf, ctx, s) {}
|
||||
: fmt::arg_formatter<fmt::buffer>(buf, ctx, s) {}
|
||||
};
|
||||
|
||||
TEST(OStreamTest, CustomArg) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::context ctx("", fmt::format_args());
|
||||
fmt::context ctx(buffer, "", fmt::format_args());
|
||||
fmt::format_specs spec;
|
||||
TestArgFormatter af(buffer, ctx, spec);
|
||||
visit(af, fmt::internal::make_arg<fmt::context>(TestEnum()));
|
||||
|
@ -81,9 +81,9 @@ struct formatter<Test, Char> {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
void format(basic_buffer<Char> &b, Test, basic_context<Char> &) {
|
||||
void format(Test, basic_context<basic_buffer<Char>> &ctx) {
|
||||
const Char *test = "test";
|
||||
b.append(test, test + std::strlen(test));
|
||||
ctx.range().append(test, test + std::strlen(test));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -442,7 +442,7 @@ struct CustomContext {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
void format(fmt::buffer &, const T &, CustomContext& ctx) {
|
||||
void format(const T &, CustomContext& ctx) {
|
||||
ctx.called = true;
|
||||
}
|
||||
};
|
||||
@ -456,8 +456,7 @@ TEST(UtilTest, MakeValueWithCustomFormatter) {
|
||||
::Test t;
|
||||
fmt::internal::value<CustomContext> arg(t);
|
||||
CustomContext ctx = {false};
|
||||
fmt::memory_buffer buffer;
|
||||
arg.custom.format(buffer, &t, ctx);
|
||||
arg.custom.format(&t, ctx);
|
||||
EXPECT_TRUE(ctx.called);
|
||||
}
|
||||
|
||||
@ -518,7 +517,8 @@ VISIT_TYPE(float, double);
|
||||
#define CHECK_ARG_(Char, expected, value) { \
|
||||
testing::StrictMock<MockVisitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
fmt::visit(visitor, make_arg<fmt::basic_context<Char>>(value)); \
|
||||
fmt::visit(visitor, \
|
||||
make_arg<fmt::basic_context<basic_buffer<Char>>>(value)); \
|
||||
}
|
||||
|
||||
#define CHECK_ARG(value) { \
|
||||
@ -596,8 +596,8 @@ TEST(UtilTest, CustomArg) {
|
||||
testing::StrictMock<visitor> v;
|
||||
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke([&](handle h) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::context ctx("", fmt::format_args());
|
||||
h.format(buffer, ctx);
|
||||
fmt::context ctx(buffer, "", fmt::format_args());
|
||||
h.format(ctx);
|
||||
EXPECT_EQ("test", std::string(buffer.data(), buffer.size()));
|
||||
return visitor::Result();
|
||||
}));
|
||||
|
Loading…
x
Reference in New Issue
Block a user