| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| // Formatting library for C++ - dynamic format arguments | ||
| // | ||
| // Copyright (c) 2012 - present, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #ifndef FMT_ARGS_H_ | ||
| #define FMT_ARGS_H_ | ||
|
|
||
| #include <functional> // std::reference_wrapper | ||
| #include <memory> // std::unique_ptr | ||
| #include <vector> | ||
|
|
||
| #include "core.h" | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
|
|
||
| namespace detail { | ||
|
|
||
| template <typename T> struct is_reference_wrapper : std::false_type {}; | ||
| template <typename T> | ||
| struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; | ||
|
|
||
| template <typename T> const T& unwrap(const T& v) { return v; } | ||
| template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) { | ||
| return static_cast<const T&>(v); | ||
| } | ||
|
|
||
| class dynamic_arg_list { | ||
| // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for | ||
| // templates it doesn't complain about inability to deduce single translation | ||
| // unit for placing vtable. So storage_node_base is made a fake template. | ||
| template <typename = void> struct node { | ||
| virtual ~node() = default; | ||
| std::unique_ptr<node<>> next; | ||
| }; | ||
|
|
||
| template <typename T> struct typed_node : node<> { | ||
| T value; | ||
|
|
||
| template <typename Arg> | ||
| FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} | ||
|
|
||
| template <typename Char> | ||
| FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg) | ||
| : value(arg.data(), arg.size()) {} | ||
| }; | ||
|
|
||
| std::unique_ptr<node<>> head_; | ||
|
|
||
| public: | ||
| template <typename T, typename Arg> const T& push(const Arg& arg) { | ||
| auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg)); | ||
| auto& value = new_node->value; | ||
| new_node->next = std::move(head_); | ||
| head_ = std::move(new_node); | ||
| return value; | ||
| } | ||
| }; | ||
| } // namespace detail | ||
|
|
||
| /** | ||
| \rst | ||
| A dynamic version of `fmt::format_arg_store`. | ||
| It's equipped with a storage to potentially temporary objects which lifetimes | ||
| could be shorter than the format arguments object. | ||
| It can be implicitly converted into `~fmt::basic_format_args` for passing | ||
| into type-erased formatting functions such as `~fmt::vformat`. | ||
| \endrst | ||
| */ | ||
| template <typename Context> | ||
| class dynamic_format_arg_store | ||
| #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 | ||
| // Workaround a GCC template argument substitution bug. | ||
| : public basic_format_args<Context> | ||
| #endif | ||
| { | ||
| private: | ||
| using char_type = typename Context::char_type; | ||
|
|
||
| template <typename T> struct need_copy { | ||
| static constexpr detail::type mapped_type = | ||
| detail::mapped_type_constant<T, Context>::value; | ||
|
|
||
| enum { | ||
| value = !(detail::is_reference_wrapper<T>::value || | ||
| std::is_same<T, basic_string_view<char_type>>::value || | ||
| std::is_same<T, detail::std_string_view<char_type>>::value || | ||
| (mapped_type != detail::type::cstring_type && | ||
| mapped_type != detail::type::string_type && | ||
| mapped_type != detail::type::custom_type)) | ||
| }; | ||
| }; | ||
|
|
||
| template <typename T> | ||
| using stored_type = conditional_t<detail::is_string<T>::value && | ||
| !has_formatter<T, Context>::value && | ||
| !detail::is_reference_wrapper<T>::value, | ||
| std::basic_string<char_type>, T>; | ||
|
|
||
| // Storage of basic_format_arg must be contiguous. | ||
| std::vector<basic_format_arg<Context>> data_; | ||
| std::vector<detail::named_arg_info<char_type>> named_info_; | ||
|
|
||
| // Storage of arguments not fitting into basic_format_arg must grow | ||
| // without relocation because items in data_ refer to it. | ||
| detail::dynamic_arg_list dynamic_args_; | ||
|
|
||
| friend class basic_format_args<Context>; | ||
|
|
||
| unsigned long long get_types() const { | ||
| return detail::is_unpacked_bit | data_.size() | | ||
| (named_info_.empty() | ||
| ? 0ULL | ||
| : static_cast<unsigned long long>(detail::has_named_args_bit)); | ||
| } | ||
|
|
||
| const basic_format_arg<Context>* data() const { | ||
| return named_info_.empty() ? data_.data() : data_.data() + 1; | ||
| } | ||
|
|
||
| template <typename T> void emplace_arg(const T& arg) { | ||
| data_.emplace_back(detail::make_arg<Context>(arg)); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void emplace_arg(const detail::named_arg<char_type, T>& arg) { | ||
| if (named_info_.empty()) { | ||
| constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr}; | ||
| data_.insert(data_.begin(), {zero_ptr, 0}); | ||
| } | ||
| data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value))); | ||
| auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { | ||
| data->pop_back(); | ||
| }; | ||
| std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> | ||
| guard{&data_, pop_one}; | ||
| named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); | ||
| data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; | ||
| guard.release(); | ||
| } | ||
|
|
||
| public: | ||
| constexpr dynamic_format_arg_store() = default; | ||
|
|
||
| /** | ||
| \rst | ||
| Adds an argument into the dynamic store for later passing to a formatting | ||
| function. | ||
| Note that custom types and string types (but not string views) are copied | ||
| into the store dynamically allocating memory if necessary. | ||
| **Example**:: | ||
| fmt::dynamic_format_arg_store<fmt::format_context> store; | ||
| store.push_back(42); | ||
| store.push_back("abc"); | ||
| store.push_back(1.5f); | ||
| std::string result = fmt::vformat("{} and {} and {}", store); | ||
| \endrst | ||
| */ | ||
| template <typename T> void push_back(const T& arg) { | ||
| if (detail::const_check(need_copy<T>::value)) | ||
| emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); | ||
| else | ||
| emplace_arg(detail::unwrap(arg)); | ||
| } | ||
|
|
||
| /** | ||
| \rst | ||
| Adds a reference to the argument into the dynamic store for later passing to | ||
| a formatting function. | ||
| **Example**:: | ||
| fmt::dynamic_format_arg_store<fmt::format_context> store; | ||
| char band[] = "Rolling Stones"; | ||
| store.push_back(std::cref(band)); | ||
| band[9] = 'c'; // Changing str affects the output. | ||
| std::string result = fmt::vformat("{}", store); | ||
| // result == "Rolling Scones" | ||
| \endrst | ||
| */ | ||
| template <typename T> void push_back(std::reference_wrapper<T> arg) { | ||
| static_assert( | ||
| need_copy<T>::value, | ||
| "objects of built-in types and string views are always copied"); | ||
| emplace_arg(arg.get()); | ||
| } | ||
|
|
||
| /** | ||
| Adds named argument into the dynamic store for later passing to a formatting | ||
| function. ``std::reference_wrapper`` is supported to avoid copying of the | ||
| argument. The name is always copied into the store. | ||
| */ | ||
| template <typename T> | ||
| void push_back(const detail::named_arg<char_type, T>& arg) { | ||
| const char_type* arg_name = | ||
| dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); | ||
| if (detail::const_check(need_copy<T>::value)) { | ||
| emplace_arg( | ||
| fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); | ||
| } else { | ||
| emplace_arg(fmt::arg(arg_name, arg.value)); | ||
| } | ||
| } | ||
|
|
||
| /** Erase all elements from the store */ | ||
| void clear() { | ||
| data_.clear(); | ||
| named_info_.clear(); | ||
| dynamic_args_ = detail::dynamic_arg_list(); | ||
| } | ||
|
|
||
| /** | ||
| \rst | ||
| Reserves space to store at least *new_cap* arguments including | ||
| *new_cap_named* named arguments. | ||
| \endrst | ||
| */ | ||
| void reserve(size_t new_cap, size_t new_cap_named) { | ||
| FMT_ASSERT(new_cap >= new_cap_named, | ||
| "Set of arguments includes set of named arguments"); | ||
| data_.reserve(new_cap); | ||
| named_info_.reserve(new_cap_named); | ||
| } | ||
| }; | ||
|
|
||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_ARGS_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,64 +1,2 @@ | ||
| #include "xchar.h" | ||
| #warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,236 @@ | ||
| // Formatting library for C++ - optional wchar_t and exotic character support | ||
| // | ||
| // Copyright (c) 2012 - present, Victor Zverovich | ||
| // All rights reserved. | ||
| // | ||
| // For the license information refer to format.h. | ||
|
|
||
| #ifndef FMT_XCHAR_H_ | ||
| #define FMT_XCHAR_H_ | ||
|
|
||
| #include <cwchar> | ||
| #include <tuple> | ||
|
|
||
| #include "format.h" | ||
|
|
||
| FMT_BEGIN_NAMESPACE | ||
| namespace detail { | ||
| template <typename T> | ||
| using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; | ||
| } | ||
|
|
||
| FMT_MODULE_EXPORT_BEGIN | ||
|
|
||
| using wstring_view = basic_string_view<wchar_t>; | ||
| using wformat_parse_context = basic_format_parse_context<wchar_t>; | ||
| using wformat_context = buffer_context<wchar_t>; | ||
| using wformat_args = basic_format_args<wformat_context>; | ||
| using wmemory_buffer = basic_memory_buffer<wchar_t>; | ||
|
|
||
| #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 | ||
| // Workaround broken conversion on older gcc. | ||
| template <typename... Args> using wformat_string = wstring_view; | ||
| #else | ||
| template <typename... Args> | ||
| using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; | ||
| #endif | ||
|
|
||
| template <> struct is_char<wchar_t> : std::true_type {}; | ||
| template <> struct is_char<detail::char8_type> : std::true_type {}; | ||
| template <> struct is_char<char16_t> : std::true_type {}; | ||
| template <> struct is_char<char32_t> : std::true_type {}; | ||
|
|
||
| template <typename... Args> | ||
| constexpr format_arg_store<wformat_context, Args...> make_wformat_args( | ||
| const Args&... args) { | ||
| return {args...}; | ||
| } | ||
|
|
||
| inline namespace literals { | ||
| constexpr auto operator"" _format(const wchar_t* s, size_t n) | ||
| -> detail::udl_formatter<wchar_t> { | ||
| return {{s, n}}; | ||
| } | ||
|
|
||
| #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS | ||
| constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { | ||
| return {s}; | ||
| } | ||
| #endif | ||
| } // namespace literals | ||
|
|
||
| template <typename It, typename Sentinel> | ||
| auto join(It begin, Sentinel end, wstring_view sep) | ||
| -> join_view<It, Sentinel, wchar_t> { | ||
| return {begin, end, sep}; | ||
| } | ||
|
|
||
| template <typename Range> | ||
| auto join(Range&& range, wstring_view sep) | ||
| -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, | ||
| wchar_t> { | ||
| return join(std::begin(range), std::end(range), sep); | ||
| } | ||
|
|
||
| template <typename T> | ||
| auto join(std::initializer_list<T> list, wstring_view sep) | ||
| -> join_view<const T*, const T*, wchar_t> { | ||
| return join(std::begin(list), std::end(list), sep); | ||
| } | ||
|
|
||
| template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> | ||
| auto vformat(basic_string_view<Char> format_str, | ||
| basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||
| -> std::basic_string<Char> { | ||
| basic_memory_buffer<Char> buffer; | ||
| detail::vformat_to(buffer, format_str, args); | ||
| return to_string(buffer); | ||
| } | ||
|
|
||
| // Pass char_t as a default template parameter instead of using | ||
| // std::basic_string<char_t<S>> to reduce the symbol size. | ||
| template <typename S, typename... Args, typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(!std::is_same<Char, char>::value)> | ||
| auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { | ||
| const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); | ||
| return vformat(to_string_view(format_str), vargs); | ||
| } | ||
|
|
||
| template <typename Locale, typename S, typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_locale<Locale>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| inline auto vformat( | ||
| const Locale& loc, const S& format_str, | ||
| basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||
| -> std::basic_string<Char> { | ||
| return detail::vformat(loc, to_string_view(format_str), args); | ||
| } | ||
|
|
||
| template <typename Locale, typename S, typename... Args, | ||
| typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_locale<Locale>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| inline auto format(const Locale& loc, const S& format_str, Args&&... args) | ||
| -> std::basic_string<Char> { | ||
| return detail::vformat(loc, to_string_view(format_str), | ||
| fmt::make_args_checked<Args...>(format_str, args...)); | ||
| } | ||
|
|
||
| template <typename OutputIt, typename S, typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| auto vformat_to(OutputIt out, const S& format_str, | ||
| basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||
| -> OutputIt { | ||
| auto&& buf = detail::get_buffer<Char>(out); | ||
| detail::vformat_to(buf, to_string_view(format_str), args); | ||
| return detail::get_iterator(buf); | ||
| } | ||
|
|
||
| template <typename OutputIt, typename S, typename... Args, | ||
| typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { | ||
| const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); | ||
| return vformat_to(out, to_string_view(fmt), vargs); | ||
| } | ||
|
|
||
| template <typename S, typename... Args, typename Char, size_t SIZE, | ||
| typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)> | ||
| FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf, | ||
| const S& format_str, Args&&... args) -> | ||
| typename buffer_context<Char>::iterator { | ||
| const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); | ||
| detail::vformat_to(buf, to_string_view(format_str), vargs, {}); | ||
| return detail::buffer_appender<Char>(buf); | ||
| } | ||
|
|
||
| template <typename Locale, typename S, typename OutputIt, typename... Args, | ||
| typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||
| detail::is_locale<Locale>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| inline auto vformat_to( | ||
| OutputIt out, const Locale& loc, const S& format_str, | ||
| basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { | ||
| auto&& buf = detail::get_buffer<Char>(out); | ||
| vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); | ||
| return detail::get_iterator(buf); | ||
| } | ||
|
|
||
| template < | ||
| typename OutputIt, typename Locale, typename S, typename... Args, | ||
| typename Char = char_t<S>, | ||
| bool enable = detail::is_output_iterator<OutputIt, Char>::value&& | ||
| detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> | ||
| inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, | ||
| Args&&... args) -> | ||
| typename std::enable_if<enable, OutputIt>::type { | ||
| const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); | ||
| return vformat_to(out, loc, to_string_view(format_str), vargs); | ||
| } | ||
|
|
||
| template <typename OutputIt, typename Char, typename... Args, | ||
| FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| inline auto vformat_to_n( | ||
| OutputIt out, size_t n, basic_string_view<Char> format_str, | ||
| basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||
| -> format_to_n_result<OutputIt> { | ||
| detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, | ||
| n); | ||
| detail::vformat_to(buf, format_str, args); | ||
| return {buf.out(), buf.count()}; | ||
| } | ||
|
|
||
| template <typename OutputIt, typename S, typename... Args, | ||
| typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||
| detail::is_exotic_char<Char>::value)> | ||
| inline auto format_to_n(OutputIt out, size_t n, const S& fmt, | ||
| const Args&... args) -> format_to_n_result<OutputIt> { | ||
| const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); | ||
| return vformat_to_n(out, n, to_string_view(fmt), vargs); | ||
| } | ||
|
|
||
| template <typename S, typename... Args, typename Char = char_t<S>, | ||
| FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> | ||
| inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { | ||
| detail::counting_buffer<Char> buf; | ||
| const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); | ||
| detail::vformat_to(buf, to_string_view(fmt), vargs); | ||
| return buf.count(); | ||
| } | ||
|
|
||
| inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { | ||
| wmemory_buffer buffer; | ||
| detail::vformat_to(buffer, fmt, args); | ||
| buffer.push_back(L'\0'); | ||
| if (std::fputws(buffer.data(), f) == -1) | ||
| FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | ||
| } | ||
|
|
||
| inline void vprint(wstring_view fmt, wformat_args args) { | ||
| vprint(stdout, fmt, args); | ||
| } | ||
|
|
||
| template <typename... T> | ||
| void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { | ||
| return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); | ||
| } | ||
|
|
||
| template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { | ||
| return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); | ||
| } | ||
|
|
||
| /** | ||
| Converts *value* to ``std::wstring`` using the default format for type *T*. | ||
| */ | ||
| template <typename T> inline auto to_wstring(const T& value) -> std::wstring { | ||
| return format(FMT_STRING(L"{}"), value); | ||
| } | ||
| FMT_MODULE_EXPORT_END | ||
| FMT_END_NAMESPACE | ||
|
|
||
| #endif // FMT_XCHAR_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| module; | ||
| #ifndef __cpp_modules | ||
| # error Module not supported. | ||
| #endif | ||
|
|
||
| // put all implementation-provided headers into the global module fragment | ||
| // to prevent attachment to this module | ||
| #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) | ||
| # define _CRT_SECURE_NO_WARNINGS | ||
| #endif | ||
| #if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32) | ||
| # define WIN32_LEAN_AND_MEAN | ||
| #endif | ||
|
|
||
| #include <algorithm> | ||
| #include <cctype> | ||
| #include <cerrno> | ||
| #include <chrono> | ||
| #include <climits> | ||
| #include <clocale> | ||
| #include <cmath> | ||
| #include <cstdarg> | ||
| #include <cstddef> | ||
| #include <cstdint> | ||
| #include <cstdio> | ||
| #include <cstdlib> | ||
| #include <cstring> | ||
| #include <ctime> | ||
| #include <cwchar> | ||
| #include <exception> | ||
| #include <functional> | ||
| #include <iterator> | ||
| #include <limits> | ||
| #include <locale> | ||
| #include <memory> | ||
| #include <ostream> | ||
| #include <sstream> | ||
| #include <stdexcept> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <system_error> | ||
| #include <type_traits> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #if _MSC_VER | ||
| # include <intrin.h> | ||
| #endif | ||
| #if defined __APPLE__ || defined(__FreeBSD__) | ||
| # include <xlocale.h> | ||
| #endif | ||
| #if __has_include(<winapifamily.h>) | ||
| # include <winapifamily.h> | ||
| #endif | ||
| #if (__has_include(<fcntl.h>) || defined(__APPLE__) || \ | ||
| defined(__linux__)) && \ | ||
| (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) | ||
| # include <fcntl.h> | ||
| # include <sys/stat.h> | ||
| # include <sys/types.h> | ||
| # ifndef _WIN32 | ||
| # include <unistd.h> | ||
| # else | ||
| # include <io.h> | ||
| # endif | ||
| #endif | ||
| #ifdef _WIN32 | ||
| # include <windows.h> | ||
| #endif | ||
|
|
||
| export module fmt; | ||
|
|
||
| #define FMT_MODULE_EXPORT export | ||
| #define FMT_MODULE_EXPORT_BEGIN export { | ||
| #define FMT_MODULE_EXPORT_END } | ||
| #define FMT_BEGIN_DETAIL_NAMESPACE \ | ||
| } \ | ||
| namespace detail { | ||
| #define FMT_END_DETAIL_NAMESPACE \ | ||
| } \ | ||
| export { | ||
| // all library-provided declarations and definitions | ||
| // must be in the module purview to be exported | ||
| #include "fmt/args.h" | ||
| #include "fmt/chrono.h" | ||
| #include "fmt/color.h" | ||
| #include "fmt/compile.h" | ||
| #include "fmt/format.h" | ||
| #include "fmt/os.h" | ||
| #include "fmt/printf.h" | ||
| #include "fmt/xchar.h" | ||
|
|
||
| // gcc doesn't yet implement private module fragments | ||
| #if !FMT_GCC_VERSION | ||
| module : private; | ||
| #endif | ||
|
|
||
| #include "format.cc" | ||
| #include "os.cc" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| build --symlink_prefix=/ # Out of source build |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 4.2.1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| cc_library( | ||
| name = "fmt", | ||
| srcs = [ | ||
| #"src/fmt.cc", # No C++ module support | ||
| "src/format.cc", | ||
| "src/os.cc", | ||
| ], | ||
| hdrs = [ | ||
| "include/fmt/args.h", | ||
| "include/fmt/chrono.h", | ||
| "include/fmt/color.h", | ||
| "include/fmt/compile.h", | ||
| "include/fmt/core.h", | ||
| "include/fmt/format.h", | ||
| "include/fmt/format-inl.h", | ||
| "include/fmt/locale.h", | ||
| "include/fmt/os.h", | ||
| "include/fmt/ostream.h", | ||
| "include/fmt/printf.h", | ||
| "include/fmt/ranges.h", | ||
| "include/fmt/xchar.h", | ||
| ], | ||
| includes = [ | ||
| "include", | ||
| "src", | ||
| ], | ||
| strip_include_prefix = "include", | ||
| visibility = ["//visibility:public"], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| # Bazel support | ||
|
|
||
| To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project. This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}). | ||
|
|
||
| ## Using {fmt} as a dependency | ||
|
|
||
| The following minimal example shows how to use {fmt} as a dependency within a Bazel project. | ||
|
|
||
| The following file structure is assumed: | ||
|
|
||
| ``` | ||
| example | ||
| ├── BUILD.bazel | ||
| ├── main.cpp | ||
| └── WORKSPACE.bazel | ||
| ``` | ||
|
|
||
| *main.cpp*: | ||
|
|
||
| ```c++ | ||
| #include "fmt/core.h" | ||
| int main() { | ||
| fmt::print("The answer is {}\n", 42); | ||
| } | ||
| ``` | ||
|
|
||
| The expected output of this example is `The answer is 42`. | ||
|
|
||
| *WORKSPACE.bazel*: | ||
|
|
||
| ```python | ||
| load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") | ||
| git_repository( | ||
| name = "fmt", | ||
| branch = "master", | ||
| remote = "https://github.com/fmtlib/fmt", | ||
| patch_cmds = [ | ||
| "mv support/bazel/.bazelrc .bazelrc", | ||
| "mv support/bazel/.bazelversion .bazelversion", | ||
| "mv support/bazel/BUILD.bazel BUILD.bazel", | ||
| "mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel", | ||
| ], | ||
| # Windows-related patch commands are only needed in the case MSYS2 is not installed. | ||
| # More details about the installation process of MSYS2 on Windows systems can be found here: | ||
| # https://docs.bazel.build/versions/main/install-windows.html#installing-compilers-and-language-runtimes | ||
| # Even if MSYS2 is installed the Windows related patch commands can still be used. | ||
| patch_cmds_win = [ | ||
| "Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc", | ||
| "Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion", | ||
| "Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel", | ||
| "Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel", | ||
| ], | ||
| ) | ||
| ``` | ||
|
|
||
| In the *WORKSPACE* file, the {fmt} GitHub repository is fetched. Using the attribute `patch_cmds` the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` are moved to the root of the {fmt} repository. This way the {fmt} repository is recognized as a bazelized workspace. | ||
|
|
||
| *BUILD.bazel*: | ||
|
|
||
| ```python | ||
| cc_binary( | ||
| name = "Demo", | ||
| srcs = ["main.cpp"], | ||
| deps = ["@fmt"], | ||
| ) | ||
| ``` | ||
|
|
||
| The *BUILD* file defines a binary named `Demo` that has a dependency to {fmt}. | ||
|
|
||
| To execute the binary you can run `bazel run //:Demo`. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| workspace(name = "fmt") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| #!/usr/bin/env python | ||
| # Build the documentation in CI. | ||
|
|
||
| from __future__ import print_function | ||
| import errno, os, shutil, subprocess, sys, urllib | ||
| from subprocess import call, check_call, Popen, PIPE, STDOUT | ||
|
|
||
| def rmtree_if_exists(dir): | ||
| try: | ||
| shutil.rmtree(dir) | ||
| except OSError as e: | ||
| if e.errno == errno.ENOENT: | ||
| pass | ||
|
|
||
| # Build the docs. | ||
| fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | ||
| sys.path.insert(0, os.path.join(fmt_dir, 'doc')) | ||
| import build | ||
| build.create_build_env() | ||
| html_dir = build.build_docs() | ||
|
|
||
| repo = 'fmtlib.github.io' | ||
| branch = os.environ['GITHUB_REF'] | ||
| is_ci = 'CI' in os.environ | ||
| if is_ci and branch != 'refs/heads/master': | ||
| print('Branch: ' + branch) | ||
| exit(0) # Ignore non-master branches | ||
| if is_ci and 'KEY' not in os.environ: | ||
| # Don't update the repo if building in CI from an account that doesn't have | ||
| # push access. | ||
| print('Skipping update of ' + repo) | ||
| exit(0) | ||
|
|
||
| # Clone the fmtlib.github.io repo. | ||
| rmtree_if_exists(repo) | ||
| git_url = 'https://github.com/' if is_ci else 'git@github.com:' | ||
| check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)]) | ||
|
|
||
| # Copy docs to the repo. | ||
| target_dir = os.path.join(repo, 'dev') | ||
| rmtree_if_exists(target_dir) | ||
| shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*')) | ||
| if is_ci: | ||
| check_call(['git', 'config', '--global', 'user.name', 'fmtbot']) | ||
| check_call(['git', 'config', '--global', 'user.email', 'viz@fmt.dev']) | ||
|
|
||
| # Push docs to GitHub pages. | ||
| check_call(['git', 'add', '--all'], cwd=repo) | ||
| if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo): | ||
| check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo) | ||
| cmd = 'git push' | ||
| if is_ci: | ||
| cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master' | ||
| p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo) | ||
| # Print the output without the key. | ||
| print(p.communicate()[0].decode('utf-8').replace(os.environ['KEY'], '$KEY')) | ||
| if p.returncode != 0: | ||
| raise subprocess.CalledProcessError(p.returncode, cmd) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| # This script is based on | ||
| # https://github.com/rust-lang/rust/blob/master/library/core/src/unicode/printable.py | ||
| # distributed under https://github.com/rust-lang/rust/blob/master/LICENSE-MIT. | ||
|
|
||
| # This script uses the following Unicode tables: | ||
| # - UnicodeData.txt | ||
|
|
||
|
|
||
| from collections import namedtuple | ||
| import csv | ||
| import os | ||
| import subprocess | ||
|
|
||
| NUM_CODEPOINTS=0x110000 | ||
|
|
||
| def to_ranges(iter): | ||
| current = None | ||
| for i in iter: | ||
| if current is None or i != current[1] or i in (0x10000, 0x20000): | ||
| if current is not None: | ||
| yield tuple(current) | ||
| current = [i, i + 1] | ||
| else: | ||
| current[1] += 1 | ||
| if current is not None: | ||
| yield tuple(current) | ||
|
|
||
| def get_escaped(codepoints): | ||
| for c in codepoints: | ||
| if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '): | ||
| yield c.value | ||
|
|
||
| def get_file(f): | ||
| try: | ||
| return open(os.path.basename(f)) | ||
| except FileNotFoundError: | ||
| subprocess.run(["curl", "-O", f], check=True) | ||
| return open(os.path.basename(f)) | ||
|
|
||
| Codepoint = namedtuple('Codepoint', 'value class_') | ||
|
|
||
| def get_codepoints(f): | ||
| r = csv.reader(f, delimiter=";") | ||
| prev_codepoint = 0 | ||
| class_first = None | ||
| for row in r: | ||
| codepoint = int(row[0], 16) | ||
| name = row[1] | ||
| class_ = row[2] | ||
|
|
||
| if class_first is not None: | ||
| if not name.endswith("Last>"): | ||
| raise ValueError("Missing Last after First") | ||
|
|
||
| for c in range(prev_codepoint + 1, codepoint): | ||
| yield Codepoint(c, class_first) | ||
|
|
||
| class_first = None | ||
| if name.endswith("First>"): | ||
| class_first = class_ | ||
|
|
||
| yield Codepoint(codepoint, class_) | ||
| prev_codepoint = codepoint | ||
|
|
||
| if class_first is not None: | ||
| raise ValueError("Missing Last after First") | ||
|
|
||
| for c in range(prev_codepoint + 1, NUM_CODEPOINTS): | ||
| yield Codepoint(c, None) | ||
|
|
||
| def compress_singletons(singletons): | ||
| uppers = [] # (upper, # items in lowers) | ||
| lowers = [] | ||
|
|
||
| for i in singletons: | ||
| upper = i >> 8 | ||
| lower = i & 0xff | ||
| if len(uppers) == 0 or uppers[-1][0] != upper: | ||
| uppers.append((upper, 1)) | ||
| else: | ||
| upper, count = uppers[-1] | ||
| uppers[-1] = upper, count + 1 | ||
| lowers.append(lower) | ||
|
|
||
| return uppers, lowers | ||
|
|
||
| def compress_normal(normal): | ||
| # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f | ||
| # lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff | ||
| compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] | ||
|
|
||
| prev_start = 0 | ||
| for start, count in normal: | ||
| truelen = start - prev_start | ||
| falselen = count | ||
| prev_start = start + count | ||
|
|
||
| assert truelen < 0x8000 and falselen < 0x8000 | ||
| entry = [] | ||
| if truelen > 0x7f: | ||
| entry.append(0x80 | (truelen >> 8)) | ||
| entry.append(truelen & 0xff) | ||
| else: | ||
| entry.append(truelen & 0x7f) | ||
| if falselen > 0x7f: | ||
| entry.append(0x80 | (falselen >> 8)) | ||
| entry.append(falselen & 0xff) | ||
| else: | ||
| entry.append(falselen & 0x7f) | ||
|
|
||
| compressed.append(entry) | ||
|
|
||
| return compressed | ||
|
|
||
| def print_singletons(uppers, lowers, uppersname, lowersname): | ||
| print(" static constexpr singleton {}[] = {{".format(uppersname)) | ||
| for u, c in uppers: | ||
| print(" {{{:#04x}, {}}},".format(u, c)) | ||
| print(" };") | ||
| print(" static constexpr unsigned char {}[] = {{".format(lowersname)) | ||
| for i in range(0, len(lowers), 8): | ||
| print(" {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8]))) | ||
| print(" };") | ||
|
|
||
| def print_normal(normal, normalname): | ||
| print(" static constexpr unsigned char {}[] = {{".format(normalname)) | ||
| for v in normal: | ||
| print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) | ||
| print(" };") | ||
|
|
||
| def main(): | ||
| file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") | ||
|
|
||
| codepoints = get_codepoints(file) | ||
|
|
||
| CUTOFF=0x10000 | ||
| singletons0 = [] | ||
| singletons1 = [] | ||
| normal0 = [] | ||
| normal1 = [] | ||
| extra = [] | ||
|
|
||
| for a, b in to_ranges(get_escaped(codepoints)): | ||
| if a > 2 * CUTOFF: | ||
| extra.append((a, b - a)) | ||
| elif a == b - 1: | ||
| if a & CUTOFF: | ||
| singletons1.append(a & ~CUTOFF) | ||
| else: | ||
| singletons0.append(a) | ||
| elif a == b - 2: | ||
| if a & CUTOFF: | ||
| singletons1.append(a & ~CUTOFF) | ||
| singletons1.append((a + 1) & ~CUTOFF) | ||
| else: | ||
| singletons0.append(a) | ||
| singletons0.append(a + 1) | ||
| else: | ||
| if a >= 2 * CUTOFF: | ||
| extra.append((a, b - a)) | ||
| elif a & CUTOFF: | ||
| normal1.append((a & ~CUTOFF, b - a)) | ||
| else: | ||
| normal0.append((a, b - a)) | ||
|
|
||
| singletons0u, singletons0l = compress_singletons(singletons0) | ||
| singletons1u, singletons1l = compress_singletons(singletons1) | ||
| normal0 = compress_normal(normal0) | ||
| normal1 = compress_normal(normal1) | ||
|
|
||
| print("""\ | ||
| inline auto is_printable(uint32_t cp) -> bool {\ | ||
| """) | ||
| print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') | ||
| print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') | ||
| print_normal(normal0, 'normal0') | ||
| print_normal(normal1, 'normal1') | ||
| print("""\ | ||
| auto lower = static_cast<uint16_t>(cp); | ||
| if (cp < 0x10000) { | ||
| return is_printable(lower, singletons0, | ||
| sizeof(singletons0) / sizeof(*singletons0), | ||
| singletons0_lower, normal0, sizeof(normal0)); | ||
| } | ||
| if (cp < 0x20000) { | ||
| return is_printable(lower, singletons1, | ||
| sizeof(singletons1) / sizeof(*singletons1), | ||
| singletons1_lower, normal1, sizeof(normal1)); | ||
| }\ | ||
| """) | ||
| for a, b in extra: | ||
| print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b)) | ||
| print("""\ | ||
| return cp < 0x{:x}; | ||
| }}\ | ||
| """.format(NUM_CODEPOINTS)) | ||
|
|
||
| if __name__ == '__main__': | ||
| main() |