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.
  • Loading branch information
DanielaE authored and vitaut committed Jun 11, 2021
1 parent 3423d75 commit 0193e7c
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 36 deletions.
5 changes: 3 additions & 2 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ 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::detail_exported::fixed_string<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 +623,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 <detail_exported::fixed_string Str>
constexpr detail::udl_compiled_string<
remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
Expand Down
68 changes: 38 additions & 30 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,33 @@ FMT_INLINE auto make_args_checked(const S& fmt,
return {args...};
}

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

// 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(detail::std_string_view<Char> s)
-> basic_string_view<Char> {
return {s.data(), s.size()};
}
} // namespace detail_exported

FMT_BEGIN_DETAIL_NAMESPACE

inline void throw_format_error(const char* message) {
Expand Down Expand Up @@ -2098,20 +2125,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 @@ -2120,7 +2133,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::detail_exported::compile_string_to_view<char_type>(s); \
} \
}; \
return FMT_COMPILE_STRING(); \
Expand All @@ -2138,16 +2151,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 @@ -2159,22 +2162,27 @@ 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::detail_exported::fixed_string<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::detail_exported::fixed_string<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::detail_exported::fixed_string<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::detail_exported::fixed_string<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 @@ -2721,7 +2729,7 @@ inline namespace literals {
\endrst
*/
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <detail::fixed_string Str>
template <detail_exported::fixed_string 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 0193e7c

Please sign in to comment.