atomic/test/api_test_helpers.hpp
Andrey Semashev ea5911a413 Added tests for arithmetic and bitwise ops with immediate constants.
This test verifies arithmetic and bitwise (logic) operations with
immediate constant arguments, which may affect instruction choice
in the operations. In particular, the test verifies that bug
https://github.com/boostorg/atomic/issues/41 is fixed.
2020-11-20 13:58:16 +03:00

1526 lines
46 KiB
C++

// Copyright (c) 2011 Helge Bahmann
// Copyright (c) 2017 - 2020 Andrey Semashev
//
// 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)
#ifndef BOOST_ATOMIC_API_TEST_HELPERS_HPP
#define BOOST_ATOMIC_API_TEST_HELPERS_HPP
#include <boost/atomic.hpp>
#include <cstddef>
#include <cstring>
#include <cstdlib>
#include <limits>
#include <vector>
#include <iostream>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/type.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_signed.hpp>
#include <boost/type_traits/is_unsigned.hpp>
#include <boost/type_traits/make_signed.hpp>
#include <boost/type_traits/make_unsigned.hpp>
#include <boost/type_traits/conditional.hpp>
#include "lightweight_test_stream.hpp"
#include "value_with_epsilon.hpp"
#include "atomic_wrapper.hpp"
const unsigned int max_weak_cas_loops = 1000;
template< typename T >
struct is_atomic :
public boost::false_type
{
};
template< typename T >
struct is_atomic< boost::atomic< T > > :
public boost::true_type
{
};
template< typename T >
struct is_atomic< boost::ipc_atomic< T > > :
public boost::true_type
{
};
template< typename T >
struct is_atomic_ref :
public boost::false_type
{
};
template< typename T >
struct is_atomic_ref< boost::atomic_ref< T > > :
public boost::true_type
{
};
template< typename T >
struct is_atomic_ref< boost::ipc_atomic_ref< T > > :
public boost::true_type
{
};
template< typename Flag >
inline void test_flag_api(void)
{
#ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT
Flag f = BOOST_ATOMIC_FLAG_INIT;
#else
Flag f;
#endif
BOOST_TEST( !f.test() );
BOOST_TEST( !f.test_and_set() );
BOOST_TEST( f.test() );
BOOST_TEST( f.test_and_set() );
BOOST_TEST( f.test() );
f.clear();
BOOST_TEST( !f.test() );
BOOST_TEST( !f.test_and_set() );
}
template< typename T >
inline typename boost::enable_if< is_atomic< T > >::type test_atomic_type_traits(boost::type< T >)
{
BOOST_TEST_GE(sizeof(T), sizeof(typename T::value_type));
}
template< typename T >
inline typename boost::enable_if< is_atomic_ref< T > >::type test_atomic_type_traits(boost::type< T >)
{
if (T::is_always_lock_free)
{
BOOST_TEST_GE(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
}
else
{
// Lock-based implementation should not require alignment higher than alignof(T)
BOOST_TEST_EQ(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
}
}
template< template< typename > class Wrapper, typename T >
void test_base_operators(T value1, T value2, T value3)
{
test_atomic_type_traits(boost::type< typename Wrapper<T>::atomic_type >());
// explicit load/store
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
BOOST_TEST_EQ( a.load(), value1 );
}
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.store(value2);
BOOST_TEST_EQ( a.load(), value2 );
}
// overloaded assignment/conversion
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
BOOST_TEST( value1 == a );
}
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a = value2;
BOOST_TEST( value2 == a );
}
// exchange-type operators
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.exchange(value2);
BOOST_TEST_EQ( a.load(), value2 );
BOOST_TEST_EQ( n, value1 );
}
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T expected = value1;
bool success = a.compare_exchange_strong(expected, value3);
BOOST_TEST( success );
BOOST_TEST_EQ( a.load(), value3 );
BOOST_TEST_EQ( expected, value1 );
}
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T expected = value2;
bool success = a.compare_exchange_strong(expected, value3);
BOOST_TEST( !success );
BOOST_TEST_EQ( a.load(), value1 );
BOOST_TEST_EQ( expected, value1 );
}
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T expected;
unsigned int loops = 0;
bool success = false;
do
{
expected = value1;
success = a.compare_exchange_weak(expected, value3);
++loops;
}
while (!success && loops < max_weak_cas_loops);
BOOST_TEST( success );
BOOST_TEST_EQ( a.load(), value3 );
BOOST_TEST_EQ( expected, value1 );
}
{
Wrapper<T> wrapper(value1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T expected;
unsigned int loops = 0;
bool success = false;
do
{
expected = value2;
success = a.compare_exchange_weak(expected, value3);
if (expected != value2)
break;
++loops;
}
while (!success && loops < max_weak_cas_loops);
BOOST_TEST( !success );
BOOST_TEST_EQ( a.load(), value1 );
BOOST_TEST_EQ( expected, value1 );
}
}
//! Tests whether boost::atomic supports constexpr constructor. Note that boost::atomic_ref (as std::atomic_ref) does not support constexpr constructor.
template< typename T >
void test_constexpr_ctor()
{
#ifndef BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_UNION_INIT
constexpr T value(0);
constexpr boost::atomic<T> tester(value);
BOOST_TEST( tester == value );
#endif
}
//! The type traits provides max and min values of type D that can be added/subtracted to T(0) without signed overflow
template< typename T, typename D, bool IsSigned = boost::is_signed< D >::value >
struct distance_limits
{
//! Difference type D promoted to the width of type T
typedef typename boost::conditional<
IsSigned,
boost::make_signed< T >,
boost::make_unsigned< T >
>::type::type promoted_difference_type;
static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return (std::numeric_limits< D >::min)();
}
static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return (std::numeric_limits< D >::max)();
}
};
#if defined(BOOST_MSVC)
#pragma warning(push)
// 'static_cast': truncation of constant value. There is no actual truncation happening because
// the cast is only performed if the value fits in the range of the result.
#pragma warning(disable: 4309)
#endif
template< typename T, typename D >
struct distance_limits< T*, D, true >
{
//! Difference type D promoted to the width of type T
typedef std::ptrdiff_t promoted_difference_type;
static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::min)() / static_cast< std::ptrdiff_t >(sizeof(T));
const D diff = (std::numeric_limits< D >::min)();
// Both values are negative. Return the closest value to zero.
return diff < ptrdiff ? static_cast< D >(ptrdiff) : diff;
}
static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::max)() / static_cast< std::ptrdiff_t >(sizeof(T));
const D diff = (std::numeric_limits< D >::max)();
// Both values are positive. Return the closest value to zero.
return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff;
}
};
template< typename T, typename D >
struct distance_limits< T*, D, false >
{
//! Difference type D promoted to the width of type T
typedef std::size_t promoted_difference_type;
static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return (std::numeric_limits< D >::min)();
}
static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
const std::size_t ptrdiff = static_cast< std::size_t >((std::numeric_limits< std::ptrdiff_t >::max)()) / sizeof(T);
const D diff = (std::numeric_limits< D >::max)();
return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff;
}
};
#if defined(BOOST_HAS_INT128)
// At least libstdc++ does not specialize std::numeric_limits for __int128 in strict mode (i.e. with GNU extensions disabled).
// So we have to specialize the limits ourself. We assume two's complement signed representation.
template< typename T, bool IsSigned >
struct distance_limits< T, boost::int128_type, IsSigned >
{
//! Difference type D promoted to the width of type T
typedef boost::int128_type promoted_difference_type;
static boost::int128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return -(max)() - 1;
}
static boost::int128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return static_cast< boost::int128_type >((~static_cast< boost::uint128_type >(0u)) >> 1);
}
};
template< typename T, bool IsSigned >
struct distance_limits< T, boost::uint128_type, IsSigned >
{
//! Difference type D promoted to the width of type T
typedef boost::uint128_type promoted_difference_type;
static boost::uint128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return 0u;
}
static boost::uint128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
{
return ~static_cast< boost::uint128_type >(0u);
}
};
#endif // defined(BOOST_HAS_INT128)
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
#if defined(BOOST_MSVC)
#pragma warning(push)
// unary minus operator applied to unsigned type, result still unsigned
#pragma warning(disable: 4146)
#endif
template< template< typename > class Wrapper, typename T, typename D, typename AddType >
void test_additive_operators_with_type_and_test()
{
#if defined(UBSAN)
// clang UBSAN flags this test when AddType is a pointer as it considers subtracting from a null pointer (zero_add) an UB
if (boost::is_pointer< AddType >::value)
return;
#endif
// Note: This set of tests is extracted to a separate function because otherwise MSVC-10 for x64 generates broken code
typedef typename distance_limits< T, D >::promoted_difference_type promoted_difference_type;
typedef typename boost::make_unsigned< promoted_difference_type >::type unsigned_promoted_difference_type;
const T zero_value = 0;
const D zero_diff = 0;
const D one_diff = 1;
const AddType zero_add = 0;
{
Wrapper<T> wrapper(zero_value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.add_and_test(zero_diff);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), zero_value );
f = a.add_and_test(one_diff);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(zero_add + one_diff) );
}
{
Wrapper<T> wrapper(zero_value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.add_and_test((distance_limits< T, D >::max)());
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::max)()) );
}
{
Wrapper<T> wrapper(zero_value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.add_and_test((distance_limits< T, D >::min)());
BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) );
BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::min)()) );
}
{
Wrapper<T> wrapper(zero_value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.sub_and_test(zero_diff);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), zero_value );
f = a.sub_and_test(one_diff);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(zero_add - one_diff) );
}
{
Wrapper<T> wrapper(zero_value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.sub_and_test((distance_limits< T, D >::max)());
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(zero_add - (distance_limits< T, D >::max)()) );
}
{
Wrapper<T> wrapper(zero_value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.sub_and_test((distance_limits< T, D >::min)());
BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) );
// Be very careful as to not cause signed overflow on negation
unsigned_promoted_difference_type umin = static_cast< unsigned_promoted_difference_type >(
static_cast< promoted_difference_type >((distance_limits< T, D >::min)()));
umin = -umin;
promoted_difference_type neg_min;
std::memcpy(&neg_min, &umin, sizeof(neg_min));
BOOST_TEST_EQ( a.load(), T(zero_add + neg_min) );
}
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
template< template< typename > class Wrapper, typename T, typename D, typename AddType >
void test_additive_operators_with_type(T value, D delta)
{
/* note: the tests explicitly cast the result of any addition
to the type to be tested to force truncation of the result to
the correct range in case of overflow */
// explicit add/sub
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_add(delta);
BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_sub(delta);
BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
BOOST_TEST_EQ( n, value );
}
// add/sub with an immediate
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_add(1);
BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_sub(1);
BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_add(76);
BOOST_TEST_EQ( a.load(), T((AddType)value + 76) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_sub(76);
BOOST_TEST_EQ( a.load(), T((AddType)value - 76) );
BOOST_TEST_EQ( n, value );
}
if ((std::numeric_limits< D >::max)() >= 4097)
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_add((D)4097);
BOOST_TEST_EQ( a.load(), T((AddType)value + (D)4097) );
BOOST_TEST_EQ( n, value );
}
if ((std::numeric_limits< D >::max)() >= 4097)
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_sub((D)4097);
BOOST_TEST_EQ( a.load(), T((AddType)value - (D)4097) );
BOOST_TEST_EQ( n, value );
}
// overloaded modify/assign
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a += delta);
BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
BOOST_TEST_EQ( n, T((AddType)value + delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a -= delta);
BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
BOOST_TEST_EQ( n, T((AddType)value - delta) );
}
// overloaded increment/decrement
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a++;
BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = ++a;
BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
BOOST_TEST_EQ( n, T((AddType)value + 1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a--;
BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = --a;
BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
BOOST_TEST_EQ( n, T((AddType)value - 1) );
}
// Operations returning the actual resulting value
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.add(delta);
BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
BOOST_TEST_EQ( n, T((AddType)value + delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.sub(delta);
BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
BOOST_TEST_EQ( n, T((AddType)value - delta) );
}
// The same with an immediate
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.add(1);
BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
BOOST_TEST_EQ( n, T((AddType)value + 1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.sub(1);
BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
BOOST_TEST_EQ( n, T((AddType)value - 1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.add(76);
BOOST_TEST_EQ( a.load(), T((AddType)value + 76) );
BOOST_TEST_EQ( n, T((AddType)value + 76) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.sub(76);
BOOST_TEST_EQ( a.load(), T((AddType)value - 76) );
BOOST_TEST_EQ( n, T((AddType)value - 76) );
}
if ((std::numeric_limits< D >::max)() >= 4097)
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.add((D)4097);
BOOST_TEST_EQ( a.load(), T((AddType)value + (D)4097) );
BOOST_TEST_EQ( n, T((AddType)value + (D)4097) );
}
if ((std::numeric_limits< D >::max)() >= 4097)
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.sub((D)4097);
BOOST_TEST_EQ( a.load(), T((AddType)value - (D)4097) );
BOOST_TEST_EQ( n, T((AddType)value - (D)4097) );
}
// Opaque operations
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_add(delta);
BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_sub(delta);
BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
}
// The same with an immediate
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_add(1);
BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_sub(1);
BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_add(76);
BOOST_TEST_EQ( a.load(), T((AddType)value + 76) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_sub(76);
BOOST_TEST_EQ( a.load(), T((AddType)value - 76) );
}
if ((std::numeric_limits< D >::max)() >= 4097)
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_add((D)4097);
BOOST_TEST_EQ( a.load(), T((AddType)value + (D)4097) );
}
if ((std::numeric_limits< D >::max)() >= 4097)
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_sub((D)4097);
BOOST_TEST_EQ( a.load(), T((AddType)value - (D)4097) );
}
// Modify and test operations
test_additive_operators_with_type_and_test< Wrapper, T, D, AddType >();
}
template< template< typename > class Wrapper, typename T, typename D >
void test_additive_operators(T value, D delta)
{
test_additive_operators_with_type< Wrapper, T, D, T >(value, delta);
}
template< template< typename > class Wrapper, typename T >
void test_negation()
{
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_negate();
BOOST_TEST_EQ( a.load(), (T)-1 );
BOOST_TEST_EQ( n, (T)1 );
n = a.fetch_negate();
BOOST_TEST_EQ( a.load(), (T)1 );
BOOST_TEST_EQ( n, (T)-1 );
}
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.negate();
BOOST_TEST_EQ( a.load(), (T)-1 );
BOOST_TEST_EQ( n, (T)-1 );
n = a.negate();
BOOST_TEST_EQ( a.load(), (T)1 );
BOOST_TEST_EQ( n, (T)1 );
}
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_negate();
BOOST_TEST_EQ( a.load(), (T)-1 );
a.opaque_negate();
BOOST_TEST_EQ( a.load(), (T)1 );
}
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.negate_and_test();
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), (T)-1 );
f = a.negate_and_test();
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), (T)1 );
}
{
Wrapper<T> wrapper((T)0);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.negate_and_test();
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), (T)0 );
}
}
template< template< typename > class Wrapper, typename T >
void test_additive_wrap(T value)
{
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_add(1) + (T)1;
BOOST_TEST_EQ( a.load(), n );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_sub(1) - (T)1;
BOOST_TEST_EQ( a.load(), n );
}
}
template< template< typename > class Wrapper, typename T >
void test_bit_operators(T value, T delta)
{
// explicit and/or/xor
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_and(delta);
BOOST_TEST_EQ( a.load(), T(value & delta) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_or(delta);
BOOST_TEST_EQ( a.load(), T(value | delta) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_xor(delta);
BOOST_TEST_EQ( a.load(), T(value ^ delta) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_complement();
BOOST_TEST_EQ( a.load(), T(~value) );
BOOST_TEST_EQ( n, value );
}
// and/or/xor with an immediate. The immediates below are chosen to either be encodable in an instruction or not for various target architectures.
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_and((T)1);
BOOST_TEST_EQ( a.load(), T(value & (T)1) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_or((T)1);
BOOST_TEST_EQ( a.load(), T(value | (T)1) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_xor((T)1);
BOOST_TEST_EQ( a.load(), T(value ^ (T)1) );
BOOST_TEST_EQ( n, value );
}
// The following constants are not encodable in AArch64
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_and((T)76);
BOOST_TEST_EQ( a.load(), T(value & (T)76) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_or((T)76);
BOOST_TEST_EQ( a.load(), T(value | (T)76) );
BOOST_TEST_EQ( n, value );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_xor((T)76);
BOOST_TEST_EQ( a.load(), T(value ^ (T)76) );
BOOST_TEST_EQ( n, value );
}
// overloaded modify/assign
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a &= delta);
BOOST_TEST_EQ( a.load(), T(value & delta) );
BOOST_TEST_EQ( n, T(value & delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a |= delta);
BOOST_TEST_EQ( a.load(), T(value | delta) );
BOOST_TEST_EQ( n, T(value | delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a ^= delta);
BOOST_TEST_EQ( a.load(), T(value ^ delta) );
BOOST_TEST_EQ( n, T(value ^ delta) );
}
// Operations returning the actual resulting value
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_and(delta);
BOOST_TEST_EQ( a.load(), T(value & delta) );
BOOST_TEST_EQ( n, T(value & delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_or(delta);
BOOST_TEST_EQ( a.load(), T(value | delta) );
BOOST_TEST_EQ( n, T(value | delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_xor(delta);
BOOST_TEST_EQ( a.load(), T(value ^ delta) );
BOOST_TEST_EQ( n, T(value ^ delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_complement();
BOOST_TEST_EQ( a.load(), T(~value) );
BOOST_TEST_EQ( n, T(~value) );
}
// The same with an immediate
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_and((T)1);
BOOST_TEST_EQ( a.load(), T(value & (T)1) );
BOOST_TEST_EQ( n, T(value & (T)1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_or((T)1);
BOOST_TEST_EQ( a.load(), T(value | (T)1) );
BOOST_TEST_EQ( n, T(value | (T)1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_xor((T)1);
BOOST_TEST_EQ( a.load(), T(value ^ (T)1) );
BOOST_TEST_EQ( n, T(value ^ (T)1) );
}
// The following constants are not encodable in AArch64
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_and((T)76);
BOOST_TEST_EQ( a.load(), T(value & (T)76) );
BOOST_TEST_EQ( n, T(value & (T)76) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_or((T)76);
BOOST_TEST_EQ( a.load(), T(value | (T)76) );
BOOST_TEST_EQ( n, T(value | (T)76) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.bitwise_xor((T)76);
BOOST_TEST_EQ( a.load(), T(value ^ (T)76) );
BOOST_TEST_EQ( n, T(value ^ (T)76) );
}
// Opaque operations
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_and(delta);
BOOST_TEST_EQ( a.load(), T(value & delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_or(delta);
BOOST_TEST_EQ( a.load(), T(value | delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_xor(delta);
BOOST_TEST_EQ( a.load(), T(value ^ delta) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_complement();
BOOST_TEST_EQ( a.load(), T(~value) );
}
// The same with an immediate
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_and((T)1);
BOOST_TEST_EQ( a.load(), T(value & (T)1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_or((T)1);
BOOST_TEST_EQ( a.load(), T(value | (T)1) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_xor((T)1);
BOOST_TEST_EQ( a.load(), T(value ^ (T)1) );
}
// The following constants are not encodable in AArch64
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_and((T)76);
BOOST_TEST_EQ( a.load(), T(value & (T)76) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_or((T)76);
BOOST_TEST_EQ( a.load(), T(value | (T)76) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_xor((T)76);
BOOST_TEST_EQ( a.load(), T(value ^ (T)76) );
}
// Modify and test operations
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.and_and_test((T)1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(1) );
f = a.and_and_test((T)0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(0) );
f = a.and_and_test((T)0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(0) );
}
{
Wrapper<T> wrapper((T)0);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.or_and_test((T)0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(0) );
f = a.or_and_test((T)1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(1) );
f = a.or_and_test((T)1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(1) );
}
{
Wrapper<T> wrapper((T)0);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.xor_and_test((T)0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(0) );
f = a.xor_and_test((T)1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(1) );
f = a.xor_and_test((T)1);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(0) );
}
{
Wrapper<T> wrapper((T)0);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.complement_and_test();
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), static_cast< T >(~static_cast< T >(0)) );
f = a.complement_and_test();
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(0) );
}
// Bit test and modify operations
{
Wrapper<T> wrapper((T)42);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.bit_test_and_set(0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(43) );
f = a.bit_test_and_set(1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(43) );
f = a.bit_test_and_set(2);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(47) );
}
{
Wrapper<T> wrapper((T)42);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.bit_test_and_reset(0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(42) );
f = a.bit_test_and_reset(1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(40) );
f = a.bit_test_and_set(2);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(44) );
}
{
Wrapper<T> wrapper((T)42);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
bool f = a.bit_test_and_complement(0);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(43) );
f = a.bit_test_and_complement(1);
BOOST_TEST_EQ( f, true );
BOOST_TEST_EQ( a.load(), T(41) );
f = a.bit_test_and_complement(2);
BOOST_TEST_EQ( f, false );
BOOST_TEST_EQ( a.load(), T(45) );
}
// Test that a runtime value works for the bit index. This is important for asm block constraints.
{
unsigned int runtime_bit_index = std::rand() & 7u;
Wrapper<T> wrapper((T)42);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.bit_test_and_set(runtime_bit_index);
a.bit_test_and_reset(runtime_bit_index);
a.bit_test_and_complement(runtime_bit_index);
}
}
template< template< typename > class Wrapper, typename T >
void do_test_integral_api(boost::false_type)
{
test_base_operators< Wrapper, T >(42, 43, 44);
test_additive_operators< Wrapper, T, T >(42, 17);
test_bit_operators< Wrapper, T >((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL);
/* test for unsigned overflow/underflow */
test_additive_operators< Wrapper, T, T >((T)-1, 1);
test_additive_operators< Wrapper, T, T >(0, 1);
/* test for signed overflow/underflow */
test_additive_operators< Wrapper, T, T >(((T)-1) >> (sizeof(T) * 8 - 1), 1);
test_additive_operators< Wrapper, T, T >(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1);
}
template< template< typename > class Wrapper, typename T >
void do_test_integral_api(boost::true_type)
{
do_test_integral_api< Wrapper, T >(boost::false_type());
test_additive_wrap< Wrapper, T >(0u);
BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u;
test_additive_wrap< Wrapper, T >(all_ones);
BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1;
test_additive_wrap< Wrapper, T >(all_ones ^ max_signed_twos_compl);
test_additive_wrap< Wrapper, T >(max_signed_twos_compl);
}
template< template< typename > class Wrapper, typename T >
inline void test_integral_api(void)
{
do_test_integral_api< Wrapper, T >(boost::is_unsigned<T>());
if (boost::is_signed<T>::value)
test_negation< Wrapper, T >();
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_integral_api(boost::true_type)
{
test_integral_api< Wrapper, T >();
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_integral_api(boost::false_type)
{
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_integral_api(void)
{
test_lock_free_integral_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
}
#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
template< template< typename > class Wrapper, typename T, typename D >
void test_fp_additive_operators(T value, D delta)
{
// explicit add/sub
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_add(delta);
BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
BOOST_TEST_EQ( n, approx(value) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_sub(delta);
BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
BOOST_TEST_EQ( n, approx(value) );
}
// overloaded modify/assign
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a += delta);
BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
BOOST_TEST_EQ( n, approx(T(value + delta)) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = (a -= delta);
BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
BOOST_TEST_EQ( n, approx(T(value - delta)) );
}
// Operations returning the actual resulting value
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.add(delta);
BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
BOOST_TEST_EQ( n, approx(T(value + delta)) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.sub(delta);
BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
BOOST_TEST_EQ( n, approx(T(value - delta)) );
}
// Opaque operations
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_add(delta);
BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
}
{
Wrapper<T> wrapper(value);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_sub(delta);
BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
}
}
template< template< typename > class Wrapper, typename T >
void test_fp_negation()
{
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.fetch_negate();
BOOST_TEST_EQ( a.load(), approx((T)-1) );
BOOST_TEST_EQ( n, approx((T)1) );
n = a.fetch_negate();
BOOST_TEST_EQ( a.load(), approx((T)1) );
BOOST_TEST_EQ( n, approx((T)-1) );
}
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
T n = a.negate();
BOOST_TEST_EQ( a.load(), approx((T)-1) );
BOOST_TEST_EQ( n, approx((T)-1) );
n = a.negate();
BOOST_TEST_EQ( a.load(), approx((T)1) );
BOOST_TEST_EQ( n, approx((T)1) );
}
{
Wrapper<T> wrapper((T)1);
typename Wrapper<T>::atomic_reference_type a = wrapper.a;
a.opaque_negate();
BOOST_TEST_EQ( a.load(), approx((T)-1) );
a.opaque_negate();
BOOST_TEST_EQ( a.load(), approx((T)1) );
}
}
#endif // !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
template< template< typename > class Wrapper, typename T >
void test_floating_point_api(void)
{
// Note: When support for floating point is disabled, even the base operation tests may fail because
// the generic template specialization does not account for garbage in padding bits that are present in some FP types.
#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
test_base_operators< Wrapper, T >(static_cast<T>(42.1), static_cast<T>(43.2), static_cast<T>(44.3));
test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(42.5), static_cast<T>(17.7));
test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(-42.5), static_cast<T>(-17.7));
test_fp_negation< Wrapper, T >();
#endif
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_floating_point_api(boost::true_type)
{
test_floating_point_api< Wrapper, T >();
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_floating_point_api(boost::false_type)
{
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_floating_point_api(void)
{
test_lock_free_floating_point_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
}
template< template< typename > class Wrapper, typename T >
void test_pointer_api(void)
{
std::vector< T > values;
values.resize(5000); // make the vector large enough to accommodate pointer arithmetics in the additive tests
test_base_operators< Wrapper, T* >(&values[0], &values[1], &values[2]);
test_additive_operators< Wrapper, T* >(&values[1], 1);
test_base_operators< Wrapper, void* >(&values[0], &values[1], &values[2]);
#if defined(BOOST_HAS_INTPTR_T)
Wrapper<void*> wrapper_ptr;
typename Wrapper<void*>::atomic_reference_type ptr = wrapper_ptr.a;
Wrapper<boost::intptr_t> wrapper_integral;
typename Wrapper<boost::intptr_t>::atomic_reference_type integral = wrapper_integral.a;
BOOST_TEST_EQ( ptr.is_lock_free(), integral.is_lock_free() );
#endif
}
enum test_enum
{
foo, bar, baz
};
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_pointer_api(boost::true_type)
{
test_pointer_api< Wrapper, T >();
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_pointer_api(boost::false_type)
{
}
template< template< typename > class Wrapper, typename T >
inline void test_lock_free_pointer_api(void)
{
test_lock_free_pointer_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
}
template< template< typename > class Wrapper >
void test_enum_api(void)
{
test_base_operators< Wrapper >(foo, bar, baz);
}
template< template< typename > class Wrapper >
inline void test_lock_free_enum_api(boost::true_type)
{
test_enum_api< Wrapper >();
}
template< template< typename > class Wrapper >
inline void test_lock_free_enum_api(boost::false_type)
{
}
template< template< typename > class Wrapper >
inline void test_lock_free_enum_api(void)
{
test_lock_free_enum_api< Wrapper >(boost::integral_constant< bool, Wrapper< test_enum >::atomic_type::is_always_lock_free >());
}
template< typename T >
struct test_struct
{
typedef T value_type;
value_type i;
inline bool operator==(test_struct const& c) const { return i == c.i; }
inline bool operator!=(test_struct const& c) const { return !operator==(c); }
};
template< typename Char, typename Traits, typename T >
inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct< T > const& s)
{
test_stream << "{" << s.i << "}";
return strm;
}
template< template< typename > class Wrapper, typename T >
void test_struct_api(void)
{
T a = {1}, b = {2}, c = {3};
test_base_operators< Wrapper >(a, b, c);
{
Wrapper<T> wrapper_sa;
typename Wrapper<T>::atomic_reference_type sa = wrapper_sa.a;
Wrapper<typename T::value_type> wrapper_si;
typename Wrapper<typename T::value_type>::atomic_reference_type si = wrapper_si.a;
BOOST_TEST_EQ( sa.is_lock_free(), si.is_lock_free() );
}
}
template< typename T >
struct test_struct_x2
{
typedef T value_type;
value_type i, j;
inline bool operator==(test_struct_x2 const& c) const { return i == c.i && j == c.j; }
inline bool operator!=(test_struct_x2 const& c) const { return !operator==(c); }
};
template< typename Char, typename Traits, typename T >
inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_x2< T > const& s)
{
test_stream << "{" << s.i << ", " << s.j << "}";
return strm;
}
template< template< typename > class Wrapper, typename T >
void test_struct_x2_api(void)
{
T a = {1, 1}, b = {2, 2}, c = {3, 3};
test_base_operators< Wrapper >(a, b, c);
}
struct large_struct
{
unsigned char data[256u];
inline bool operator==(large_struct const& c) const
{
return std::memcmp(data, &c.data, sizeof(data)) == 0;
}
inline bool operator!=(large_struct const& c) const
{
return std::memcmp(data, &c.data, sizeof(data)) != 0;
}
};
template< typename Char, typename Traits >
inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, large_struct const&)
{
strm << "[large_struct]";
return strm;
}
template< template< typename > class Wrapper >
void test_large_struct_api(void)
{
large_struct a = {{1}}, b = {{2}}, c = {{3}};
test_base_operators< Wrapper >(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==(test_struct_with_ctor const& c) const { return i == c.i; }
inline bool operator!=(test_struct_with_ctor const& c) const { return !operator==(c); }
};
template< typename Char, typename Traits >
inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_with_ctor const& s)
{
strm << "{" << s.i << "}";
return strm;
}
template< template< typename > class Wrapper >
void test_struct_with_ctor_api(void)
{
{
test_struct_with_ctor s;
Wrapper<test_struct_with_ctor> wrapper_sa;
typename Wrapper<test_struct_with_ctor>::atomic_reference_type sa = wrapper_sa.a;
// Check that the default constructor was called
BOOST_TEST( sa.load() == s );
}
test_struct_with_ctor a, b, c;
a.i = 1;
b.i = 2;
c.i = 3;
test_base_operators< Wrapper >(a, b, c);
}
#endif