// bslmf_isempty.h -*-C++-*- #ifndef INCLUDED_BSLMF_ISEMPTY #define INCLUDED_BSLMF_ISEMPTY #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a compile-time check for detecting an empty class type. // //@CLASSES: // bsl::is_empty: standard meta-function for detecting empty classes // bsl::is_empty_v: the result value of the 'bsl::is_empty' meta-function // //@SEE_ALSO: bslmf_isclass.h // //@DESCRIPTION: This component defines a meta-function, 'bsl::is_empty' and a // template variable 'bsl::is_empty_v', that represents the result value of the // 'bsl::is_empty' meta-function, which may be used to determine whether a type // is a 'class' or 'struct' with no non-static data members other than // bit-fields of length 0, no virtual member functions, no virtual base // classes, and no base class 'B' for which 'is_empty<B>::value' is 'false'. // This meta-function conforms to the definition of the C++11 standard // 'is_empty' meta-function in section [meta.unary.prop]. // // An empty class type type is *usually* stateless and, can be "stored" in a // zero-length memory region. (Hypothetically, an empty object can hold state // by means a global address-to-state map, but such a design is rare and is // discouraged.) When a class inherits from an empty type, the compiler is // expected to optimize away the storage requirements of the empty base class. // This optimization is known as the "Empty Base Optimization" or "EBO". // // Note that the template variable 'is_empty_v' is defined in the C++17 // standard as an inline variable. If the current compiler supports the inline // variable C++17 compiler feature, 'bsl::is_empty_v' is defined as an // 'inline constexpr bool' variable. Otherwise, if the compiler supports the // variable templates C++14 compiler feature, 'bsl::is_empty_v' is defined as a // non-inline 'constexpr bool' variable. See // 'BSLS_COMPILERFEATURES_SUPPORT_INLINE_VARIABLES' and // 'BSLS_COMPILERFEATURES_SUPPORT_VARIABLE_TEMPLATES' macros in // bsls_compilerfeatures component for details. // ///Usage ///----- // In this section we show intended use of this component. // ///Example 1: Compute Storage Requirements for a Type /// - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we wish to create a generic function that will allocate a record // comprising a value of specified 't_TYPE' and a description in the form of a // null-terminated character string. First, we declare the function prototype: //.. // template <class t_TYPE> // void *makeRecord(const t_TYPE& value, const char* description); //.. // Next, we implement the function so that the copy of 'value' takes up no // space if 't_TYPE' is an empty class. We manage this by computing a zero // storage requirement if 'is_empty<t_TYPE>::value' is true: //.. // #include <cstring> // #include <new> // // template <class t_TYPE> // void *makeRecord(const t_TYPE& value, const char* description) // { // // 'ValueSize' is computed at compile time. // static const std::size_t ValueSize = bsl::is_empty<t_TYPE>::value ? // 0 : sizeof(t_TYPE); // // // Allocate memory for value and description // const std::size_t MemSize = ValueSize + std::strlen(description) + 1; // void *mem = ::operator new(MemSize); // // // Construct copy of value at front of allocated memory // ::new(mem) t_TYPE(value); // // // Copy description into space following value. // std::strcpy(static_cast<char*>(mem) + ValueSize, description); // // return mem; // } //.. // Finally, we use 'makeRecord' with both an empty and non-empty value type: //.. // struct EmptyMarker { }; // // int main() // { // void *record1 = makeRecord(9999, "four nines"); // // Value takes 'sizeof(int)' bytes at front of record. // assert(9999 == *static_cast<int*>(record1)); // assert(0 == std::strcmp(static_cast<char*>(record1) + sizeof(int), // "four nines")); // // void *record2 = makeRecord(EmptyMarker(), "Empty"); // // Value takes no space at front of record. // assert(0 == std::strcmp(static_cast<char*>(record2), "Empty")); // // ::operator delete(record1); // ::operator delete(record2); // // return 0; // } //.. #include <bslscm_version.h> #include <bslmf_integralconstant.h> #include <bslmf_voidtype.h> #include <bsls_compilerfeatures.h> #include <bsls_keyword.h> #if defined(BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER) # include <type_traits> #endif // BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bsls_nativestd.h> #endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #if defined(BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER) # define BSLMF_ISEMPTY_USE_NATIVE_TRAIT 1 #endif #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bslmf_isclass.h> #endif namespace bsl { // =============== // struct is_empty // =============== template <class t_TYPE> struct is_empty; // This 'struct' is a meta-function to determine whether the (template // parameter) 't_TYPE' is an empty class type. This 'struct' derives from // 'bsl::true_type' if the 't_TYPE' is empty, and from 'bsl::false_type' // otherwise. This meta-function has the same syntax as the 'is_empty' // meta-function defined in the C++11 standard [meta.unary.prop]; on C++03 // platforms, however, this meta-function defaults to 'true_type' if // 't_TYPE' is a 'class' or 'struct' with no non-static data members other // than bit-fields of length 0, no virtual member functions, no virtual // base classes, and no base class 'B' for which 'is_empty<B>::value' is // 'false'; otherwise 'is_empty' defaults to 'false_type'. Note that this // meta-function will fail to compile for a union that is the same size as // an empty class in C++03. } // close namespace bsl // ============================================================================ // CLASS TEMPLATE DEFINITIONS // ============================================================================ namespace bsl { #if defined(BSLMF_ISEMPTY_USE_NATIVE_TRAIT) // ======================= // struct is_empty (C++11) // ======================= template <class t_TYPE> struct is_empty : bsl::integral_constant<bool, ::std::is_empty<t_TYPE>::value> { // This specification defers to the native trait on supported C++11 // compilers. }; #ifdef BSLS_COMPILERFEATURES_SUPPORT_VARIABLE_TEMPLATES template <class t_TYPE> BSLS_KEYWORD_INLINE_VARIABLE constexpr bool is_empty_v = is_empty<t_TYPE>::value; // This template variable represents the result value of the // 'bsl::is_empty' meta-function. #endif #else // ==================== // struct Is_Empty_Size // ==================== struct Is_Empty_Size { // Private class: do not use outside of 'bslmf_isempty' component. This // component-private class is an example of an empty type, that can be used // to calculate the likely size of an empty type on the current platform. // As the inheritance-based technique used below disallows unions with a // hard error, rather than a SFINAE-friendly failure, it is important to // avoid instantiating the 'Imp' test unless there is a real chance that // the type might be empty. While we cannot protect against testing small // unions, we can rule out the more common cases where at least one union // member has more than the smallest permissible size on the current ABI. }; // ========================= // struct Is_Empty_Class_Imp // ========================= template <class t_TYPE, bool IS_CLASS = sizeof(t_TYPE) == sizeof(Is_Empty_Size)> struct Is_Empty_Class_Imp : false_type { // Private class: do not use outside of 'bslmf_isempty' component. This // meta-function derives from 'false_type' unless (the template parameter) // 't_TYPE' is exactly the same size as a known empty type, in which case // the following partial specialization is chosen. This test filters out // the majority of problems with 'union' types that are classes, and would // produce a hard, non-SFINAEable error trying to a create a derived class // to compare size in the next test. }; template <class t_TYPE> struct Is_Empty_Class_Imp<t_TYPE, true> { // Private class: do not use outside of 'bslmf_isempty' component. // Implementation of 'bsl::is_empty' for class types that are small enough // that they might be an empty class. private: struct Derived : t_TYPE { // This 'struct' derives from the template parameter 't_TYPE' from the // enclosing class template, and will have the same size as an 'int' if // 't_TYPE' is an empty class, and a larger size otherwise. Derived(); // Declared but not defined Derived(const Derived&); // Declared but not defined ~Derived(); // Declared but not defined int d_data; // 'Derived' is not empty }; public: typedef integral_constant<bool, sizeof(Derived) == sizeof(int)> type; // 'true_type' if (the template parameter) 't_TYPE' is an empty class, // and 'false_type' otherwise. }; // =================== // struct Is_Empty_Imp // =================== template <class t_TYPE, class = void> struct Is_Empty_Imp : false_type { // Private class: do not use outside of 'bslmf_isempty' component. This // meta-function provides an initial dispatch that always derives from // 'false_type' unless the template parameter 't_TYPE' is a class type, as // only class types can be empty. The following partial specialization // forwards all class types to a final test. This two-phase dispatch is // necessary as some types, such as 'void' and function types, cannot be // passed to a 'sizeof' operator that is used to implement the next stage // of matching. }; template <class t_TYPE> struct Is_Empty_Imp<t_TYPE, BSLMF_VOIDTYPE(int t_TYPE::*)> : Is_Empty_Class_Imp<t_TYPE>::type { // Private class: do not use outside of 'bslmf_isempty' component. // Implementation of 'bsl::is_empty'. This partial specialization derives // from the nested 'type' member of the 'Is_Empty_Class_Imp' meta-function, // which must be 'true_type' if (the template parameter) 't_TYPE' is an // empty class, and 'false_type' otherwise. }; // ======================= // struct is_empty (C++03) // ======================= template <class t_TYPE> struct is_empty : Is_Empty_Imp<t_TYPE>::type { // This 'struct' is a meta-function to determine whether the (template // parameter) 't_TYPE' is an empty class type. 'is_empty' inherits from // 'true_type' if 't_TYPE' is a 'class' or 'struct' with no non-static data // members other than bit-fields of length 0, no virtual member functions, // no virtual base classes, and no base class 'B' for which // 'is_empty<B>::value' is 'false'; otherwise 'is_empty' inherits from // 'false_type'. Note that this meta-function will fail to compile for a // union that is the same size as an empty class in C++03. }; template <class t_TYPE> struct is_empty<const t_TYPE> : Is_Empty_Imp<t_TYPE>::type { }; template <class t_TYPE> struct is_empty<volatile t_TYPE> : Is_Empty_Imp<t_TYPE>::type { }; template <class t_TYPE> struct is_empty<const volatile t_TYPE> : Is_Empty_Imp<t_TYPE>::type { }; // Partial specializations reduce the total number of template // instantiations when cv-qualified types are involved. #endif // defined(BSLS_ISEMPTY_USE_NATIVE_TRAIT) } // close namespace bsl #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 ----------------------------------