Skip to content

Commit e7e7941

Browse files
committed
Fix error that exceed Clang's default -fconstexpr-steps value
1 parent 2a94d70 commit e7e7941

File tree

2 files changed

+179
-167
lines changed

2 files changed

+179
-167
lines changed

runtime/NanoLogCpp17.h

Lines changed: 87 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
#include <cstdint>
1919
#include <cstring>
20+
#include <cstddef>
2021

2122
#include <algorithm>
2223
#include <iostream>
2324
#include <utility>
25+
#include <array>
2426

2527
#include "Common.h"
2628
#include "Cycles.h"
@@ -43,6 +45,21 @@
4345
*/
4446
namespace NanoLogInternal {
4547

48+
/**
49+
* A struct that contains a list of ParamType enum alongside with its actual
50+
* size. Mainly used as return type of getParamInfos to store the ParamType
51+
* extracted from format string.
52+
*
53+
* \tparam N
54+
* The maximum number of elements avaliable in this struct.
55+
*/
56+
57+
template <std::size_t N>
58+
struct ParamTypeContainer{
59+
std::array<ParamType, N> data;
60+
std::size_t size;
61+
};
62+
4663
/**
4764
* Checks whether a character is with the terminal set of format specifier
4865
* characters according to the printf specification:
@@ -116,26 +133,29 @@ isDigit(char c) {
116133

117134
/**
118135
* Analyzes a static printf style format string and extracts type information
119-
* about the p-th parameter that would be used in a corresponding NANO_LOG()
136+
* about all of the parameters that would be used in a corresponding NANO_LOG()
120137
* invocation.
121138
*
122139
* \tparam N
123140
* Length of the static format string (automatically deduced)
124141
* \param fmt
125142
* Format string to parse
126-
* \param paramNum
127-
* p-th parameter to return type information for (starts from zero)
128143
* \return
129-
* Returns an ParamType enum describing the type of the parameter
144+
* Returns a ParamTypeContainer<N> struct that contains all ParamType info
145+
* in order. Note: The maximum avaliable size template parameter N is set
146+
* with the fact that, the number of parameters in the format string shall
147+
* be less than the length of that string. And exceeding the maximum size
148+
* limit in core constant expression anyways will cause the program to be
149+
* ill-formed.
130150
*/
131-
template<int N>
132-
constexpr inline ParamType
133-
getParamInfo(const char (&fmt)[N],
134-
int paramNum=0)
151+
template <std::size_t N>
152+
constexpr inline ParamTypeContainer<N>
153+
getParamInfos(const char (&fmt)[N])
135154
{
136-
int pos = 0;
137-
while (pos < N - 1) {
138-
155+
ParamTypeContainer<N> paramTypes = {};
156+
std::size_t pos = 0;
157+
std::size_t paramIdx = 0;
158+
while (pos + 1 < N) {
139159
// The code below searches for something that looks like a printf
140160
// specifier (i.e. something that follows the format of
141161
// %<flags><width>.<precision><length><terminal>). We only care
@@ -163,10 +183,9 @@ getParamInfo(const char (&fmt)[N],
163183

164184
// Consume width
165185
if (fmt[pos] == '*') {
166-
if (paramNum == 0)
167-
return ParamType::DYNAMIC_WIDTH;
186+
paramTypes.data[paramIdx] = ParamType::DYNAMIC_WIDTH;
168187

169-
--paramNum;
188+
++paramIdx;
170189
++pos;
171190
} else {
172191
while (NanoLogInternal::isDigit(fmt[pos]))
@@ -180,11 +199,11 @@ getParamInfo(const char (&fmt)[N],
180199
++pos; // consume '.'
181200

182201
if (fmt[pos] == '*') {
183-
if (paramNum == 0)
184-
return ParamType::DYNAMIC_PRECISION;
202+
paramTypes.data[paramIdx] =
203+
ParamType::DYNAMIC_PRECISION;
185204

186205
hasDynamicPrecision = true;
187-
--paramNum;
206+
++paramIdx;
188207
++pos;
189208
} else {
190209
precision = 0;
@@ -198,7 +217,7 @@ getParamInfo(const char (&fmt)[N],
198217
// consume length
199218
while (isLength(fmt[pos]))
200219
++pos;
201-
220+
202221
// Consume terminal
203222
if (!NanoLogInternal::isTerminal(fmt[pos])) {
204223
throw std::invalid_argument(
@@ -212,128 +231,90 @@ getParamInfo(const char (&fmt)[N],
212231
"%n specifiers are not support in NanoLog!");
213232
}
214233

215-
if (paramNum != 0) {
216-
--paramNum;
217-
++pos;
218-
continue;
234+
if (fmt[pos] != 's'){
235+
paramTypes.data[paramIdx] = ParamType::NON_STRING;
236+
++paramIdx;
237+
} else if (hasDynamicPrecision) {
238+
paramTypes.data[paramIdx] =
239+
ParamType::STRING_WITH_DYNAMIC_PRECISION;
240+
++paramIdx;
241+
} else if (precision == -1) {
242+
paramTypes.data[paramIdx] =
243+
ParamType::STRING_WITH_NO_PRECISION;
244+
++paramIdx;
219245
} else {
220-
if (fmt[pos] != 's')
221-
return ParamType::NON_STRING;
222-
223-
if (hasDynamicPrecision)
224-
return ParamType::STRING_WITH_DYNAMIC_PRECISION;
225-
226-
if (precision == -1)
227-
return ParamType::STRING_WITH_NO_PRECISION;
228-
else
229-
return ParamType(precision);
246+
paramTypes.data[paramIdx] = ParamType(precision);
247+
++paramIdx;
230248
}
231249
}
232250
}
233251
}
234252

235-
return ParamType::INVALID;
236-
}
237-
238-
239-
/**
240-
* Helper to analyzeFormatString. This level of indirection is needed to
241-
* unpack the index_sequence generated in analyzeFormatString and
242-
* use the sequence as indices for calling getParamInfo.
243-
*
244-
* \tparam N
245-
* Length of the format string (automatically deduced)
246-
* \tparam Indices
247-
* An index sequence from [0, N) where N is the number of parameters in
248-
* the format string (automatically deduced)
249-
*
250-
* \param fmt
251-
* printf format string to analyze
252-
*
253-
* \return
254-
* An std::array describing the types at each index (zero based).
255-
*/
256-
template<int N, std::size_t... Indices>
257-
constexpr std::array<ParamType, sizeof...(Indices)>
258-
analyzeFormatStringHelper(const char (&fmt)[N], std::index_sequence<Indices...>)
259-
{
260-
return {{ getParamInfo(fmt, Indices)... }};
253+
paramTypes.size = paramIdx;
254+
return paramTypes;
261255
}
262256

263257

264258
/**
265259
* Computes a ParamType array describing the parameters that would be used
266-
* with the provided printf style format string. The indices of the array
267-
* correspond with the parameter position in the variable args portion of
268-
* the invocation.
260+
* with the provided ParamTypeContainer analyzed from the printf style format
261+
* string. The indices of the array correspond with the parameter position in
262+
* the variable args portion of the invocation.
269263
*
270264
* \template NParams
271265
* The number of additional format parameters that follow the format
272266
* string in a printf-like function. For example printf("%*.*d", 9, 8, 7)
273267
* would have NParams = 3
274268
* \template N
275-
* length of the printf style format string (automatically deduced)
276-
*
277-
* \param fmt
278-
* Format string to generate the array for
269+
* template parameter N of the ParamTypeContainer parameter (automatically
270+
* deduced)
271+
* \param paramTypes
272+
* ParamTypeContainer object to generate the array for
279273
*
280274
* \return
281275
* An std::array where the n-th index indicates that the
282276
* n-th format parameter is a "%s" or not.
283277
*/
284-
template<int NParams, size_t N>
278+
template<int NParams, std::size_t N>
285279
constexpr std::array<ParamType, NParams>
286-
analyzeFormatString(const char (&fmt)[N])
280+
analyzeParamTypeContainer(const ParamTypeContainer<N> &paramTypes)
287281
{
288-
return analyzeFormatStringHelper(fmt, std::make_index_sequence<NParams>{});
289-
}
290-
291-
/**
292-
* Counts the number of parameters that need to be passed in for a particular
293-
* printf style format string.
294-
*
295-
* One subtle point is that we are counting parameters, not specifiers, so a
296-
* specifier of "%*.*d" will actually count as 3 since the two '*" will result
297-
* in a parameter being passed in each.
298-
*
299-
* \tparam N
300-
* length of the printf style format string (automatically deduced)
301-
*
302-
* \param fmt
303-
* printf style format string to analyze
304-
*
305-
* @return
306-
*/
307-
template<int N>
308-
constexpr inline int
309-
countFmtParams(const char (&fmt)[N])
310-
{
311-
int count = 0;
312-
while (getParamInfo(fmt, count) != ParamType::INVALID)
313-
++count;
314-
return count;
282+
std::array<ParamType, NParams> ret = {};
283+
int fillSize = NParams;
284+
if (NParams > static_cast<int>(paramTypes.size))
285+
fillSize = static_cast<int>(paramTypes.size);
286+
for (int i = 0; i < fillSize; ++i) {
287+
ret[i] = paramTypes.data[i];
288+
}
289+
if (NParams > static_cast<int>(paramTypes.size)) {
290+
for (int i = paramTypes.size; i < NParams; ++i) {
291+
ret[i] = ParamType::INVALID;
292+
}
293+
}
294+
return ret;
315295
}
316296

317297
/**
318298
* Counts the number of nibbles that would be needed to represent all
319299
* the non-string and dynamic width/precision specifiers for a given
320-
* printf style format string in the NanoLog system.
300+
* ParamTypeContainer in the NanoLog system.
321301
*
322302
* \tparam N
323-
* length of the printf style format string (automatically deduced)
324-
* \param fmt
325-
* printf style format string to analyze
303+
* template parameter N of the ParamTypeContainer parameter (automatically
304+
* deduced)
305+
* \param paramTypes
306+
* ParamTypeContainer object to analyze
326307
*
327308
* \return
328-
* Number of non-string specifiers in the format string
309+
* Number of non-string specifiers in the paramTypes object.
329310
*/
330311
template<size_t N>
331312
constexpr int
332-
getNumNibblesNeeded(const char (&fmt)[N])
313+
getNumNibblesNeeded(const ParamTypeContainer<N> &paramTypes)
333314
{
334315
int numNibbles = 0;
335-
for (int i = 0; i < countFmtParams(fmt); ++i) {
336-
ParamType t = getParamInfo(fmt, i);
316+
for (int i = 0; i < paramTypes.size; ++i) {
317+
ParamType t = paramTypes.data[i];
337318
if (t == NON_STRING || t == DYNAMIC_PRECISION || t == DYNAMIC_WIDTH)
338319
++numNibbles;
339320
}
@@ -1071,8 +1052,9 @@ checkFormat(NANOLOG_PRINTF_FORMAT const char *, ...) {}
10711052
* Log arguments associated with the printf-like string.
10721053
*/
10731054
#define NANO_LOG(severity, format, ...) do { \
1074-
constexpr int numNibbles = NanoLogInternal::getNumNibblesNeeded(format); \
1075-
constexpr int nParams = NanoLogInternal::countFmtParams(format); \
1055+
constexpr auto paramTypeInfo = NanoLogInternal::getParamInfos(format); \
1056+
constexpr int numNibbles = NanoLogInternal::getNumNibblesNeeded(paramTypeInfo); \
1057+
constexpr int nParams = paramTypeInfo.size; \
10761058
\
10771059
/*** Very Important*** These must be 'static' so that we can save pointers
10781060
* to these variables and have them persist beyond the invocation.
@@ -1081,7 +1063,7 @@ checkFormat(NANOLOG_PRINTF_FORMAT const char *, ...) {}
10811063
* used by the compression function, which is invoked in another thread
10821064
* at a much later time. */ \
10831065
static constexpr std::array<NanoLogInternal::ParamType, nParams> paramTypes = \
1084-
NanoLogInternal::analyzeFormatString<nParams>(format); \
1066+
NanoLogInternal::analyzeParamTypeContainer<nParams>(paramTypeInfo); \
10851067
static int logId = NanoLogInternal::UNASSIGNED_LOGID; \
10861068
\
10871069
if (NanoLog::severity > NanoLog::getLogLevel()) \

0 commit comments

Comments
 (0)