Skip to content

Instantly share code, notes, and snippets.

@alebastr
Created August 21, 2020 01:39
Show Gist options
  • Save alebastr/a98f7d50a096052e4fbaa90072ba939a to your computer and use it in GitHub Desktop.
Save alebastr/a98f7d50a096052e4fbaa90072ba939a to your computer and use it in GitHub Desktop.
/*
* CXXFLAGS += -std=c++17
* CXXFLAGS += $(shell pkg-config --cflags catch2 fmt glibmm-2.4)
* LDFLAGS += $(shell pkg-config --libs catch2 fmt glibmm-2.4)
*/
#define CATCH_CONFIG_MAIN
#define FMT_USE_INTERNAL
#include <catch2/catch.hpp>
#include <fmt/format.h>
#include <glibmm/ustring.h>
/*
* 1. Just print std::string.
*
* Broken because initializing char* with UTF-8 data does not make it
* contain UTF-8 data.
*/
TEST_CASE("std::string formatter") {
std::string str = "魔法少女まどか★マギカ";
auto result = fmt::format("{:.4}", str);
REQUIRE(result == "魔法少女");
}
/*
* 2. Use UTF-8 string literals from The Standard.
*
* Broken before C++20 because why would it even work? UTF-8 string literal is
* char[N] under the hood which is not an UTF-8 string...
*
* Broken in C++20 because `u8` suddenly became `char8_t[N]` and requires
* reinterpret_cast for every real world interaction.
*/
TEST_CASE("u8 formatter") {
REQUIRE(fmt::format(u8"{:.4}", u8"魔法少女まどか★マギカ") == u8"魔法少女");
}
/*
* 3. Define custom formatter for Glib::ustring and use unicode-aware string
* APIs from glibmm.
*
* Works by copying tons of internals from fmt. Might work after reimplementing
* everything but requres even more internal knowledge.
*/
template <> struct fmt::formatter<Glib::ustring> {
/*
* specs_ in formatter<std::string> is private, thus we have to reimplement
* the parser instead of simply inheriting from the right base
*
* copy some fmt internal code instead of reinventing the wheel.
*/
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
using handler_type = fmt::internal::dynamic_specs_handler<ParseContext>;
fmt::internal::specs_checker<handler_type> handler(
handler_type(specs_, ctx), fmt::internal::type::string_type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
fmt::internal::check_string_type_spec(specs_.type, ctx.error_handler());
return it;
}
template <typename FormatContext>
auto format(const Glib::ustring &value, FormatContext &ctx) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
auto b = value;
if (specs_.precision >= 0 && (unsigned)specs_.precision < b.length()) {
if (specs_.precision == 0) {
specs_.precision++;
}
b = b.substr(0, specs_.precision);
auto diff = b.bytes() - specs_.precision;
if (specs_.width > 0) {
specs_.width += diff;
}
}
#if FMT_VERSION >= 70000
return fmt::detail::write_bytes(ctx.out(), b.data(), specs_);
#else
using range_type =
fmt::internal::output_range<typename FormatContext::iterator, char>;
fmt::internal::basic_writer<range_type> writer(range_type(ctx.out()));
writer.write_bytes(b.data(), specs_);
return writer.out();
#endif
}
private:
fmt::internal::dynamic_format_specs<char> specs_;
};
TEST_CASE("Glib::ustring formatter") {
Glib::ustring str = "魔法少女まどか★マギカ";
REQUIRE(fmt::format("{:.4}", str) == "魔法少女");
REQUIRE(fmt::format("{:6.4}", str) == "魔法少女 ");
REQUIRE(fmt::format("{:<6.4}", str) == "魔法少女 ");
REQUIRE(fmt::format("{:^6.4}", str) == " 魔法少女 ");
REQUIRE(fmt::format("{:>6.4}", str) == " 魔法少女");
REQUIRE(fmt::format("{:*<6.4}", str) == "魔法少女**");
REQUIRE(fmt::format("{:*^6.4}", str) == "*魔法少女*");
REQUIRE(fmt::format("{:*>6.4}", str) == "**魔法少女");
}
/*
* 4. Use magic "C++20 will fix everything" char8_t type.
*
* Since `char8_t` (`enum unsigned char {}`) is a real UTF-8 character type,
* it's not compatible or interoperable with anything in the STL by design and
* not convertible to `char`.
*
* P0482R6
* P1423R3
*/
std::basic_string_view<fmt::internal::char8_type>
operator""_u8(const char *data, size_t size) {
return {(const fmt::internal::char8_type *)data, size};
}
TEST_CASE("fmt::char8_t formatter") {
auto str = "魔法少女まどか★マギカ"_u8;
REQUIRE(fmt::format("{:.4}"_u8, str) == "魔法少女"_u8);
REQUIRE(fmt::format("{:6.4}"_u8, str) == "魔法少女 "_u8);
REQUIRE(fmt::format("{:<6.4}"_u8, str) == "魔法少女 "_u8);
REQUIRE(fmt::format("{:^6.4}"_u8, str) == " 魔法少女 "_u8);
REQUIRE(fmt::format("{:>6.4}"_u8, str) == " 魔法少女"_u8);
REQUIRE(fmt::format("{:*<6.4}"_u8, str) == "魔法少女**"_u8);
REQUIRE(fmt::format("{:*^6.4}"_u8, str) == "*魔法少女*"_u8);
REQUIRE(fmt::format("{:*>6.4}"_u8, str) == "**魔法少女"_u8);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment