Quick Links:

bal | bbl | bdl | bsl

Component bsla_nullterminated
[Package bsla]

Provide macros for use with NULL-terminated variadic functions. More...

Outline
Purpose:
Provide macros for use with NULL-terminated variadic functions.
Macros:
BSLA_NULLTERMINATED warn if last argument is not NULL
BSLA_NULLTERMINATEDAT(ARG_IDX) warn if argument at ARG_IDX is not NULL
BSLA_NULLTERMINATED_IS_ACTIVE 1 if BSLA_NULLTERMINATED is active
BSLA_NULLTERMINATEDAT_IS_ACTIVE 1 if BSLA_NULLTERMINATEDAT is active
See also:
Component 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:
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
BSLA_NULLTERMINATEDAT_IS_ACTIVE In these two cases, X_IS_ACTIVE is defined to 0 if X expands to nothing and 1 otherwise.
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:
  void catStrings(char *outputBuffer, ...) BSLA_NULLTERMINATED;
  void catStrings(char *outputBuffer, ...)
      // 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.
  {
      *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);
  }
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):
  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);
  }
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, ...)
       ^