// bsls_nameof.h                                                      -*-C++-*-
#ifndef INCLUDED_BSLS_NAMEOF
#define INCLUDED_BSLS_NAMEOF

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

//@PURPOSE: Provide a `NameOf` type for displaying template type at run-time.
//
//@CLASSES:
//  bsls::NameOf: template class to return name of template parameter
//
//@FREE FUNCTIONS:
//  bsls::nameOfType(const TYPE&): template function to return name of `TYPE`
//
//@DESCRIPTION: This component provides a template class,
// `bsls::NameOf<TYPE>`, which can implicitly cast to a `const char *` which
// will point to a description of `TYPE`.
//
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example 1:
/// - - - - - - - - - - - - - - - - - - - - - - - - - -
// First, your test driver must have the following `using` statements so that
// the template class `NameOf` and the template function `nameOfType` can be
// referred to concisely, without having to qualify them with namespaces on
// each call.  Note that if you've already said `using namespace BloombergLP`
// you don't have to give the `BloombergLP::` qualifiers here:
// ```
// using BloombergLP::bsls::NameOf;
// using BloombergLP::bsls::nameOfType;
// ```
// Next, we define some types in the unnamed namespace:
// ```
// namespace {
//
// struct MyType {
//     int  d_i;
//     char d_c;
// };
//
// union MyUnion {
//     int  d_i;
//     char d_buffer[100];
// };
//
// }  // close unnamed namespace
// ```
// Next, we see that the `NameOf` template class, when created with a type, can
// be implicitly cast to a `const char *` which points to a description of the
// type.
// ```
// assert(!std::strcmp("double", NameOf<double>()));
// assert(!std::strcmp("int",    NameOf<int>()));
// ```
// Then, we see that when `NameOf` is passed a `typedef` or template parameter,
// it resolves it to the original type:
// ```
// typedef int Woof;
//
// assert(!std::strcmp("int",    NameOf<Woof>()));
// ```
// Next, we introduce the `nameOfType` template function, which takes as any
// variable as an argument, and returns a `const char *` pointing to a
// description of the type of the variable.
// ```
// int ii = 2;
//
// assert(!std::strcmp("int",    nameOfType(ii)));
// ```
// Then, we see that `NameOf` and `nameOfType` will strip `BloombergLP::`
// namespace qualifiers, as well as anonymous namespace qualifiers.
// ```
// typedef BloombergLP::bsls::Stopwatch SW;
//
// const SW      sw;
// const MyType  mt = { 2, 'a' };
// MyUnion       mu;
// mu.d_i = 7;
//
// assert(!std::strcmp("bsls::Stopwatch", NameOf<SW>()));
// assert(!std::strcmp("bsls::Stopwatch",
//                                   NameOf<BloombergLP::bsls::Stopwatch>()));
// assert(!std::strcmp("bsls::Stopwatch", nameOfType(sw)));
//
// assert(!std::strcmp("MyType",          NameOf<MyType>()));
// assert(!std::strcmp("MyType",          nameOfType(mt)));
//
// assert(!std::strcmp("MyUnion",         NameOf<MyUnion>()));
// assert(!std::strcmp("MyUnion",         nameOfType(mu)));
// ```
// There is a problem with template code not knowing how to implicitly cast the
// `NameOf` type to `const char *` for initializing or comparing with
// `std::string`s.  To facilitate, `NameOf` provides a `const char *` `name`
// accessor, to avoid the user having to do a more verbose `static cast`.
// ```
// const std::string swName = "bsls::Stopwatch";
// assert(swName == static_cast<const char *>(NameOf<SW>()));
// assert(swName == NameOf<SW>().name());
//
// const std::string swNameB = NameOf<SW>().name();
// assert(swNameB == swName);
//
// printf("NameOf<SW>() = \"%s\"\n", NameOf<SW>().name());
// printf("NameOfType(4 + 3) = \"%s\"\n", nameOfType(4 + 3));
// ```
// Note that `nameOfType` naturally returns a `const char *` and needs no help
// casting.  Note also that `bsls::debugprint` is able to figure out how to
// cast `NameOf` directly to `const char *` with no problems, as can iostreams,
// so there is no problem with putting a `NameOf` in a `LOOP_ASSERT` or
// `ASSERTV`.  It is anticipated that displaying by the BDE `ASSERTV`,
// `LOOP_ASSERT, and `P' macros will be the primary use of this component.
// ```
// printf("NameOf<double>() = ");
// BloombergLP::bsls::debugprint(NameOf<double>());
// printf("\n");
//
// typedef double DTYPE;
// DTYPE x = 7.3;
//
// LOOP_ASSERT(NameOf<DTYPE>(), x > 7);
//
// std::string myStr;              // Assign, not init, of string doesn't need
// myStr = NameOf<DTYPE>();        // '.name()'.
// assert("double" == myStr);
// ```
// Which produces:
// ```
// NameOf<SW>() = "bsls::Stopwatch"
// ```
// Finally, we see that `NameOf` and `nameOfType` will simplify
// `std::basic_string<...>` declarations to `std::string`.
// ```
// const std::string s = "std::string";
//
// assert(s == NameOf<std::basic_string<char> >().name());
// assert(s == NameOf<std::string>().name());
// assert(s == nameOfType(s));
//
// typedef NameOf<std::string> Nos;
//
// const std::string s2 = "bsls::NameOf<std::string>";
//
// assert(s2 == NameOf<NameOf<std::basic_string<char> > >().name());
// assert(s2 == NameOf<NameOf<std::string> >().name());
// assert(s2 == NameOf<Nos>().name());
// assert(s2 == nameOfType(Nos()));
// ```

