From 0ae5cebc7fefda545eb68ed02a67e064c57739d4 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 25 May 2020 04:44:29 +0300 Subject: [PATCH 1/4] Add value_init_test2.cpp, which tests the cases from value_init_workaround_test --- test/Jamfile.v2 | 1 + test/value_init_test2.cpp | 169 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 test/value_init_test2.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 55fdd44..07d771a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -36,6 +36,7 @@ run string_view_test2.cpp ; run string_view_test_io.cpp ; run value_init_test.cpp ; +run value_init_test2.cpp ; run value_init_workaround_test.cpp ; run initialized_test.cpp ; compile-fail value_init_test_fail1.cpp ; diff --git a/test/value_init_test2.cpp b/test/value_init_test2.cpp new file mode 100644 index 0000000..e3438cc --- /dev/null +++ b/test/value_init_test2.cpp @@ -0,0 +1,169 @@ +// Copyright 2010, Niels Dekker. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Test program for the boost::value_initialized workaround. +// +// 17 June 2010 (Created) Niels Dekker + +#include +#include +#include +#include + +namespace +{ + struct empty_struct + { + }; + + // A POD aggregate struct derived from an empty struct. + // Similar to struct Foo1 from Microsoft Visual C++ bug report 484295, + // "VC++ does not value-initialize members of derived classes without + // user-declared constructor", reported in 2009 by Sylvester Hesp: + // https://connect.microsoft.com/VisualStudio/feedback/details/484295 + struct derived_struct: empty_struct + { + int data; + }; + + bool is_value_initialized(const derived_struct& arg) + { + return arg.data == 0; + } + + + class virtual_destructor_holder + { + public: + int i; + virtual ~virtual_destructor_holder() + { + } + }; + + bool is_value_initialized(const virtual_destructor_holder& arg) + { + return arg.i == 0; + } + + // Equivalent to the Stats class from GCC Bug 33916, + // "Default constructor fails to initialize array members", reported in 2007 by + // Michael Elizabeth Chastain: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 + // and fixed for GCC 4.2.4. + class private_int_array_pair + { + friend bool is_value_initialized(const private_int_array_pair& arg); + private: + int first[12]; + int second[12]; + }; + + bool is_value_initialized(const private_int_array_pair& arg) + { + for ( unsigned i = 0; i < 12; ++i) + { + if ( (arg.first[i] != 0) || (arg.second[i] != 0) ) + { + return false; + } + } + return true; + } + + struct int_pair_struct + { + int first; + int second; + }; + + typedef int int_pair_struct::*ptr_to_member_type; + + struct ptr_to_member_struct + { + ptr_to_member_type data; + }; + + bool is_value_initialized(const ptr_to_member_struct& arg) + { + return arg.data == 0; + } + + template + bool is_value_initialized(const T(& arg)[2]) + { + return + is_value_initialized(arg[0]) && + is_value_initialized(arg[1]); + } + + template + bool is_value_initialized(const boost::value_initialized& arg) + { + return is_value_initialized(arg.data()); + } + + // Returns zero when the specified object is value-initializated, and one otherwise. + // Prints a message to standard output if the value-initialization has failed. + template + unsigned failed_to_value_initialized(const T& object, const char *const object_name) + { + if ( is_value_initialized(object) ) + { + return 0u; + } + else + { + std::cout << "Note: Failed to value-initialize " << object_name << '.' << std::endl; + return 1u; + } + } + +// A macro that passed both the name and the value of the specified object to +// the function above here. +#define FAILED_TO_VALUE_INITIALIZE(value) failed_to_value_initialized(value, #value) + + // Equivalent to the dirty_stack() function from GCC Bug 33916, + // "Default constructor fails to initialize array members", reported in 2007 by + // Michael Elizabeth Chastain: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 + void dirty_stack() + { + unsigned char array_on_stack[4096]; + for (unsigned i = 0; i < sizeof(array_on_stack); ++i) + { + array_on_stack[i] = 0x11; + } + } + +} + + +int main() +{ +#ifdef BOOST_DETAIL_VALUE_INIT_WORKAROUND_SUGGESTED + + std::cout << "BOOST_DETAIL_VALUE_INIT_WORKAROUND_SUGGESTED is defined.\n\n"; + +#endif + + dirty_stack(); + + BOOST_TEST( is_value_initialized( boost::value_initialized() ) ); + BOOST_TEST( is_value_initialized( boost::value_initialized() ) ); + BOOST_TEST( is_value_initialized( boost::value_initialized() ) ); + +#if !BOOST_WORKAROUND( BOOST_MSVC, BOOST_TESTED_AT(1925) ) + + // Null pointers to data members are represented as -1 in MSVC, but + // value initialization sets them to all zero. The workaround employed + // by value_initialized<> is to memset the storage to all zero, which + // doesn't help. + + BOOST_TEST( is_value_initialized( boost::value_initialized() ) ); + +#endif + + return boost::report_errors(); +} From 25cb7aa122ab39f0cc8664b1b92e6e6061e4ec49 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 25 May 2020 05:09:36 +0300 Subject: [PATCH 2/4] Use a base class to apply the memset workaround to avoid dependency on TypeTraits --- include/boost/utility/value_init.hpp | 104 +++++++++------------------ test/value_init_test.cpp | 1 + 2 files changed, 35 insertions(+), 70 deletions(-) diff --git a/include/boost/utility/value_init.hpp b/include/boost/utility/value_init.hpp index 7bd7943..2490b48 100644 --- a/include/boost/utility/value_init.hpp +++ b/include/boost/utility/value_init.hpp @@ -1,4 +1,5 @@ // (C) Copyright 2002-2008, Fernando Luis Cacciola Carballal. +// Copyright 2020 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -21,15 +22,10 @@ // issues, by clearing the bytes of T, before constructing the T object it // contains. More details on these issues are at libs/utility/value_init.htm -#include #include // For BOOST_NO_COMPLETE_VALUE_INITIALIZATION. -#include -#include -#include -#include #include #include -#include +#include #ifdef BOOST_MSVC #pragma warning(push) @@ -39,6 +35,8 @@ // It is safe to ignore the following MSVC warning, which may pop up when T is // a const type: "warning C4512: assignment operator could not be generated". #pragma warning(disable: 4512) +// C4355: 'this' : used in base member initializer list +#pragma warning(disable: 4355) #endif #ifdef BOOST_NO_COMPLETE_VALUE_INITIALIZATION @@ -60,92 +58,58 @@ namespace boost { +namespace detail { + + struct zero_init + { + zero_init() + { + } + + zero_init( void * p, std::size_t n ) + { + std::memset( p, 0, n ); + } + }; + +} // namespace detail + template class initialized +#if BOOST_DETAIL_VALUE_INIT_WORKAROUND + : detail::zero_init +#endif { - private : - struct wrapper - { -#if !BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x592)) - typename -#endif - remove_const::type data; + private: - BOOST_GPU_ENABLED - wrapper() - : - data() - { - } - - BOOST_GPU_ENABLED - wrapper(T const & arg) - : - data(arg) - { - } - }; - - mutable -#if !BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x592)) - typename -#endif - aligned_storage::value>::type x; - - BOOST_GPU_ENABLED - wrapper * wrapper_address() const - { - return static_cast( static_cast(&x)); - } + T data_; public : BOOST_GPU_ENABLED - initialized() - { + initialized(): #if BOOST_DETAIL_VALUE_INIT_WORKAROUND - std::memset(&x, 0, sizeof(x)); + zero_init( this, sizeof(*this) ), #endif - new (wrapper_address()) wrapper(); + data_() + { } BOOST_GPU_ENABLED - initialized(initialized const & arg) + explicit initialized(T const & arg): data_( arg ) { - new (wrapper_address()) wrapper( static_cast(*(arg.wrapper_address()))); - } - - BOOST_GPU_ENABLED - explicit initialized(T const & arg) - { - new (wrapper_address()) wrapper(arg); - } - - BOOST_GPU_ENABLED - initialized & operator=(initialized const & arg) - { - // Assignment is only allowed when T is non-const. - BOOST_STATIC_ASSERT( ! is_const::value ); - *wrapper_address() = static_cast(*(arg.wrapper_address())); - return *this; - } - - BOOST_GPU_ENABLED - ~initialized() - { - wrapper_address()->wrapper::~wrapper(); } BOOST_GPU_ENABLED T const & data() const { - return wrapper_address()->data; + return data_; } BOOST_GPU_ENABLED T& data() { - return wrapper_address()->data; + return data_; } BOOST_GPU_ENABLED @@ -157,13 +121,13 @@ class initialized BOOST_GPU_ENABLED operator T const &() const { - return wrapper_address()->data; + return data_; } BOOST_GPU_ENABLED operator T&() { - return wrapper_address()->data; + return data_; } } ; diff --git a/test/value_init_test.cpp b/test/value_init_test.cpp index c4ed281..8cfb72e 100644 --- a/test/value_init_test.cpp +++ b/test/value_init_test.cpp @@ -22,6 +22,7 @@ #endif #include +#include // // Sample POD type From 8faf831bd179d7e47eced117f746e5616c8ce85a Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 25 May 2020 23:50:35 +0300 Subject: [PATCH 3/4] memset data_ instead of *this --- include/boost/utility/value_init.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/boost/utility/value_init.hpp b/include/boost/utility/value_init.hpp index 2490b48..418a214 100644 --- a/include/boost/utility/value_init.hpp +++ b/include/boost/utility/value_init.hpp @@ -35,8 +35,6 @@ // It is safe to ignore the following MSVC warning, which may pop up when T is // a const type: "warning C4512: assignment operator could not be generated". #pragma warning(disable: 4512) -// C4355: 'this' : used in base member initializer list -#pragma warning(disable: 4355) #endif #ifdef BOOST_NO_COMPLETE_VALUE_INITIALIZATION @@ -89,7 +87,7 @@ class initialized BOOST_GPU_ENABLED initialized(): #if BOOST_DETAIL_VALUE_INIT_WORKAROUND - zero_init( this, sizeof(*this) ), + zero_init( &const_cast< char& >( reinterpret_cast( data_ ) ), sizeof( data_ ) ), #endif data_() { From 688628f764fc92279efbaf8fb24ee5f59c00c4cb Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Tue, 26 May 2020 00:39:51 +0300 Subject: [PATCH 4/4] Add test/value_init_test3 --- test/Jamfile.v2 | 1 + test/value_init_test3.cpp | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 test/value_init_test3.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 07d771a..fdaef72 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -37,6 +37,7 @@ run string_view_test_io.cpp ; run value_init_test.cpp ; run value_init_test2.cpp ; +run value_init_test3.cpp ; run value_init_workaround_test.cpp ; run initialized_test.cpp ; compile-fail value_init_test_fail1.cpp ; diff --git a/test/value_init_test3.cpp b/test/value_init_test3.cpp new file mode 100644 index 0000000..d90834d --- /dev/null +++ b/test/value_init_test3.cpp @@ -0,0 +1,40 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#if __cplusplus >= 201103L || ( defined(BOOST_MSVC) && BOOST_MSVC >= 1900 ) + +struct X +{ + int a; + char b; +}; + +struct Y: boost::value_initialized +{ + char c = 42; +}; + +int main() +{ + Y y; + + BOOST_TEST_EQ( y.data().a, 0 ); + BOOST_TEST_EQ( y.data().b, 0 ); + BOOST_TEST_EQ( y.c, 42 ); + + return boost::report_errors(); +} + +#else + +BOOST_PRAGMA_MESSAGE( "Skipping test because compiler doesn't support in-class member initializers" ) + +int main() {} + +#endif