is_valid does not allow lvalue references
Flamefire opened this issue · comments
const Foo&
as this fails with
error: non-const lvalue reference to type '::boost::rv<const Foo&>' cannot bind to a value of unrelated type 'const Foo'
I guess that should just be regular params as per the signature?
Hi,
Do you have a sample code which fails? What compiler are you using?
Just mock a function bool handle(const Foo&)
and pass it a reference (lvalue). Using C++98 obviously
But to be exact the code is invalid if I did not misunderstood it. Assume the above function as virtual in a base class (or just that this has to be mocked). Then the mock macro will create a wrapper handle_moc(const Foo& t){ ...; ...->is_valid(boost::forward<const Foo&>(t)); ...}
and that is_valid is defined as is_valid(Foo&& a)
.
As can be seen is_valid
only takes rvalue references although it should simply take whatever the mocked function takes. I even think the forward
call is wrong:
handle(const Foo&)
-> forwarded asconst Foo&
handle(Foo&&)
-> forwarded asFoo&&
handle(Foo)
-> forwarded asFoo&&
Hence you are passing an instance (copy) as an rvalue, so consumers could move from it effectively destroying it. As only internal functions are called, this should not be a problem as they don't make use of that, but what if any of those internal functions decides to do valueCopy(forward(value))
and value
is a smart pointer/vector...?
A possible bug is e.g. here: Values are forwarded to is_valid
(
functor
if present (
). Hence the 2nd call might receive a moved-from value!This is even worse than I thought. There are many forward
and move
calls in there and I think almost all of them are wrong.
From my understanding:
- The mocked function signature should match exactly the real function signature
- Arguments processed from there shall be passed by reference only (either const, or non-const ref depending in const-ness of the param)
- Only valid move would be the capture/receive function
- Note that this happens in the
is_valid
call and hence will make a passedfunctor
fail here - Hence the is_valid should NEVER alter its arguments and only receive them by const-ref
- The capturing should happen in the functor instead
- Note that this happens in the
I experimented a lot with this now and got so far that my GCC 5 compiles and validates it in C++98 mode except for 1 example which I have to look into
BUT: We can make this much easier: An essential part of this is boost::function
which is used in the action
class. This does NOT support move-only types (I created a test using Boost.Move in C++98) So you cannot create a boost::function
with a signature taking an instance of a move-only type which means you cannot mock such a function.
So there is a choice to make now @mat007 :
- Do not support Move-Only types for C++98 and only conditionally use
T&&
instead ofBOOST_RV_REF
which would solve this issue too - Rework the parameter passing now. As explained above unconditionally taking rvalue references is wrong. It should rather be a reference only in all classes except
functor
andfunction
. And only 1 instance shall receive the rvalue reference (if it is moveable, ref otherwise)
So the Signature template param would need to be changed. IMO this would be the right way to go but is probably quite some work.
Any idea?
Thanks for diving into this @Flamefire !
I don't really mind deprecating support for 'old' stuff if this makes things simpler.
I honestly don't know if anyone still uses all that Boost move emulation, my take would be that it was merely during the transition phase when we played with move when it was first introduced…
Today if someone really wants move support they'll likely upgrade their compiler to at least C++11 instead of bothering with a partial move emulation.
So 1. looks appealing and simpler than 2. right?
The problem is that the current code is most likely not correct and definitely wrong in 1 place (possible use of moved from /forwarded value) so one need to (partially) do 2 anyway. Maybe correct usage of the types/moves already solved this problem too.
- Would be a quick(er) fix but would prevent usage if e.g. boost::unique_ptr as rvalue in interfaces. Not sure if this is possible anyway