Skip to content

Commit

Permalink
Support compile-time strings and compile-time format string compilati…
Browse files Browse the repository at this point in the history
…on in module

Make just the necessary parts available for lookup from client context.
Shorter linker symbols are a nice sideeffect.
  • Loading branch information
DanielaE committed Jun 11, 2021
1 parent f6b5cc9 commit 59179de
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 36 deletions.
4 changes: 2 additions & 2 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
#endif

#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N, fixed_string<Char, N> Str>
template <typename Char, size_t N, fmt::ct::str<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
constexpr operator basic_string_view<char_type>() const {
Expand Down Expand Up @@ -622,7 +622,7 @@ void print(const S& format_str, const Args&... args) {

#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals {
template <detail::fixed_string Str>
template <ct::str Str>
constexpr detail::udl_compiled_string<
remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
Expand Down
63 changes: 33 additions & 30 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,33 @@ FMT_INLINE auto make_args_checked(const S& fmt,
return {args...};
}

// compile-time support
namespace ct {
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N> struct str {
using char_type = Char;
constexpr str(const Char (&s)[N]) {
detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(s),
s + N, data);
}
Char data[N]{};
};
#endif

// Converts a compile-time string to basic_string_view.
template <typename Char, size_t N>
constexpr auto string_to_view(const Char (&s)[N]) -> basic_string_view<Char> {
// Remove trailing NUL character if needed. Won't be present if this is used
// with a raw character array (i.e. not defined as a string).
return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
}
template <typename Char>
constexpr auto string_to_view(detail::std_string_view<Char> s)
-> basic_string_view<Char> {
return {s.data(), s.size()};
}
} // namespace ct

FMT_BEGIN_DETAIL_NAMESPACE

inline void throw_format_error(const char* message) {
Expand Down Expand Up @@ -2105,20 +2132,6 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value,
}
}

// Converts a compile-time string to basic_string_view.
template <typename Char, size_t N>
constexpr auto compile_string_to_view(const Char (&s)[N])
-> basic_string_view<Char> {
// Remove trailing NUL character if needed. Won't be present if this is used
// with a raw character array (i.e. not defined as a string).
return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
}
template <typename Char>
constexpr auto compile_string_to_view(std_string_view<Char> s)
-> basic_string_view<Char> {
return {s.data(), s.size()};
}

#define FMT_STRING_IMPL(s, base, explicit) \
[] { \
/* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
Expand All @@ -2127,7 +2140,7 @@ constexpr auto compile_string_to_view(std_string_view<Char> s)
using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
operator fmt::basic_string_view<char_type>() const { \
return fmt::detail::compile_string_to_view<char_type>(s); \
return fmt::ct::string_to_view<char_type>(s); \
} \
}; \
return FMT_COMPILE_STRING(); \
Expand All @@ -2145,16 +2158,6 @@ constexpr auto compile_string_to_view(std_string_view<Char> s)
*/
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, )

#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N> struct fixed_string {
constexpr fixed_string(const Char (&str)[N]) {
copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str), str + N,
data);
}
Char data[N]{};
};
#endif

#if FMT_USE_USER_DEFINED_LITERALS
template <typename Char> struct udl_formatter {
basic_string_view<Char> str;
Expand All @@ -2166,22 +2169,22 @@ template <typename Char> struct udl_formatter {
};

# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename T, typename Char, size_t N, fixed_string<Char, N> Str>
template <typename T, typename Char, size_t N, fmt::ct::str<Char, N> Str>
struct statically_named_arg : view {
static constexpr auto name = Str.data;

const T& value;
statically_named_arg(const T& v) : value(v) {}
};

template <typename T, typename Char, size_t N, fixed_string<Char, N> Str>
template <typename T, typename Char, size_t N, fmt::ct::str<Char, N> Str>
struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};

template <typename T, typename Char, size_t N, fixed_string<Char, N> Str>
template <typename T, typename Char, size_t N, fmt::ct::str<Char, N> Str>
struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
: std::true_type {};

template <typename Char, size_t N, fixed_string<Char, N> Str> struct udl_arg {
template <typename Char, size_t N, fmt::ct::str<Char, N> Str> struct udl_arg {
template <typename T> auto operator=(T&& value) const {
return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
}
Expand Down Expand Up @@ -2728,7 +2731,7 @@ inline namespace literals {
\endrst
*/
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <detail::fixed_string Str>
template <ct::str Str>
constexpr auto operator""_a()
-> detail::udl_arg<remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> {
Expand Down
8 changes: 4 additions & 4 deletions src/fmt.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
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)
Expand Down Expand Up @@ -76,10 +80,6 @@ export module fmt;
} \
export {

#if defined(_MSC_FULL_VER) && _MSC_FULL_VER > 192930036
#define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
#endif

// all library-provided declarations and definitions
// must be in the module purview to be exported
#include "fmt/args.h"
Expand Down
8 changes: 8 additions & 0 deletions test/module-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,11 @@ TEST(module_test, has_formatter) {
TEST(module_test, is_formattable) {
EXPECT_FALSE(fmt::is_formattable<disabled_formatter>::value);
}

TEST(module_test, compile_format_string) {
using namespace fmt::literals;
EXPECT_EQ("42", fmt::format("{0:x}"_cf, 0x42));
EXPECT_EQ(L"42", fmt::format(L"{:}"_cf, 42));
EXPECT_EQ("4.2", fmt::format("{arg:3.1f}"_cf, "arg"_a = 4.2));
EXPECT_EQ(L" 42", fmt::format(L"{arg:>3}"_cf, L"arg"_a = L"42"));
}

0 comments on commit 59179de

Please sign in to comment.