// bsla_scanf.h                                                       -*-C++-*-
#ifndef INCLUDED_BSLA_SCANF
#define INCLUDED_BSLA_SCANF

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@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: 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 'int's and 'float's with Random Numbers
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we want to have a function that will populate a list of 'int's and
// 'float's 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
//..

#include <bsls_compilerfeatures.h>
#include <bsls_platform.h>

#if defined(BSLS_PLATFORM_CMP_GNU)   ||                                       \
    defined(BSLS_PLATFORM_CMP_CLANG) ||                                       \
    defined(BSLS_PLATFORM_CMP_HP)
    #define BSLA_SCANF(FMTIDX, STARTIDX)                                      \
                               __attribute__((format(scanf, FMTIDX, STARTIDX)))

    #define BSLA_SCANF_IS_ACTIVE 1
#else
    #define BSLA_SCANF(FMTIDX, STARTIDX)

    #define BSLA_SCANF_IS_ACTIVE 0
#endif

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------