2
0
mirror of https://github.com/boostorg/compat.git synced 2026-01-30 07:42:36 +00:00

7 Commits

Author SHA1 Message Date
Peter Dimov
349fb928b5 Merge pull request #25 from cmazakas/fix/placement-new
Qualify calls to placement new
2026-01-24 12:40:15 +02:00
Christian Mazakas
d6fa246a9c properly qualify calls to placement new
To avoid class-based overload sets for operator new, we qualify our
calls to placement new with `::new` which forces us to pick up the
correct overload when emplacing objects in the small buffer.
2026-01-23 19:38:19 -08:00
Christian Mazakas
008ff1c071 add operator new overloads to test Callables
When Callables are sufficiently small, they're emplaced into a small
buffer directly by placement new. If one, however, uses an unqualified
call to placement new, the class-based overloads are picked up and
compilation fails.

    libs/compat/test/move_only_function_test.cpp:107:11: note: candidate
    function not viable: requires 1 argument, but 2 were provided
        107 |     void* operator new(std::size_t) { throw 1234; }
2026-01-23 19:34:36 -08:00
Peter Dimov
6050e534ca Merge pull request #24 from cmazakas/fix/sbo-invoke-copy
remove copying when invoking objects stored in sbo
2026-01-22 09:49:54 +02:00
Christian Mazakas
4596e8938e initialize object storage using tag dispatch
Currently, the code branches on whether to use SBO or not using a
runtime check, i.e. a raw `if(x)`. This can trick the msvc optimizer
into believing the SBO path can be taken, which in the case of
sufficiently large Callables triggers a buffer overrun warning as
emplacing into the small buffer would exceed its size.

By updating the code to instead dispatch at compile-time via tags, the
msvc optimizer is no longer confused and the warning disappears as the
code path is now truly unreachable.
2026-01-21 19:36:38 -08:00
Peter Dimov
e7098ce569 Bump MSVC < 1950 workarounds to < 1960; still unfixed. Sad! 2026-01-20 18:51:03 +02:00
Peter Dimov
96ec9c1b89 Add msvc-14.5 to .drone.jsonnet 2026-01-20 18:44:56 +02:00
7 changed files with 34 additions and 17 deletions

View File

@@ -292,4 +292,10 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
"cppalliance/dronevs2022:1", "cppalliance/dronevs2022:1",
{ TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' }, { TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' },
), ),
windows_pipeline(
"Windows VS2026 msvc-14.5",
"cppalliance/dronevs2026:1",
{ TOOLSET: 'msvc-14.5', CXXSTD: '14,17,20,latest' },
),
] ]

View File

@@ -397,7 +397,7 @@ struct move_only_function_base
case op_type::move: case op_type::move:
{ {
VT* p = static_cast<VT*>( src->addr() ); VT* p = static_cast<VT*>( src->addr() );
new(s.addr()) VT( std::move( *p ) ); ::new( s.addr() ) VT( std::move( *p ) );
// destruct the element here because move construction will leave the container empty // destruct the element here because move construction will leave the container empty
// outside of this function // outside of this function
p->~VT(); p->~VT();
@@ -435,6 +435,22 @@ struct move_only_function_base
{ {
} }
template<class VT, class ...CArgs>
void init_object( std::false_type /* use_sbo */, CArgs&& ...args )
{
s_.pobj_ = new VT( std::forward<CArgs>( args )... );
invoke_ = &mo_invoke_object_holder<RQ, Const, NoEx, VT, R, Args...>::invoke_object;
manager_ = &manage_object<VT>;
}
template<class VT, class ...CArgs>
void init_object( std::true_type /* use_sbo */, CArgs&& ...args )
{
::new( s_.addr() ) VT( std::forward<CArgs>( args )... );
invoke_ = &mo_invoke_local_holder<RQ, Const, NoEx, VT, R, Args...>::invoke_local;
manager_ = &manage_local<VT>;
}
template<class VT, class ...CArgs> template<class VT, class ...CArgs>
void init( std::false_type /* is_function */, CArgs&& ...args ) void init( std::false_type /* is_function */, CArgs&& ...args )
{ {
@@ -444,18 +460,7 @@ struct move_only_function_base
return; return;
} }
if( !storage::use_sbo<VT>() ) init_object<VT>( std::integral_constant<bool, storage::use_sbo<VT>()>{}, std::forward<CArgs>( args )... );
{
s_.pobj_ = new VT( std::forward<CArgs>( args )... );
invoke_ = &mo_invoke_object_holder<RQ, Const, NoEx, VT, R, Args...>::invoke_object;
manager_ = &manage_object<VT>;
}
else
{
new( s_.addr() ) VT( std::forward<CArgs>( args )... );
invoke_ = &mo_invoke_local_holder<RQ, Const, NoEx, VT, R, Args...>::invoke_local;
manager_ = &manage_local<VT>;
}
} }
template<class VT, class ...CArgs> template<class VT, class ...CArgs>

View File

@@ -39,7 +39,7 @@ int main()
BOOST_TEST_EQ( boost::compat::bind_back( &X::m, &x )(), -1 ); BOOST_TEST_EQ( boost::compat::bind_back( &X::m, &x )(), -1 );
} }
#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) #if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1960)
{ {
BOOST_TEST_EQ( boost::compat::bind_back( &X::m, Y() )(), -1 ); BOOST_TEST_EQ( boost::compat::bind_back( &X::m, Y() )(), -1 );

View File

@@ -39,7 +39,7 @@ int main()
BOOST_TEST_EQ( boost::compat::bind_front( &X::m, &x )(), -1 ); BOOST_TEST_EQ( boost::compat::bind_front( &X::m, &x )(), -1 );
} }
#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) #if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1960)
{ {
BOOST_TEST_EQ( boost::compat::bind_front( &X::m, Y() )(), -1 ); BOOST_TEST_EQ( boost::compat::bind_front( &X::m, Y() )(), -1 );

View File

@@ -29,7 +29,7 @@ int main()
BOOST_TEST_EQ( boost::compat::invoke( &X::m, &x ), -1 ); BOOST_TEST_EQ( boost::compat::invoke( &X::m, &x ), -1 );
} }
#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) #if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1960)
{ {
BOOST_TEST_EQ( boost::compat::invoke( &X::m, Y() ), -1 ); BOOST_TEST_EQ( boost::compat::invoke( &X::m, Y() ), -1 );

View File

@@ -36,7 +36,7 @@ int main()
#endif #endif
} }
#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) #if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1960)
{ {
constexpr Y y = {}; constexpr Y y = {};

View File

@@ -102,6 +102,9 @@ struct callable
{ {
return *p_ + x; return *p_ + x;
} }
// Should never be called, as this should always fit into the small buffer.
void* operator new(std::size_t) { throw 1234; }
}; };
struct noex_callable struct noex_callable
@@ -122,6 +125,9 @@ struct noex_callable
{ {
return *p_ + x; return *p_ + x;
} }
// Should never be called, as this should always fit into the small buffer.
void* operator new(std::size_t) { throw 1234; }
}; };
struct large_callable struct large_callable