Skip to content

Commit

Permalink
Merge branch 'fmtlib:master' into zig-pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
kassane committed Oct 29, 2023
2 parents cf47456 + 19276d7 commit 1524602
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 30 deletions.
13 changes: 7 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ Features
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
for localization
* Implementation of `C++20 std::format
<https://en.cppreference.com/w/cpp/utility/format>`__
<https://en.cppreference.com/w/cpp/utility/format>`__ and `C++23 std::print
<https://en.cppreference.com/w/cpp/io/print>`__
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
Expand Down Expand Up @@ -177,15 +178,15 @@ This can be `5 to 9 times faster than fprintf
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
"Hello, {}!\n", "world");
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
fmt::emphasis::underline, "Hello, {}!\n", "мир");
fmt::emphasis::underline, "Olá, {}!\n", "Mundo");
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
"Hello, {}!\n", "世界");
"你好{}!\n", "世界");
}

Output on a modern terminal:
Output on a modern terminal with Unicode support:

.. image:: https://user-images.githubusercontent.com/
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
.. image:: https://github.com/fmtlib/fmt/assets/
576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7

Benchmarks
----------
Expand Down
38 changes: 38 additions & 0 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,44 @@ For ``build2`` newcomers or to get more details and use cases, you can read the
``build2``
`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_.

Usage with Meson
================

`Meson's WrapDB <https://mesonbuild.com/Wrapdb-projects.html>` includes a ``fmt``
package, which repackages fmt to be built by Meson as a subproject.

**Usage:**

- Install the ``fmt`` subproject from the WrapDB by running::

meson wrap install fmt

from the root of your project.

- In your project's ``meson.build`` file, add an entry for the new subproject::

fmt = subproject('fmt')
fmt_dep = fmt.get_variable('fmt_dep')

- Include the new dependency object to link with fmt::

my_build_target = executable('name', 'src/main.cc', dependencies: [fmt_dep])

**Options:**

If desired, ``fmt`` may be built as a static library, or as a header-only
library.

For a static build, use the following subproject definition::

fmt = subproject('fmt', default_options: 'default_library=static')
fmt_dep = fmt.get_variable('fmt_dep')

For the header-only version, use::

fmt = subproject('fmt')
fmt_dep = fmt.get_variable('fmt_header_only_dep')

Building the Documentation
==========================

Expand Down
26 changes: 14 additions & 12 deletions include/fmt/format-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ FMT_FUNC void report_error(format_func func, int error_code,
}

// A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
size_t written = std::fwrite(ptr, 1, count, stream);
if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
Expand Down Expand Up @@ -1426,20 +1425,16 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {

namespace detail {
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
FMT_FUNC bool write_console(int, string_view) { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
void*, const void*, dword, dword*, void*);

FMT_FUNC bool write_console(std::FILE* f, string_view text) {
auto fd = _fileno(f);
if (!_isatty(fd)) return false;
FMT_FUNC bool write_console(int fd, string_view text) {
auto u16 = utf8_to_utf16(text);
auto written = dword();
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<uint32_t>(u16.size()), &written,
nullptr) != 0;
static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
}
#endif

Expand All @@ -1448,12 +1443,19 @@ FMT_FUNC bool write_console(std::FILE* f, string_view text) {
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
fwrite_fully(buffer.data(), 1, buffer.size(), f);
fwrite_fully(buffer.data(), buffer.size(), f);
}
#endif

FMT_FUNC void print(std::FILE* f, string_view text) {
if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
#ifdef _WIN32
int fd = _fileno(f);
if (_isatty(fd)) {
std::fflush(f);
if (write_console(fd, text)) return;
}
#endif
fwrite_fully(text.data(), text.size(), f);
}
} // namespace detail

Expand Down
7 changes: 5 additions & 2 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {

FMT_END_EXPORT
namespace detail {
FMT_API bool write_console(std::FILE* f, string_view text);
FMT_API bool write_console(int fd, string_view text);
FMT_API void print(std::FILE*, string_view);
} // namespace detail

Expand Down Expand Up @@ -3751,8 +3751,11 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR auto write(OutputIt out, const T& value)
-> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
OutputIt> {
auto formatter = typename Context::template formatter_type<T>();
auto parse_ctx = typename Context::parse_context_type({});
formatter.parse(parse_ctx);
auto ctx = Context(out, {}, {});
return typename Context::template formatter_type<T>().format(value, ctx);
return formatter.format(value, ctx);
}

// An argument visitor that formats the argument and writes it via the output
Expand Down
31 changes: 22 additions & 9 deletions include/fmt/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

#include <fstream> // std::filebuf

#if defined(_WIN32) && defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif

#include "format.h"
Expand All @@ -38,21 +41,31 @@ auto get_file(std::filebuf&) -> FILE*;
#endif

inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
FILE* f = nullptr;
#if FMT_MSC_VERSION
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data);
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf();
FILE* c_file;
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
c_file = sfbuf->file();
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
c_file = fbuf->file();
f = fbuf->file();
else
return false;
if (c_file) return write_console(c_file, data);
#else
ignore_unused(os, data);
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
Expand Down
7 changes: 6 additions & 1 deletion test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,18 @@ TEST(ranges_test, format_vector) {
EXPECT_EQ(fmt::format("{:n:n:}", vvc), "a, b, c, a, b, c");
}

TEST(ranges_test, format_vector2) {
TEST(ranges_test, format_nested_vector) {
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
EXPECT_EQ(fmt::format("{:n:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
}

TEST(ranges_test, to_string_vector) {
auto v = std::vector<std::string>{"a", "b", "c"};
EXPECT_EQ(fmt::to_string(v), "[\"a\", \"b\", \"c\"]");
}

TEST(ranges_test, format_map) {
auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}");
Expand Down

0 comments on commit 1524602

Please sign in to comment.