// bsls_bsltestutil.h                                                 -*-C++-*-
#ifndef INCLUDED_BSLS_BSLTESTUTIL
#define INCLUDED_BSLS_BSLTESTUTIL

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

//@PURPOSE: Provide test utilities for `bsl` that do not use <iostream>.
//
//@CLASSES:
//  bsls::BslTestUtil: utilities to aid writing `bsl` test drivers
//
//@MACROS:
//  BSLS_BSLTESTUTIL_ASSERT(X): record and print error if `!X`
//  BSLS_BSLTESTUTIL_LOOP_ASSERT(I, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP2_ASSERT(I, J, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP3_ASSERT(I, J, K, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP4_ASSERT(I, J, K, L, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP5_ASSERT(I, J, K, L, M, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP6_ASSERT(I, J, K, L, M, N, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP7_ASSERT(I, J, K, L, M, N, O, X): print args if `!X`
//  BSLS_BSLTESTUTIL_LOOP8_ASSERT(I, J, K, L, M, N, O,V, X): print args if `!X`
//
//  BSLS_BSLTESTUTIL_Q(X) : quote identifier literally
//  BSLS_BSLTESTUTIL_P(X) : print identifier and value
//  BSLS_BSLTESTUTIL_P_(X): print identifier and value without '\n'
//  BSLS_BSLTESTUTIL_L_   : current line number
//  BSLS_BSLTESTUTIL_T_   : print tab without '\n'
//
//  BSLS_BSLTESTUTIL_FORMAT_ZU : `printf` format for `size_t`
//  BSLS_BSLTESTUTIL_FORMAT_TD : `printf` format for `ptrdiff_t`
//  BSLS_BSLTESTUTIL_FORMAT_I64: `printf` format for unsigned 64-bit integers
//  BSLS_BSLTESTUTIL_FORMAT_U64: `printf` format for signed 64-bit integers
//  BSLS_BSLTESTUTIL_FORMAT_PTR: `printf` format for `uintptr_t`
//
//@DESCRIPTION: This component provides standard facilities for components in
// the `bsl` package group to produce test driver output, including the
// standard printing macros used in BDE-style test drivers (`ASSERT`,
// `LOOP_ASSERT`, `ASSERTV`, `P`, `Q`, `L`, and `T`), and a suite of
// cross-platform format strings for printing C++ or BDE-specific types with
// `printf`.
//
// Many components in the `bsl` package group reside below the standard
// library; therefore, hierarchical design dictates that the test driver for
// these components shall not use `iostream` (which is part of the standard
// library), and instead they shall only rely on the `printf` function to print
// objects' values.  Using `printf` over `iostream` has the following
// disadvantages:
//
// * The `printf` function requires a format string to specify the way to
//   print an object; so, unlike `iostream`, printing different types of
//   objects using `printf` requires different syntaxes due to the need for
//   different format strings.
// * While the format strings for built-in types can be included as part of
//   the standard boiler plate code of the test driver, printing a
//   user-defined type often requires additional code that is not part of the
//   standard boilerplate.
//
// This component provides solutions to these issues by (1) encapsulating all
// the standard printing macros in a single place, (2) providing a way to
// extend the supplied macros to support user-defined types, and (3) providing
// macros that resolve the correct `printf` format strings for types that do
// not have standard, cross-platform format strings of their own.
//
// The macros in this component use a class method template,
// `BslTestUtil::callDebugprint`, to print the value of an object of the
// parameterized type, along with an optional leading string and an optional
// trailing string, to the console.  The value of the object of the
// parameterized type will be printed using a free function named `debugprint`.
//
// The macros defined in this component natively support built-in types through
// the `debugprint` function overloads for these types defined in this
// component.  The macros can be extended support additional user-defined types
// by defining function overloads for `debugprint` that takes a single
// parameter of each user-defined type, in the same namespace in which the
// user-defined type is defined.  See the second usage example for more
// details.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Writing a Test Driver
/// - - - - - - - - - - - - - - - -
// First, we write a component to test, which provides a utility class:
// ```
// namespace bslabc {
//
// struct BslExampleUtil {
//     // This utility class provides sample functionality to demonstrate how
//     // a test driver might be written validating its only method.
//
//     static int fortyTwo();
//         // Return the integer value '42'.
// };
//
// inline
// int BslExampleUtil::fortyTwo()
// {
//     return 42;
// }
//
// }  // close package namespace
// ```
// Then, we can write a test driver for this component.  We start by providing
// the standard BDE assert test macro:
// ```
// // ========================================================================
// //                       STANDARD BDE ASSERT TEST MACRO
// // ------------------------------------------------------------------------
// static int testStatus = 0;
//
// static void aSsErT(bool b, const char *s, int i)
// {
//     if (b) {
//         printf("Error " __FILE__ "(%d): %s    (failed)\n", i, s);
//         fflush(stdout);
//         if (testStatus >= 0 && testStatus <= 100) ++testStatus;
//     }
// }
//
// # define ASSERT(X) { aSsErT(!(X), #X, __LINE__); }
// ```
// Next, we define the standard print and `LOOP_ASSERT` macros, as aliases to
// the macros defined by this component:
// ```
// // ========================================================================
// //                       STANDARD BDE TEST DRIVER MACROS
// // ------------------------------------------------------------------------
// #define LOOP_ASSERT  BSLS_BSLTESTUTIL_LOOP_ASSERT
// #define LOOP2_ASSERT BSLS_BSLTESTUTIL_LOOP2_ASSERT
// #define LOOP3_ASSERT BSLS_BSLTESTUTIL_LOOP3_ASSERT
// #define LOOP4_ASSERT BSLS_BSLTESTUTIL_LOOP4_ASSERT
// #define LOOP5_ASSERT BSLS_BSLTESTUTIL_LOOP5_ASSERT
// #define LOOP6_ASSERT BSLS_BSLTESTUTIL_LOOP6_ASSERT
// #define LOOP7_ASSERT BSLS_BSLTESTUTIL_LOOP7_ASSERT
// #define LOOP8_ASSERT BSLS_BSLTESTUTIL_LOOP8_ASSERT
//
// #define Q   BSLS_BSLTESTUTIL_Q   // Quote identifier literally.
// #define P   BSLS_BSLTESTUTIL_P   // Print identifier and value.
// #define P_  BSLS_BSLTESTUTIL_P_  // 'P(X)' without '\n'.
// #define T_  BSLS_BSLTESTUTIL_T_  // Print a tab (w/o newline).
// #define L_  BSLS_BSLTESTUTIL_L_  // current Line number
// ```
// Now, using the (standard) abbreviated macro names we have just defined, we
// write a test function for the `static` `fortyTwo` method, to be called from
// a test case in a test driver.
// ```
// void testFortyTwo(bool verbose)
// {
//     const int value = bslabc::BslExampleUtil::fortyTwo();
//     if (verbose) P(value);
//     LOOP_ASSERT(value, 42 == value);
// }
// ```
// Finally, when `testFortyTwo` is called from a test case in verbose mode we
// observe the console output:
// ```
// value = 42
// ```
//
///Example 2: Adding Support For A New User-Defined Type
///- - - - - - - - - - - - - - - - - - - - - - - - - - -
// First, we define a new user-defined type, `MyType`:
// ```
// namespace xyza {
//
// class MyType {
//     // This elided class provides a type intended to show how the macros in
//     // 'bsls_bsltestutil' can be extended to support a new user-defined
//     // type.
//
//   private:
//     // DATA
//     int d_value;  // the value of MyType
//
//     // ...
//
//   public:
//     // CREATORS
//
//     // ...
//
//     explicit MyType(int value);
//         // Create a 'MyType' object with 'd_value' set to the specified
//         // 'value'.
//
//     // ACCESSORS
//
//     // ...
//
//     int value() const;
//         // Return the value of 'd_value'.
//
//     // ...
// };
//
// // ...
//
// inline
// MyType::MyType(int value)
// : d_value(value)
// {
// }
//
// // ...
//
// inline
// int MyType::value() const
// {
//     return d_value;
// }
// ```
// Then, in the same namespace in which `MyType` is defined, we define a
// function `debugprint` that prints the value of a `MyType` object to the
// console.  (In this case, we will simply print a string literal for
// simplicity):
// ```
// void debugprint(const MyType& obj)
// {
//     printf("MyType<%d>", obj.value());
// }
//
// }  // close namespace xyza
// ```
// Now, using the (standard) abbreviated macro names previously defined, we
// write a test function for the `MyType` constructor, to be called from a test
// case in a test driver.
// ```
// void testMyTypeSetValue(bool verbose) {
//     xyza::MyType obj(9);
//     if (verbose) P(obj);
//     LOOP_ASSERT(obj.value(), obj.value() == 9);
// }
// ```
// Finally, when `testMyTypeSetValue` is called from a test case in verbose
// mode we observe the console output:
// ```
// obj = MyType<9>
// ```
//
///Example 3: Printing Unusual Types with `printf`
///- - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we are writing a test driver that needs to print out the contents of
// a complex data structure in `veryVeryVerbose` mode.  The complex data
// structure contains, among other values, an array of block sizes, expressed
// as `size_t`.  It would be very cumbersome, and visually confusing, to print
// each member of the array with either the `P_` or `Q_` standard output
// macros, so we elect to print out the array as a single string, following the
// pattern of `[ A, B, C, D, E, ... ]`.  This could be easily accomplished with
// multiple calls to `printf`, except that `printf` has no cross-platform
// standard formatting string for `size_t`.  We can use the
// `BSLS_BSLTESTUTIL_FORMAT_ZU` macro to resolve the appropriate format string
// for us on each platform.
//
// First, we write a component to test, which provides an a utility that
// operates on a list of memory blocks.  Each block is a structure containing a
// base address, a block size, and a pointer to the next block in the list.
// ```
// namespace xyza {
// struct Block {
//     // DATA
//     char   *d_address;
//     size_t  d_size;
//     Block  *d_next;
//
//     // ...
// };
//
// class BlockList {
//     // ...
//
//     // DATA
//     Block *d_head;
//
//     // ...
//
//   public:
//     // CREATORS
//     BlockList();
//     ~BlockList();
//
//     // MANIPULATORS
//
//     Block *begin();
//     Block *end();
//
//     void addBlock(size_t size);
//
//     // ...
//
//     // ACCESSORS
//     int length();
//
//     // ...
// };
//
// }  // close namespace xyza
// ```
// Then, we write a test driver for this component.
// ```
// // ...
//
// // ========================================================================
// //                       STANDARD BDE TEST DRIVER MACROS
// // ------------------------------------------------------------------------
//
// // ...
// ```
// Here, after defining the standard BDE test macros, we define a macro, `ZU`
// for the platform-specific `printf` format string for `size_t`:
// ```
// // ========================================================================
// //                          PRINTF FORMAT MACROS
// // ------------------------------------------------------------------------
// #define ZU BSLS_BSLTESTUTIL_FORMAT_ZU
// ```
// Note that, we could use `BSLS_BSLTESTUTIL_FORMAT_ZU` as is, but it is more
// convenient to define `ZU` locally as an abbreviation.
//
// Next, we write the test apparatus for the test driver, which includes a
// support function that prints the list of blocks in a `BlockList` in a
// visually succinct form:
// ```
// void printBlockList(xyza::BlockList &list)
// {
//     xyza::Block *blockPtr = list.begin();
//
//     printf("{\n");
//     while (blockPtr != list.end()) {
// ```
// Here, we use `ZU` as the format specifier for the `size_t` in the `printf`
// invocation.  `ZU` is the appropriate format specifier for `size_t` on each
// supported platform.
// ```
//         printf("\t{ address: %p,\tsize: " ZU " }",
//                blockPtr->d_address,
//                blockPtr->d_size);
//         blockPtr = blockPtr->d_next;
//
//         if (blockPtr) {
//             printf(",\n");
//         } else {
//             printf("\n");
//         }
//     }
//     printf("}\n");
// }
// ```
// Note that because we are looping through a number of blocks, formatting the
// output directly with `printf` produces more readable output than we would
// get from callling the standard output macros.
//
// Calling `printf` directly will yield output similar to:
// ```
// {
//     { address: 0x012345600,    size: 32 },
//     ...
// }
// ```
// while the standard output macros would have produced:
// ```
// {
//     { blockPtr->d_address = 0x012345600,    blockPtr->d_size: 32 },
//     ...
// }
// ```
// Now, we write a test function for one of our test cases, which provides a
// detailed trace of `BlockList` contents:
// ```
// void testBlockListConstruction(bool veryVeryVerbose)
// {
//     // ...
//
//     {
//         xyza::BlockList bl;
//
//         bl.addBlock(42);
//         bl.addBlock(19);
//         bl.addBlock(1024);
//
//         if (veryVeryVerbose) {
//             printBlockList(bl);
//         }
//
//         ASSERT(3 == bl.length());
//
//         // ...
//     }
//
//     // ...
// }
// ```
// Finally, when `testBlockListConstruction` is called from a test case in
// `veryVeryVerbose` mode, we observe console output similar to:
// ```
// {
//     { address: 0x012345600,    size: 42 },
//     { address: 0x012345610,    size: 19 },
//     { address: 0x012345620,    size: 1024 }
// }
// ```

