Skip to content

Conversation

@cmazakas
Copy link
Member

Resolves: #23

Right now the invoke holders take the storage by-value which is strictly incorrect when using SBO as it creates a copy of the callable which leads to surprising results in user-code that expect multiple calls to a lambda with a mutable capture.

@pdimov
Copy link
Member

pdimov commented Jan 16, 2026

Instead of storage&, make it a template Storage& to capture the const which will allow you to omit the const_casts.

@cmazakas
Copy link
Member Author

Instead of storage&, make it a template Storage& to capture the const which will allow you to omit the const_casts.

I'm trying that but I'm having trouble getting code like this to then compile:

        move_only_function<int( noex_callable ) const> f2( std::move( f1 ) );
        BOOST_TEST_EQ( f2( c ), 1235 );

        move_only_function<int( noex_callable )> f3( std::move( f2 ) );
        BOOST_TEST_EQ( f3( c ), 1235 );

The compilers reject it with:

./boost/compat/move_only_function.hpp:421:24: error: invalid conversion from ‘int (*)(boost::compat::detail::move_only_function_base<boost::compat::detail::ref_quals::none, true, false, int, noex_callable>::storage_type&, noex_callable&&)’ {aka ‘int (*)(const boost::compat::detail::storage&, noex_callable&&)’} to ‘int (*)(boost::compat::detail::move_only_function_base<boost::compat::detail::ref_quals::none, false, false, int, noex_callable>::storage_type&, noex_callable&&)’ {aka ‘int (*)(boost::compat::detail::storage&, noex_callable&&)’} [-fpermissive]
  421 |         invoke_ = base.invoke_;

The problem being there's no conversion from:

int (*)(const boost::compat::detail::storage&

to

int (*)(boost::compat::detail::storage&

Many of the tests fail like:

libs/compat/test/move_only_function_test.cpp:844:41: note: in instantiation of function template specialization 'boost::compat::move_only_function<int (long)>::move_only_function<boost::compat::move_only_function<int (long) const>, boost::compat::move_only_function<int (long) const>, 0>' requested here
  844 |         move_only_function<int( long )> e3( std::move( e2 ) );

Do you want me to push up my WIP branch?

The more I think about it, the const_cast is probably harmless here anyway because we correctly apply the cv-ref qualifiers at the invoke site.

I'm not sure what we should do to fix these kinds of function pointer cast errors.

@pdimov
Copy link
Member

pdimov commented Jan 16, 2026

Ah, I see.

Fewer const_casts will be needed if you declare the functions to take storage const& (or storage const* if you prefer) and then const_cast inside, instead of at the call site.

@cmazakas cmazakas force-pushed the fix/sbo-invoke-copy branch from a68b9b4 to eed8de3 Compare January 17, 2026 14:23
Resolves: boostorg#23

Right now the invoke holders take the storage by-value which is strictly
incorrect when using SBO as it creates a copy of the callable which
leads to surprising results in user-code that expect multiple calls
to a lambda with a mutable capture.

Signed-off-by: Christian Mazakas <christian.mazakas@gmail.com>
@cmazakas cmazakas force-pushed the fix/sbo-invoke-copy branch from eed8de3 to d9faeab Compare January 17, 2026 15:43
#endif

#ifdef _MSC_VER
#pragma warning(disable: 4789) // false buffer overrun warning in test_mutable_lambda()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the buffer overrun warning?

@pdimov
Copy link
Member

pdimov commented Jan 18, 2026

Can you please structure this as: commit 1 - test additions without the 4789 warning suppression; commit 2 - changes to the implementation; commit 3 - warning suppression in the test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

move_only_function does not carry mutable lambda state over multiple calls

2 participants