BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bsla_nullterminated

Detailed Description

Provide macros for use with NULL-terminated variadic functions.

Outline

Purpose

Provide macros for use with NULL-terminated variadic functions.

Macros

See also
bsla_annotations

Description

This 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.

Macro Reference

Usage

This section illustrates intended use of this component.

Example 1: catStrings Function

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:

/// The specified `outputBuffer` is a buffer where the output of this
/// function is placed. The specified `...` is a `NULL`-terminated list
/// of `const char *` strings, which are to be copied into
/// `outputBuffer`, concatenated together and separated by spaces. The
/// behavior is undefined unless the `...` is a `NULL`-terminated list
/// of `const char *` arguments.
void catStrings(char *outputBuffer, ...) BSLA_NULLTERMINATED;
void catStrings(char *outputBuffer, ...)
{
*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);
}
va_end(ap);
}
#define BSLA_NULLTERMINATED
Definition bsla_nullterminated.h:276

Then, in main, we call catStrings correctly:

char buf[1000];
catStrings(buf, "Now", "you", "see", "it.", NULL);
printf("%s\n", buf);

which compiles without a warning and produces the output:

Now you see it.

Now, we call catStrings again and forget to add the terminating ‘NULL’:

catStrings(buf, "Now", "you", "don't.");
printf("%s\n", buf);

Finally, we get the compiler warning:

.../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, ...)
^

Example 2: catVerdict Function

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, 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 BSLA_NULL_TERMINATEDAT(1):

/// 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`.
void catVerdict(char *outputBuffer, ...) BSLA_NULLTERMINATEDAT(1);
void catVerdict(char *outputBuffer, ...)
{
*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);
}
#define BSLA_NULLTERMINATEDAT(ARG_IDX)
Definition bsla_nullterminated.h:277

Then, in main, we call catVerdict correctly:

char buf[1000];
catVerdict(buf, "We find the", "defendant,", "Bugs Bunny", NULL, 0);
printf("%s\n", buf);

which compiles without a warning and produces the output:

We find the defendant, Bugs Bunny: not guilty

Next, we call catVerdict with no NULL passed, and get a warning (and probably a core dump if we ran it):

catVerdict(buf, "We find the", "defendant,", "Wile E. Coyote", 1);
printf("%s\n", buf);

And we get the following compiler warning:

.../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, ...)
^

Now, we call catVerdict and forget to put the integer that indicates guilt or innocence after the NULL. This means that NULL is happening at index 0, not index 1, which violates the requirement imposed by the annotation:

catVerdict(buf, "We find the", "defendant,", "Road Runner", NULL);
printf("%s\n", buf);

Finally, we get the compiler warning:

.../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, ...)
^

Macro Definition Documentation

◆ BSLA_NULLTERMINATED

#define BSLA_NULLTERMINATED

◆ BSLA_NULLTERMINATEDAT

#define BSLA_NULLTERMINATEDAT (   ARG_IDX)