#include <bsls_platform.h>

#if defined(BSLS_PLATFORM_CMP_MSVC)
#   include <stddef.h>
#else
#   include <stdint.h>
#endif

                            // =================
                            // Macro Definitions
                            // =================

#define BSLS_BSLTESTUTIL_ASSERT(X)                                            \
    do { aSsErT(!(X), #X, __LINE__); } while (false)

#define BSLS_BSLTESTUTIL_LOOP0_ASSERT                                         \
    BSLS_BSLTESTUTIL_ASSERT

#define BSLS_BSLTESTUTIL_LOOP_ASSERT(I,X) do {                                \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP1_ASSERT                                         \
    BSLS_BSLTESTUTIL_LOOP_ASSERT

#define BSLS_BSLTESTUTIL_LOOP2_ASSERT(I,J,X) do {                             \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP3_ASSERT(I,J,K,X) do {                           \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(K, #K ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP4_ASSERT(I,J,K,L,X) do {                         \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(K, #K ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(L, #L ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP5_ASSERT(I,J,K,L,M,X) do {                       \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(K, #K ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(L, #L ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(M, #M ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP6_ASSERT(I,J,K,L,M,N,X) do {                     \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(K, #K ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(L, #L ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(M, #M ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(N, #N ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP7_ASSERT(I,J,K,L,M,N,O,X) do {                   \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(K, #K ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(L, #L ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(M, #M ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(N, #N ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(O, #O ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

#define BSLS_BSLTESTUTIL_LOOP8_ASSERT(I,J,K,L,M,N,O,V,X) do {                 \
    if (!(X)) { BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(I, #I ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(J, #J ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(K, #K ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(L, #L ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(M, #M ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(N, #N ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(O, #O ": ", "\t");          \
                BloombergLP::bsls::                                           \
                      BslTestUtil::callDebugprint(V, #V ": ",                 \
                                                        "    (context)\n");   \
                aSsErT(true, #X, __LINE__); } } while (false)

// The 'BSLS_BSLTESTUTIL_EXPAND' macro is required to workaround a
// pre-processor issue on windows that prevents __VA_ARGS__ to be expanded in
// the definition of 'BSLS_BSLTESTUTIL_NUM_ARGS'
#define BSLS_BSLTESTUTIL_EXPAND(X)                                            \
    X

#define BSLS_BSLTESTUTIL_NUM_ARGS_IMPL(X8, X7, X6, X5, X4, X3, X2, X1, X0,    \
                                       N, ...)                                \
    N

#define BSLS_BSLTESTUTIL_NUM_ARGS(...)                                        \
    BSLS_BSLTESTUTIL_EXPAND(BSLS_BSLTESTUTIL_NUM_ARGS_IMPL(                   \
                                   __VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0, ""))

#define BSLS_BSLTESTUTIL_LOOPN_ASSERT_IMPL(N, ...)                            \
    BSLS_BSLTESTUTIL_EXPAND(BSLS_BSLTESTUTIL_LOOP ## N ## _ASSERT(__VA_ARGS__))

#define BSLS_BSLTESTUTIL_LOOPN_ASSERT(N, ...)                                 \
    BSLS_BSLTESTUTIL_LOOPN_ASSERT_IMPL(N, __VA_ARGS__)

#define BSLS_BSLTESTUTIL_ASSERTV(...)                                         \
    BSLS_BSLTESTUTIL_LOOPN_ASSERT(                                            \
                           BSLS_BSLTESTUTIL_NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

// STANDARD TEST DRIVER OUTPUT MACROS

/// Quote identifier literally.
#define BSLS_BSLTESTUTIL_Q(X)                                                 \
                BloombergLP::bsls::                                           \
                             BslTestUtil::printStringNoFlush("<| " #X " |>\n");

/// Print identifier and its value.
#define BSLS_BSLTESTUTIL_P(X)                                                 \
                BloombergLP::bsls::                                           \
                                BslTestUtil::callDebugprint(X, #X " = ", "\n");

/// P(X) without '\n'.
#define BSLS_BSLTESTUTIL_P_(X)                                                \
                BloombergLP::bsls::                                           \
                                BslTestUtil::callDebugprint(X, #X " = ", ", ");

/// Current line number.
#define BSLS_BSLTESTUTIL_L_ __LINE__

/// Print a tab (w/o newline).
#define BSLS_BSLTESTUTIL_T_ BloombergLP::bsls::BslTestUtil::printTab();

// PRINTF FORMAT MACROS
#if defined(BSLS_PLATFORM_CMP_MSVC)
#  define BSLS_BSLTESTUTIL_FORMAT_ZU "%Iu"
#else
#  define BSLS_BSLTESTUTIL_FORMAT_ZU "%zu"
#endif
    // Provide a platform-independent way to specify a 'size_t' format for
    // 'printf'.

#if defined(BSLS_PLATFORM_CMP_MSVC)
#  define BSLS_BSLTESTUTIL_FORMAT_TD "%Id"
#else
#  define BSLS_BSLTESTUTIL_FORMAT_TD "%td"
#endif
    // Provide a platform-independent way to specify a 'ptrdiff_t' format for
    // 'printf'.

#if defined(BSLS_PLATFORM_CMP_MSVC)
#  define BSLS_BSLTESTUTIL_FORMAT_I64 "%I64d"
#else
#  define BSLS_BSLTESTUTIL_FORMAT_I64 "%lld"
#endif
    // Provide a platform-independent way to specify a signed 64-bit integer
    // format for 'printf'.

#if defined(BSLS_PLATFORM_CMP_MSVC)
#  define BSLS_BSLTESTUTIL_FORMAT_U64 "%I64u"
#else
#  define BSLS_BSLTESTUTIL_FORMAT_U64 "%llu"
#endif
    // Provide a platform-independent way to specify an unsigned 64-bit integer
    // format for 'printf'.

#if defined(BSLS_PLATFORM_CPU_64_BIT)
#   if defined(BSLS_PLATFORM_CMP_MSVC)
#       define BSLS_BSLTESTUTIL_FORMAT_PTR "%llX"
#   else
#       define BSLS_BSLTESTUTIL_FORMAT_PTR "%lX"
#   endif
#else
#  define BSLS_BSLTESTUTIL_FORMAT_PTR "%X"
#endif
    // Provide a platform-independent way to specify a 'uintptr_t' integer
    // format for 'printf'.

namespace BloombergLP {

namespace bsls {

                           // ==================
                           // struct BslTestUtil
                           // ==================

/// This class provides a namespace for utilities that are useful when
/// writing a test driver that is not permitted to use the standard C++
/// iostream facilities, which is typical of test drivers in the `bsl`
/// package group.
struct BslTestUtil {

  private:
    // PRIVATE CLASS METHODS

    /// Return `ptr` without modification.  Note that this is NOT an inline
    /// function, so that if the caller is not in the same module, the
    /// compiler has no way of knowing that it's an identity transform.
    static void *identityPtr(void *ptr);

  public:
    // CLASS METHODS

    /// Write any unwritten text in the output buffer to `stdout`.
    static void flush();

    /// Print to the console the specified string, `s`.  Note that the
    /// underlying stream is *not* flushed.
    static void printStringNoFlush(const char *s);

    /// Print to the console a tab character, and then `flush` the
    /// underlying stream to ensure the text is written.
    static void printTab();

    /// Print the value of the specified `object` of the parameterized
    /// `TYPE` to the console.  Optionally specify a `leadingString`, which
    /// will be printed before `object`, and a `trailingString`, which will
    /// be printed after `object`.  If `leadingString` is 0, then nothing
    /// will be printed before `object`.  If `trailingString` is 0, then
    /// nothing will be printed after `object`.
    template <class TYPE>
    static void callDebugprint(const TYPE&  object,
                               const char  *leadingString  = 0,
                               const char  *trailingString = 0);

    /// Return the specified `functionPtr` (expected to be a static function
    /// pointer) without modification.  The value of `functionPtr` is
    /// transformed through `identityPtr` so that if the caller is in a
    /// different module, the compiler will have no way of knowing that this
    /// is an identity transform and thus no way of inlining the call.
    ///
    /// Note: the Windows optimizer is still able to inline the call, it may
    /// be comparing the result of this function with the argument and
    /// branching to inline on equality and call on inequality, so the
    /// Windows optimizer has to be turned off with
    /// `# pragma optimize("", off)`.
    ///
    /// Also note that even with an optimizer that can't figure out that
    /// this is an identity transform, there is still the possibility of
    /// chaining the call.
    template <class FUNCTION_PTR>
    static FUNCTION_PTR makeFunctionCallNonInline(FUNCTION_PTR functionPtr);
};

// FREE FUNCTIONS

/// Print to the console the string "true" if the specified `v` is true, and
/// the string "false" otherwise.
void debugprint(bool v);

/// Print to the console the specified character, `v`, enclosed by
/// single-quote characters (').
void debugprint(char v);

/// Print to the console the specified integer value, `v`, formatted as a
/// string.
void debugprint(signed char        v);
void debugprint(unsigned char      v);
void debugprint(short              v);
void debugprint(unsigned short     v);
void debugprint(int                v);
void debugprint(unsigned int       v);
void debugprint(long               v);
void debugprint(unsigned long      v);
void debugprint(long long          v);
void debugprint(unsigned long long v);

/// Print to the console the specified value, `v`, formatted as a string
/// enclosed by single-quote characters (').
void debugprint(float       v);
void debugprint(double      v);
void debugprint(long double v);

/// Print to the console the specified string, `v`, enclosed by quote
/// characters ("), unless `v` is null, in which case print `(null)`
/// (without quotes of any kind).
void debugprint(const char *         v);
void debugprint(char *               v);
void debugprint(const volatile char *v);
void debugprint(volatile char *v);

/// Print to the console the specified memory address, `v`, formatted as a
/// hexadecimal integer.
void debugprint(void *               v);
void debugprint(volatile void *      v);
void debugprint(const void *         v);
void debugprint(const volatile void *v);

/// Print to the console the specified function pointer, `v`, formatted as a
/// hexadecimal integer.  On some platforms (notably Windows), a function
/// pointer is treated differently from an object pointer, and the compiler
/// will not be able to determine which `void *` overload of `debugprint`
/// should be used for a function pointer.  Therefore an overload of
/// `debugprint` is provided specifically for function pointers.  Because
/// the type signature of a function pointer varies with its return type as
/// well as with its argument list, a template function is used, to provide
/// matches for all return types.
template <class RESULT>
void debugprint(RESULT (*v)());

// ============================================================================
//                  TEMPLATE AND INLINE FUNCTION DEFINITIONS
// ============================================================================

                           // ------------------
                           // struct BslTestUtil
                           // ------------------

// CLASS METHODS
template <class TYPE>
void BslTestUtil::callDebugprint(const TYPE&  obj,
                                 const char  *leadingString,
                                 const char  *trailingString)
{
    if (leadingString) {
        BloombergLP::bsls::BslTestUtil::printStringNoFlush(leadingString);
    }

    debugprint(obj);

    if (trailingString) {
        BloombergLP::bsls::BslTestUtil::printStringNoFlush(trailingString);
    }
    flush();
}

template <class FUNCTION_PTR>
inline
FUNCTION_PTR BslTestUtil::makeFunctionCallNonInline(FUNCTION_PTR function)
{
    // 'static_assert' isn't available pre-C++11, and 'BSLMF_ASSERT' is not
    // accessible from here in bsls, so we divide by the boolean expression
    // under test -- if the boolean expression is 'false', it will be a
    // divide-by-zero in an enum and fail to compile, thus providing us with a
    // "poor man's" compile-time assert.
    //
    // The cast to 'int' is necessary because dividing by 'bool' gives a
    // compiler warning on Windows.

    enum { k_STATIC_ASSERT = 1 /
                    static_cast<int>(sizeof(FUNCTION_PTR) == sizeof(void *)) };

    return reinterpret_cast<FUNCTION_PTR>(identityPtr(reinterpret_cast<void *>(
                                                                   function)));
}

}  // close package namespace

// FREE FUNCTIONS
template <class RESULT>
void bsls::debugprint(RESULT (*v)())
{
    uintptr_t address = reinterpret_cast<uintptr_t>(v);
    debugprint(reinterpret_cast<void *>(address));
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2018 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 ----------------------------------