#include <bsls_assert.h>
#include <bsls_atomic.h>
#include <bsls_bslonce.h>
#include <bsls_platform.h>

namespace BloombergLP {
namespace bsls {

                        // =======================
                        // class bsls::NameOf_Base
                        // =======================

/// This `class` provide non-template implementation code for the `NameOf`
/// template class.
class NameOf_Base {

  protected:
    // PROTECTED TYPES
#if defined(BSLS_PLATFORM_CMP_SUN)
    enum { k_BUF_SIZE_SOLARIS_CC = 256 };
#endif

    // On all platforms, the 'functionName' passed to 'initBuffer' will start
    // with the contents of local buffer 'uselessPreamble', which will not make
    // it into the final buffer, so we know that the final buffer can be
    // trimmed of this length.

#if defined(BSLS_PLATFORM_CMP_MSVC)
# if defined(BSLS_PLATFORM_CPU_64_BIT)
    enum { k_USELESS_PREAMBLE_LEN = 34 };
# else
    enum { k_USELESS_PREAMBLE_LEN = 37 };
# endif
#else
    enum { k_USELESS_PREAMBLE_LEN = 26 };
#endif

    // PROTECTED CLASS METHOD

    /// Initialize the specified `buffer` with the type name contained in
    /// the specified `functionName`, where `functionName` is the function
    /// name of the `NameOf` constructor.  Return either a pointer to
    /// `buffer`, or if `buffer` couldn't be properly initialized,
    /// `functionName`.
    static const char *initBuffer(char       *buffer,
                                  const char *functionName);
};

                            // ==================
                            // class bsls::NameOf
                            // ==================

/// This `class` provides a means to display the type name of its template
/// parameter `TYPE`.  An instance of this `class` can be implicitly (or
/// explicitly via the `name` accessor) cast to a `const char *` which will
/// point to a buffer containing the description of the type.  Note that all
/// instances of a given type will refer to the same character buffer
/// containing the name.
template <class TYPE>
class NameOf : public NameOf_Base {

    // CLASS DATA
    static bsls::AtomicPointer<const char> s_buffer_p;

  public:
    // CREATOR

    /// Initialize the base class of this object to the name of `TYPE`.
    NameOf();

    // ACCESSOR

    /// Return a pointer to the a string containing the name of `TYPE`.
    operator const char *() const;

    /// Return a pointer to the a string containing the name of `TYPE`, this
    /// serves as a convenient way to explicitly cast the return value to a
    /// `const char *`.
    const char *name() const;
};

// ============================================================================
//                        TEMPLATE FUNCTION DEFINITIONS
// ----------------------------------------------------------------------------

                              // ------------------
                              // class bsls::NameOf
                              // ------------------

// CLASS DATA
template <class TYPE>
bsls::AtomicPointer<const char> NameOf<TYPE>::s_buffer_p;

// CREATOR

/// Initialize the base class of this object to name of `TYPE`.
template <class TYPE>
NameOf<TYPE>::NameOf()
{
    // It is important to ensure that no two threads are initializing the same
    // buffer at the same time.

    static BslOnce once = BSLS_BSLONCE_INITIALIZER;
    BslOnceGuard   onceGuard;

    if (!s_buffer_p && onceGuard.enter(&once)) {
#if   defined(BSLS_PLATFORM_CMP_GNU) || defined(BSLS_PLATFORM_CMP_CLANG)
        static char buffer[sizeof(__PRETTY_FUNCTION__) -
                                                       k_USELESS_PREAMBLE_LEN];
        s_buffer_p = initBuffer(buffer, __PRETTY_FUNCTION__);
#elif defined(BSLS_PLATFORM_CMP_SUN)
# if  BSLS_PLATFORM_CMP_VERSION >= 0x5120
        // The Solaris CC compiler doesn't understand
        // 'sizeof(__PRETTY_FUNCTION__)'.

        static char buffer[k_BUF_SIZE_SOLARIS_CC];
        s_buffer_p = initBuffer(buffer, __PRETTY_FUNCTION__);
# else
        // '__PRETTY_FUNCTION__' not supported, only '__FUNCTION__', which
        // doesn't mention 'TYPE', so we can't deduce the name of 'TYPE'

        s_buffer_p = "unknown_type";
# endif
#elif defined(BSLS_PLATFORM_CMP_IBM)
        static char buffer[sizeof(__FUNCTION__) - k_USELESS_PREAMBLE_LEN];
        s_buffer_p = initBuffer(buffer, __FUNCTION__);
#elif defined(BSLS_PLATFORM_CMP_MSVC)
        static char buffer[sizeof(__FUNCSIG__) - k_USELESS_PREAMBLE_LEN];
        s_buffer_p = initBuffer(buffer, __FUNCSIG__);
#else
# error No function signature macro defined.
#endif

        BSLS_ASSERT_SAFE(s_buffer_p);
    }
}

// ACCESSOR
template <class TYPE>
inline
NameOf<TYPE>::operator const char *() const
{
    return s_buffer_p;
}

template <class TYPE>
inline
const char *NameOf<TYPE>::name() const
{
    return s_buffer_p;
}

// FREE FUNCTIONS

/// Return the name of the type of the object passed to this function.
template <class TYPE>
const char *nameOfType(const TYPE&)
{
    return NameOf<TYPE>();
}

}  // close package namespace
}  // close enterprise namespace

#endif

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