Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace detail {
#endif

template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
constexpr auto first(const T& value, const Tail&...) -> const T& {
return value;
}

Expand Down Expand Up @@ -436,8 +436,8 @@ FMT_BEGIN_EXPORT
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING std::basic_string<Char> format(
const CompiledFormat& cf, const Args&... args) {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
Expand All @@ -452,8 +452,8 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,

template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING std::basic_string<typename S::char_type> format(
const S&, Args&&... args) {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
Expand Down
35 changes: 32 additions & 3 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,33 @@
# define FMT_NOINLINE
#endif

// Detect constexpr std::string.
#if !FMT_USE_CONSTEVAL
# define FMT_USE_CONSTEXPR_STRING 0
#elif defined(__cpp_lib_constexpr_string) && \
__cpp_lib_constexpr_string >= 201907L
# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE
// clang + libstdc++ are able to work only starting with gcc13.3
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294
# if FMT_GLIBCXX_RELEASE < 13
# define FMT_USE_CONSTEXPR_STRING 0
# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521
# define FMT_USE_CONSTEXPR_STRING 0
# else
# define FMT_USE_CONSTEXPR_STRING 1
# endif
# else
# define FMT_USE_CONSTEXPR_STRING 1
# endif
#else
# define FMT_USE_CONSTEXPR_STRING 0
#endif
#if FMT_USE_CONSTEXPR_STRING
# define FMT_CONSTEXPR_STRING constexpr
#else
# define FMT_CONSTEXPR_STRING
#endif

// GCC 4.9 doesn't support qualified names in specializations.
namespace std {
template <typename T> struct iterator_traits<fmt::basic_appender<T>> {
Expand Down Expand Up @@ -4207,21 +4234,23 @@ FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
* std::string answer = fmt::to_string(42);
*/
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
FMT_NODISCARD auto to_string(T value) -> std::string {
FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string {
// The buffer should be large enough to store the number including the sign
// or "false" for bool.
char buffer[max_of(detail::digits10<T>() + 2, 5)];
return {buffer, detail::write<char>(buffer, value)};
}

template <typename T, FMT_ENABLE_IF(detail::use_format_as<T>::value)>
FMT_NODISCARD auto to_string(const T& value) -> std::string {
FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value)
-> std::string {
return to_string(format_as(value));
}

template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
!detail::use_format_as<T>::value)>
FMT_NODISCARD auto to_string(const T& value) -> std::string {
FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value)
-> std::string {
auto buffer = memory_buffer();
detail::write<char>(appender(buffer), value);
return {buffer.data(), buffer.size()};
Expand Down
46 changes: 46 additions & 0 deletions test/compile-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -421,3 +421,49 @@ TEST(compile_time_formatting_test, multibyte_fill) {
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
}
#endif

#if FMT_USE_CONSTEXPR_STRING

TEST(compile_test, constexpr_format) {
{
constexpr auto result = []() {
return fmt::format(FMT_COMPILE("{}"), 1) == "1";
}();
EXPECT_TRUE(result);
}

{
constexpr auto result = []() {
return fmt::format(FMT_COMPILE("{:#b}"), 42) == "0b101010";
}();
EXPECT_TRUE(result);
}

{
constexpr auto result = []() {
return "**-42" == fmt::format(FMT_COMPILE("{:*>5}"), -42);
}();
EXPECT_TRUE(result);
}

{
constexpr auto result = []() {
return "10 " == fmt::format(FMT_COMPILE("{: ^3}"), 10);
}();
EXPECT_TRUE(result);
}

{
constexpr auto result = []() {
return "42 is 42" == fmt::format(FMT_COMPILE("{} is {}"), 42, 42);
}();
EXPECT_TRUE(result);
}

constexpr auto result = []() {
return "This is a very huge int: 1234567890" == fmt::format(FMT_COMPILE("This is a very huge int: {}"), 1234567890);
}();
EXPECT_TRUE(result);
}

#endif // FMT_USE_CONSTEXPR_STRING
Loading