// 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 // ======================= class NameOf_Base { // This 'class' provide non-template implementation code for the 'NameOf' // template class. 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 static const char *initBuffer(char *buffer, const char *functionName); // 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'. }; // ================== // class bsls::NameOf // ================== template <class TYPE> class NameOf : public NameOf_Base { // 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. // CLASS DATA static bsls::AtomicPointer<const char> s_buffer_p; public: // CREATOR NameOf(); // Initialize the base class of this object to the name of 'TYPE'. // ACCESSOR operator const char *() const; // Return a pointer to the a string containing the name of 'TYPE'. const char *name() 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 *'. }; // ============================================================================ // TEMPLATE FUNCTION DEFINITIONS // ---------------------------------------------------------------------------- // ------------------ // class bsls::NameOf // ------------------ // CLASS DATA template <class TYPE> bsls::AtomicPointer<const char> NameOf<TYPE>::s_buffer_p; // CREATOR template <class TYPE> NameOf<TYPE>::NameOf() // Initialize the base class of this object to name of 'TYPE'. { // 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 template <class TYPE> const char *nameOfType(const TYPE&) // Return the name of the type of the object passed to this function. { 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 ----------------------------------