Quick Links:

bal | bbl | bdl | bsl

Component bsla_scanf
[Package bsla]

Provide a macro for checking scanf-style format strings. More...

Outline
Purpose:
Provide a macro for checking scanf-style format strings.
Macros:
BSLA_SCANF(FMTIDX, STARTIDX) validate scanf format and arguments
BSLA_SCANF_IS_ACTIVE 0 if BSLA_SCANF expands to nothing and 1 otherwise
See also:
Component bsla_annotations
Description:
This component provides a preprocessor macro that indicates that one of the arguments to a function is a scanf-style format string, and that the arguments starting at a certain index are to be checked for compatibility with that format string.
Macro Reference:
BSLA_SCANF(FMTIDX, STARTIDX) This annotation instructs the compiler to perform additional checks on so-annotated functions that take scanf-style arguments, which should be type-checked against a format string.
The FMTIDX parameter is the one-based index to the const 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_scanf(void *obj, const char *format, ...) BSLA_SCANF(2, 3);
BSLA_SCANF_IS_ACTIVE The macro BSLA_SCANF_IS_ACTIVE is defined to 0 if BSLA_SCANF expands to nothing and 1 otherwise.
Usage:
This section illustrates intended use of this component.
Example 1: Populate a Sequence of ints and floats with Random Numbers:
Suppose we want to have a function that will populate a list of ints and floats with random numbers in the range [ 0 .. 100 ).
First, we define our function:
  int populateValues(const char *format, ...) BSLA_SCANF(1, 2);
      // Use 'rand' to populate 'int's and 'float's, passed by pointer after
      // the specified 'format', which will specify the types of the
      // variables passed.  Return the number of variables populated, or -1
      // if the format string is invalid.

  int populateValues(const char *format, ...)
  {
      int ret = 0;

      va_list ap;
      va_start(ap, format);

      for (const char *pc = format; *pc; ++pc) {
          if ('%' != *pc) {
              continue;
          }
          const char c = *++pc;
          if ('%' == c) {
              continue;
          }
          else if ('d' == c) {
 va_arg(ap, int *)   = static_cast<unsigned>(rand()) % 100;
          }
          else if ('f' == c || 'e' == c || 'g' == c) {
              const int characteristic = static_cast<unsigned>(rand()) % 100;
              const int mantissa = static_cast<unsigned>(rand()) % 1000;

 va_arg(ap, float *) = static_cast<float>(characteristic +
                                                          mantissa / 1000.0);
          }
          else {
              // Unrecognized character.  Return a negative value.

              ret = -1;
              break;
          }

          ++ret;
      }

      va_end(ap);

      return ret;
  }
Then, in main, we call populateValues properly:
      float ff[3] = { 0, 0, 0 };
      int   ii[3] = { 0, 0, 0 };

      int numVars = populateValues("%d %g %g %d %d %g",
                             &ii[0], &ff[0], &ff[1], &ii[1], &ii[2], &ff[2]);
      assert(6 == numVars);
      for (int jj = 0; jj < 3; ++jj) {
          assert(0 <= ii[jj]);
          assert(0 <= ff[jj]);
          assert(     ii[jj] < 100);
          assert(     ff[jj] < 100);
      }
      printf("%d %g %g %d %d %g\n",
                                   ii[0], ff[0], ff[1], ii[1], ii[2], ff[2]);
Next, we observe that there are no compiler warnings and a reasonable set of random numbers are output:
  83 86.777 15.793 35 86 92.649
Now, we make a call where the arguments don't match the format string:
      numVars = populateValues("%d %g", &ff[0], &ii[0]);
Finally, we observe the following compiler warnings with clang:
  .../bsla_scanf.t.cpp:351:43: warning: format specifies type 'int *' but the
  argument has type 'float *' [-Wformat]
      numVars = populateValues("%d %g", &ff[0], &ii[0]);
                                ~~      ^~~~~~
                                %f
  .../bsla_scanf.t.cpp:351:51: warning: format specifies type 'float *' but
  the argument has type 'int *' [-Wformat]
      numVars = populateValues("%d %g", &ff[0], &ii[0]);
                                   ~~           ^~~~~~
                                   %d