MCRO
C++23 utilities for Unreal Engine.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
FmtMacros.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12/**
13 * @file
14 * @brief
15 * Use leading `FMT_` or trailing `_FMT` fake text literals to create modern formatted strings with a better API.
16 *
17 * The major difference from `PRINTF_` or `FString::Printf(...)` is that `FMT` macros can take user defined string
18 * conversions into account, so more types can be used directly as arguments.
19 */
20
21#pragma once
22
23#include <string>
24
25#include "CoreMinimal.h"
26#include "Mcro/Text.h"
27#include "Mcro/TextMacros.h"
28#include "Mcro/Enums.h"
29#include "Mcro/Macros.h"
30
31template <size_t N>
32FString operator % (FStringFormatOrderedArguments&& args, const TCHAR(& format)[N])
33{
34 FString result = FString::Format(format, args);
35 result.TrimToNullTerminator();
36 return result;
37}
38
39template <size_t N>
40FString operator % (const TCHAR(& format)[N], FStringFormatOrderedArguments&& args)
41{
42 FString result = FString::Format(format, args);
43 result.TrimToNullTerminator();
44 return result;
45}
46
47template <size_t N>
48FString operator % (FStringFormatNamedArguments&& args, const TCHAR(& format)[N])
49{
50 FString result = FString::Format(format, args);
51 result.TrimToNullTerminator();
52 return result;
53}
54
55template <size_t N>
56FString operator % (const TCHAR(& format)[N], FStringFormatNamedArguments&& args)
57{
58 FString result = FString::Format(format, args);
59 result.TrimToNullTerminator();
60 return result;
61}
62
63#define MCRO_FMT_NAMED_ARG_TRANSFORM(s, data, elem) BOOST_PP_EXPAND(MCRO_FMT_NAMED_ARG elem)
64#define MCRO_FMT_NAMED_ARG(key, value) MakeTuple(FString(TEXT(#key)), value)
65
66#define MCRO_FMT_NAMED(seq) \
67 BOOST_PP_IIF(BOOST_PP_IS_BEGIN_PARENS(seq), \
68 MCRO_FMT_NAMED_0, \
69 BOOST_PP_IDENTITY_N(, 1) \
70 )(seq) //
71
72#define MCRO_FMT_NAMED_0(seq) \
73 Mcro::Text::NamedArguments( \
74 BOOST_PP_SEQ_ENUM( \
75 BOOST_PP_SEQ_TRANSFORM( \
76 MCRO_FMT_NAMED_ARG_TRANSFORM, , \
77 BOOST_PP_VARIADIC_SEQ_TO_SEQ(seq) \
78 ) \
79 ) \
80 ) //
81
82#define MCRO_FMT_ORDERED(...) Mcro::Text::OrderedArguments(__VA_ARGS__)
83
84#define MCRO_FMT_ARGS(...) \
85 BOOST_PP_IIF(BOOST_PP_IS_BEGIN_PARENS(__VA_ARGS__), \
86 MCRO_FMT_NAMED(BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__)), \
87 MCRO_FMT_ORDERED(__VA_ARGS__) \
88 ) //
89
90/**
91 * @brief
92 * Leading fake text literal which makes using `FString::Format(...);` much more comfortable.
93 *
94 * `FMT` macros allow more types to be used directly in the format arguments expression because `Mcro::Text` has
95 * a couple of conversion utilities. If the first argument of `FMT_` is a sequence of `("key", value)` pairs enclosed
96 * in parentheses, then named format arguments are assumed. Ordered format arguments are assumed otherwise. The two
97 * modes cannot be mixed.
98 *
99 * Usage:
100 * @code
101 * EPixelFormat format = PF_Unknown; int32 num = 42;
102 *
103 * FString ordered = FMT_(format, num) "Hi {0}, your number is {1}";
104 * // -> "Hi PF_Unknown, your number is 42"
105 *
106 * // | Notice the lack of comma here
107 * // V
108 * FString named = FMT_((Type, format) (Count, num)) "Hi {Type}, your number is {Count}";
109 * // -> "Hi PF_Unknown, your number is 42"
110 * @endcode
111 *
112 * The following argument types are supported out-of-box:
113 * - Originally supported by `FStringFormatArg` and what's implicitly convertable to
114 * - `FString, FStringView, int32, uint32, int64, uint64, float, double`
115 * - `ANSICHAR, WIDECHAR, UCS2CHAR, UTF8CHAR` pointer strings
116 * - Anything which has a `ToString()` member method which produces one of the above type
117 * - Including but not exclusively `FText` and `FName` for example
118 * - STL strings and views of any encoding
119 * - Enums where their entries are serialized as human-readable names
120 *
121 * @remarks
122 * To add more supported types specialize the `TAsFormatArgument` template functor for your preferred type and return
123 * a value which is implicitly convertible to `FStringFormatArg` in the `Mcro::Text` namespace. For example check
124 * `Enums.h` to see how that's done with enums. For your own types you can also implement a `ToString()` member
125 * method to get automatic support.
126 *
127 * @warning
128 * The **named arguments** overload (`FMT_((A, a) (B, b) (C, c))`) must not have comma `,` between the individual
129 * pairs. This is because named argument pairs are passed into this macro as a "sequence" instead of variadic arguments.
130 * Ordered format arguments however should be passed in as regular variadic arguments separated by comma `,` as
131 * nature intended.
132 *
133 * @note
134 * `FMT` macros are the only things in MCRO where excessive preprocessing is used
135 */
136#define FMT_(...) MCRO_FMT_ARGS(__VA_ARGS__) % TEXT_
137
138/**
139 * @brief
140 * Trailing fake text literal which makes using `FString::Format(...);` much more comfortable.
141 *
142 * `FMT` macros allow more types to be used directly in the format arguments expression because `Mcro::Text` has
143 * a couple of conversion utilities. If the first argument of `_FMT` is a sequence of `("key", value)` pairs enclosed
144 * in parentheses, then named format arguments are assumed. Ordered format arguments are assumed otherwise. The two
145 * modes cannot be mixed.
146 *
147 * Usage:
148 * @code
149 * EPixelFormat format = PF_Unknown; int32 num = 42;
150 *
151 * FString ordered = TEXT_"Hi {0}, your number is {1}" _FMT(format, num);
152 * // -> "Hi PF_Unknown, your number is 42" ^ this space is important
153 *
154 * // Named arguments look better with multiple lines on this version
155 * FString named = TEXT_"Hi {Type}, your number is {Count}" _FMT(
156 * (Type, format) // <- Notice the lack of comma here ^ this space is important
157 * (Count, num)
158 * );
159 * // -> "Hi PF_Unknown, your number is 42"
160 * @endcode
161 *
162 * The following argument types are supported out-of-box:
163 * - Originally supported by `FStringFormatArg` and what's implicitly convertable to
164 * - `FString, FStringView, int32, uint32, int64, uint64, float, double`
165 * - `ANSICHAR, WIDECHAR, UCS2CHAR, UTF8CHAR` pointer strings
166 * - Anything which has a `ToString()` member method which produces one of the above type
167 * - Including but not exclusively `FText` and `FName` for example
168 * - STL strings and views of any encoding
169 * - Enums where their entries are serialized as human-readable names
170 *
171 * @remarks
172 * To add more supported types specialize the `TAsFormatArgument` template functor for your preferred type and return
173 * a value which is implicitly convertible to `FStringFormatArg` in the `Mcro::Text` namespace. For example check
174 * `Enums.h` to see how that's done with enums. For your own types you can also implement a `ToString()` member
175 * method to get automatic support.
176 *
177 * @warning
178 * The **named arguments** overload (`_FMT((A, a) (B, b) (C, c))`) must not have comma `,` between the individual
179 * pairs. This is because named argument pairs are passed into this macro as a "sequence" instead of variadic arguments.
180 * Ordered format arguments however should be passed in as regular variadic arguments separated by comma `,` as
181 * nature intended.
182 *
183 * @note
184 * `FMT` macros are the only things in MCRO where excessive preprocessing is used
185 */
186#define _FMT(...) % MCRO_FMT_ARGS(__VA_ARGS__)
FString operator%(FStringFormatOrderedArguments &&args, const TCHAR(&format)[N])
Definition FmtMacros.h:32
Use leading TEXT_ without parenthesis for Unreal compatible text literals.