Quick Links:

bal | bbl | bdl | bsl

Component bsla_printf
[Package bsla]

Provide a macro to indicate printf-style arguments. More...

Outline
Purpose:
Provide a macro to indicate printf-style arguments.
Macros:
BSLA_PRINTF(FMTIDX, STARTIDX) validate printf format and arguments
BSLA_PRINTF_IS_ACTIVE 1 if BSLA_PRINTF is active and 0 otherwise
See also:
Component bsla_annotations
Description:
This component provides a preprocessor macro that allows the designation of a given function argument as a printf-style format string, and arguments starting at a certain index in the argument list to be formatted according to that string.
Macro Reference:
BSLA_PRINTF(FMTIDX, STARTIDX) This annotation instructs the compiler to perform additional compile-time checks on so-annotated functions that take printf-style arguments, which should be type-checked against a format string. The FMTIDX parameter is the one-based index to the const char * format string. The STARTIDX parameter is the one-based index to the first variable argument to type-check against that format string. For example:
  extern int my_printf(void *obj, const char *format, ...) BSLA_PRINTF(2, 3);
BSLA_PRINTF_IS_ACTIVE The macro BSLA_PRINTF_IS_ACTIVE is defined to 0 for compilers where BSLA_PRINTF expands to nothing, and 1 otherwise.
Usage:
This section illustrates intended use of this component.
Example 1: printf-Like Function That Returns a bsl::string by Value:
First, we define a function, strPrintf, that takes a variable number of arguments. The second argument is the format string, and we annotate it with BSLA_PRINTF:
  std::string strPrintf(size_t *numChars, const char *format, ...)
                                                           BSLA_PRINTF(2, 3);
  std::string strPrintf(size_t *numChars, const char *format, ...)
      // Do a 'sprintf'-style write to a 'std::string' and return the string
      // by value.  Ensure that the write can't overflow unless memory or
      // address space is exhausted.  The specified '*numChars' is the number
      // of characters written, the specified 'format' is the 'printf'-style
      // format string, and the specified '...' is the variable-length list
      // of arguments to be formatted.
  {
      std::string ret = " ";

      va_list ap;
      va_start(ap, format);

      // 'vsnprintf' returns the number of characters that WOULD have been
      // written (not including the terminating '\0') had the buffer been
      // long enough.

      *numChars = ::vsnprintf(&ret[0], 1, format, ap);
      va_end(ap);

      ret.resize(*numChars + 1);

      va_start(ap, format);
      *numChars = ::vsnprintf(&ret[0], *numChars + 1, format, ap);
      va_end(ap);

      BSLS_ASSERT(::strlen(ret.c_str()) == *numChars);

      ret.resize(*numChars);
      return ret;
  }
Then, in main, we call the function correctly a couple of times:
      size_t len;
      std::string s;

      s = strPrintf(&len, "%s %s %s %g\n", "woof", "meow", "arf", 23.5);
      assert("woof meow arf 23.5\n" == s);
      assert(19 == len);
      assert(len == s.length());

      s = strPrintf(&len, "%s %s %s %s %s %s %s %s %s\n",
                             "The", "rain", "in", "Spain", "falls", "mainly",
                                                       "in", "the", "plain");
      assert("The rain in Spain falls mainly in the plain\n" == s);
      assert(44 == len);
      assert(len == s.length());
Now, we call it with too many arguments and of the wrong type:
      s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
Finally, we observe the compiler warnings with clang:
  .../bsla_printf.t.cpp:328:41: warning: format specifies type 'int' but the
  argument has type 'const char *' [-Wformat]
      s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
                           ~~         ^~~~~
                           %s
  .../bsla_printf.t.cpp:328:48: warning: format specifies type 'double' but
  the argument has type 'int' [-Wformat]
      s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
                              ~~             ^~
                              %d
  .../bsla_printf.t.cpp:328:52: warning: format specifies type 'double' but
  the argument has type 'int' [-Wformat]
      s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
                                 ~~              ^~
                                 %d
  .../bsla_printf.t.cpp:328:56: warning: data argument not used by format
  string [-Wformat-extra-args]
      s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
                          ~~~~~~~~~~                 ^