BDE 4.14.0 Production release
|
Macros | |
#define | BSLA_NULLTERMINATED |
#define | BSLA_NULLTERMINATEDAT(ARG_IDX) |
Provide macros for use with NULL
-terminated variadic functions.
NULL
ARG_IDX
is not NULL
BSLA_NULLTERMINATED
is activeBSLA_NULLTERMINATEDAT
activeThis component provides preprocessor macros to indicate that a variadic function's arguments are terminated by NULL
, or, in the case of BSLA_NULLTERMINATEDAT
, by NULL
at a certain index. Note that the terminating NULL
must actually be NULL
or, with C++11, nullptr
; passing 0 in its place will result in a warning.
BSLA_NULLTERMINATED
: This annotation on a variadic macro indicates that a warning should be issued unless the last argument to the function is explicitly NULL
.
BSLA_NULLTERMINATEDAT(ARG_IDX)
: This annotation on a variadic function indicates that a warning should be issued unless the argument at ARG_IDX
is NULL
, where ARG_IDX
is the number of arguments from the last, the last argument having ARG_IDX == 0
. Thus, BSLA_NULLTERMINATED
is equivalent to BSLA_NULLTERMINATEDAT(0)
.
BSLA_NULLTERMINATED_IS_ACTIVE
: The macro BSLA_NULLTERMINATED_IS_ACTIVE
is defined if BSLA_NULLTERMINATED
expands to something with the desired effect; otherwise BSLA_NULLTERMINATED_IS_ACTIVE
is not defined and BSLA_NULLTERMINATED
expands to nothing.
BSLA_NULLTERMINATEDAT_IS_ACTIVE
: The macro BSLA_NULLTERMINATEDAT_IS_ACTIVE
is defined if BSLA_NULLTERMINATEDAT
expands to something with the desired effect; otherwise BSLA_NULLTERMINATEDAT_IS_ACTIVE
is not defined and BSLA_NULLTERMINATEDAT
expands to nothing.
This section illustrates intended use of this component.
Suppose we want to have a function that, passed a variable length argument list of const char *
strings terminated by NULL
, concatenates the strings, separated by spaces, into a buffer.
First, we declare and define the function, annotated with BSLA_NULL_TERMINATED
:
Then, in main
, we call catStrings
correctly:
which compiles without a warning and produces the output:
Now, we call catStrings" again and forget to add the terminating</tt>NULL':
@code
catStrings(buf, "Now", "you", "don't.");
printf("s
", buf);
@endcode
Finally, we get the compiler warning:
@code
.../bsla_nullterminated.t.cpp:412:47: warning: missing sentinel in function
call [-Wsentinel]
catStrings(buf, "Now", "you", "don't.");
^
, nullptr
.../bsla_nullterminated.t.cpp:137:10: note: function has been explicitly
marked sentinel here
void catStrings(char *outputBuffer, ...)
^
@endcode
@subsubsection bsla_nullterminated-example-2-catverdict-function Example 2: catVerdict Function
Suppose we want to have a function that, passed a variable length argument
list of <tt>const char *</tt> strings terminated by <tt>NULL</tt>, concatenates the
strings, separated by spaces, into a buffer, and then there's an additional
integer argument, interpreted as a boolean, that determines what is to be
appended to the end of the buffer.
First, we declare and define the function, annotated with
<tt>BSLA_NULL_TERMINATEDAT(1)</tt>:
@code
void catVerdict(char *outputBuffer, ...) BSLA_NULLTERMINATEDAT(1);
void catVerdict(char *outputBuffer, ...)
// The specified 'outputBuffer' is a buffer where output is to be
// placed. All but the last 2 of the specified '...' arguments are
// 'const char *' strings to be concatenated together into
// 'outputBuffer', separated by spaces. The second-to-last argument is
// to be 'NULL', and the last argument is an 'int' interpreted as a
// boolean to determine whether the buffer is to end with a verdict of
// "guilty" or "not guilty". The behavior is undefined unless the
// types of all the arguments are correct and the second to last
// argument is 'NULL'.
{
*outputBuffer = 0;
va_list ap;
va_start(ap, outputBuffer);
const char *next;
for (bool first = 1; (next = va_arg(ap, const char *)); first = 0) {
::strcat(outputBuffer, first ? "" : " ");
::strcat(outputBuffer, next);
}
const bool guilty = va_arg(ap, int);
::strcat(outputBuffer, guilty ? ": guilty" : ": not guilty");
va_end(ap);
}
@endcode
Then, in <tt>main</tt>, we call <tt>catVerdict</tt> correctly:
@code
char buf[1000];
catVerdict(buf, "We find the", "defendant,", "Bugs Bunny", NULL, 0);
printf("s
", buf);
@endcode
which compiles without a warning and produces the output:
@code
We find the defendant, Bugs Bunny: not guilty
@endcode
Next, we call <tt>catVerdict</tt> with no <tt>NULL</tt> passed, and get a warning (and
probably a core dump if we ran it):
@code
catVerdict(buf, "We find the", "defendant,", "Wile E. Coyote", 1);
printf("s
", buf);
@endcode
And we get the following compiler warning:
@code
.../bsla_nullterminated.t.cpp:447:70: warning: missing sentinel in function
call [-Wsentinel]
catVerdict(buf, "We find the", "defendant,", "Wile E. Coyote", 1);
^
, nullptr
.../bsla_nullterminated.t.cpp:171:10: note: function has been explicitly
marked sentinel here
void catVerdict(char *outputBuffer, ...)
^
@endcode
Now, we call <tt>catVerdict</tt> and forget to put the integer that indicates guilt
or innocence after the <tt>NULL</tt>. This means that <tt>NULL</tt> is happening at index
0, not index 1, which violates the requirement imposed by the annotation:
@code
catVerdict(buf, "We find the", "defendant,", "Road Runner", NULL);
printf("s
", buf);
@endcode
Finally, we get the compiler warning:
@code
.../bsla_nullterminated.t.cpp:471:67: warning: missing sentinel in function
call [-Wsentinel]
catVerdict(buf, "We find the", "defendant,", "Road Runner", NULL); ^ , nullptr .../bsla_nullterminated.t.cpp:171:10: note: function has been explicitly marked sentinel here void catVerdict(char *outputBuffer, ...) ^
#define BSLA_NULLTERMINATED |
#define BSLA_NULLTERMINATEDAT | ( | ARG_IDX | ) |