diff --git a/doc/atomic.qbk b/doc/atomic.qbk index c0ea5ac..880fab5 100644 --- a/doc/atomic.qbk +++ b/doc/atomic.qbk @@ -437,26 +437,19 @@ of the various ordering constraints. [^boost::atomic<['T]>] provides methods for atomically accessing variables of a suitable type [^['T]]. The type is suitable if -it is /trivial/ (3.9/9 \[basic.types\]). Following are +it is /trivially copyable/ (3.9/9 \[basic.types\]). Following are examples of the types compatible with this requirement: * a scalar type (e.g. integer, boolean, enum or pointer type) * a [^class] or [^struct] that has no non-trivial copy or move - constructors or assignment operators, has a trivial default - constructor and destructor, and that is comparable via [^memcmp]. + constructors or assignment operators, has a trivial destructor, + and that is comparable via [^memcmp]. Note that classes with virtual functions or virtual base classes do not satisfy the requirements. Also be warned that structures with "padding" between data members may compare non-equal via [^memcmp] even though all members are equal. -[note The C++11 standard has slightly more relaxed requirements on the -compatible types. `std::atomic` only requires the type to be -/trivially copyable/, which allows the default constructor to be -non-trivial. The additional requirement on the default constructor -is a property of this implementation. For most conventional uses -of `atomic` the difference is not essential.] - [section:interface_atomic_generic [^boost::atomic<['T]>] template class] All atomic objects supports the following operations: diff --git a/include/boost/atomic/detail/atomic_template.hpp b/include/boost/atomic/detail/atomic_template.hpp index 812b2a4..4fd6d79 100644 --- a/include/boost/atomic/detail/atomic_template.hpp +++ b/include/boost/atomic/detail/atomic_template.hpp @@ -333,8 +333,7 @@ protected: storage_type m_storage; public: - BOOST_DEFAULTED_FUNCTION(base_atomic(), {}) - BOOST_FORCEINLINE explicit base_atomic(value_type const& v) BOOST_NOEXCEPT : m_storage(atomics::detail::union_cast< storage_type >(v)) + BOOST_FORCEINLINE explicit base_atomic(value_type const& v = value_type()) BOOST_NOEXCEPT : m_storage(atomics::detail::memcpy_cast< storage_type >(v)) { } @@ -344,7 +343,7 @@ public: BOOST_ASSERT(order != memory_order_acquire); BOOST_ASSERT(order != memory_order_acq_rel); - operations::store(m_storage, atomics::detail::union_cast< storage_type >(v), order); + operations::store(m_storage, atomics::detail::memcpy_cast< storage_type >(v), order); } BOOST_FORCEINLINE value_type load(memory_order order = memory_order_seq_cst) const volatile BOOST_NOEXCEPT @@ -352,12 +351,12 @@ public: BOOST_ASSERT(order != memory_order_release); BOOST_ASSERT(order != memory_order_acq_rel); - return atomics::detail::union_cast< value_type >(operations::load(m_storage, order)); + return atomics::detail::memcpy_cast< value_type >(operations::load(m_storage, order)); } BOOST_FORCEINLINE value_type exchange(value_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT { - return atomics::detail::union_cast< value_type >(operations::exchange(m_storage, atomics::detail::union_cast< storage_type >(v), order)); + return atomics::detail::memcpy_cast< value_type >(operations::exchange(m_storage, atomics::detail::memcpy_cast< storage_type >(v), order)); } BOOST_FORCEINLINE bool compare_exchange_strong(value_type& expected, value_type desired, memory_order success_order, memory_order failure_order) volatile BOOST_NOEXCEPT @@ -366,9 +365,9 @@ public: BOOST_ASSERT(failure_order != memory_order_acq_rel); BOOST_ASSERT(cas_failure_order_must_not_be_stronger_than_success_order(success_order, failure_order)); - storage_type old_value = atomics::detail::union_cast< storage_type >(expected); - const bool res = operations::compare_exchange_strong(m_storage, old_value, atomics::detail::union_cast< storage_type >(desired), success_order, failure_order); - expected = atomics::detail::union_cast< value_type >(old_value); + storage_type old_value = atomics::detail::memcpy_cast< storage_type >(expected); + const bool res = operations::compare_exchange_strong(m_storage, old_value, atomics::detail::memcpy_cast< storage_type >(desired), success_order, failure_order); + expected = atomics::detail::memcpy_cast< value_type >(old_value); return res; } @@ -383,9 +382,9 @@ public: BOOST_ASSERT(failure_order != memory_order_acq_rel); BOOST_ASSERT(cas_failure_order_must_not_be_stronger_than_success_order(success_order, failure_order)); - storage_type old_value = atomics::detail::union_cast< storage_type >(expected); - const bool res = operations::compare_exchange_weak(m_storage, old_value, atomics::detail::union_cast< storage_type >(desired), success_order, failure_order); - expected = atomics::detail::union_cast< value_type >(old_value); + storage_type old_value = atomics::detail::memcpy_cast< storage_type >(expected); + const bool res = operations::compare_exchange_weak(m_storage, old_value, atomics::detail::memcpy_cast< storage_type >(desired), success_order, failure_order); + expected = atomics::detail::memcpy_cast< value_type >(old_value); return res; } diff --git a/include/boost/atomic/detail/casts.hpp b/include/boost/atomic/detail/casts.hpp index 50036cd..db28bc2 100644 --- a/include/boost/atomic/detail/casts.hpp +++ b/include/boost/atomic/detail/casts.hpp @@ -16,6 +16,7 @@ #ifndef BOOST_ATOMIC_DETAIL_CASTS_HPP_INCLUDED_ #define BOOST_ATOMIC_DETAIL_CASTS_HPP_INCLUDED_ +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -39,6 +40,23 @@ BOOST_FORCEINLINE To union_cast(From const& from) BOOST_NOEXCEPT return caster.as_to; } +template< typename To, typename From > +BOOST_FORCEINLINE To memcpy_cast(From const& from) BOOST_NOEXCEPT +{ + struct + { + To to; + } + value = {}; + std::memcpy + ( + &reinterpret_cast< char& >(value.to), + &reinterpret_cast< const char& >(from), + (sizeof(From) < sizeof(To) ? sizeof(From) : sizeof(To)) + ); + return value.to; +} + } // namespace detail } // namespace atomics } // namespace boost diff --git a/test/api_test_helpers.hpp b/test/api_test_helpers.hpp index dfd0163..107e7d7 100644 --- a/test/api_test_helpers.hpp +++ b/test/api_test_helpers.hpp @@ -344,4 +344,30 @@ test_large_struct_api(void) test_base_operators(a, b, c); } +struct test_struct_with_ctor { + typedef unsigned int value_type; + value_type i; + test_struct_with_ctor() : i(0x01234567) {} + inline bool operator==(const test_struct_with_ctor & c) const {return i == c.i;} + inline bool operator!=(const test_struct_with_ctor & c) const {return i != c.i;} +}; + +static void +test_struct_with_ctor_api(void) +{ + { + test_struct_with_ctor s; + boost::atomic sa; + // Check that the default constructor was called + BOOST_CHECK( sa.load() == s ); + } + + test_struct_with_ctor a, b, c; + a.i = 1; + b.i = 2; + c.i = 3; + + test_base_operators(a, b, c); +} + #endif diff --git a/test/fallback_api.cpp b/test/fallback_api.cpp index 54e12d1..b767856 100644 --- a/test/fallback_api.cpp +++ b/test/fallback_api.cpp @@ -51,5 +51,9 @@ int test_main(int, char *[]) test_large_struct_api(); + // Test that boost::atomic only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api(); + return 0; } diff --git a/test/native_api.cpp b/test/native_api.cpp index 4098e45..ddcc0f6 100644 --- a/test/native_api.cpp +++ b/test/native_api.cpp @@ -62,5 +62,9 @@ int test_main(int, char *[]) test_large_struct_api(); + // Test that boost::atomic only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api(); + return 0; }