mw/com: Fix method signature to avoid copies of return type#369
mw/com: Fix method signature to avoid copies of return type#369bemerybmw wants to merge 10 commits into
Conversation
44e9aa3 to
b336e39
Compare
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. If this PR is still relevant, please leave a comment or push new changes to keep it open. |
b336e39 to
cec7195
Compare
cec7195 to
5729f2f
Compare
| constexpr bool is_return_type_not_void = !std::is_same_v<ReturnType, void>; | ||
| if constexpr (is_return_type_not_void) | ||
| { | ||
| const auto typed_return_ptr_tuple = Deserialize<ReturnType>(type_erased_return.value()); |
There was a problem hiding this comment.
you could use DeserializeArg<ReturnType>() directly ... much simpler?
| ::testing::Types<MethodTypeAndHandlerType<TestMethodType, TestMethodHandlerType>, | ||
| MethodTypeAndHandlerType<void(int), void(const int&)>, | ||
| MethodTypeAndHandlerType<void(int, int), void(const int&, const int&)>, | ||
| MethodTypeAndHandlerType<void(const double, int), void(const double&, const int&)>, |
There was a problem hiding this comment.
Interessting. So you have a const in front of an in-arg .... in the user provided method-signature ... "technically" this is uneeded as with treat in-args as const anyhow! But here you are intentionally checking, that we also can cope with such a signature?
| @@ -99,17 +83,39 @@ TEST(SkeletonMethodTest, ClassTypeDependsOnMethodType) | |||
| "Class type does not depend on event data type"); | |||
There was a problem hiding this comment.
event data type
method signature?
| } | ||
|
|
||
| EmptySkeleton empty_skeleton_{std::make_unique<mock_binding::Skeleton>(), kInstanceIdWithLolaBinding}; | ||
| std::string method_name_{"dummy_method"}; |
|
|
||
| template <typename T = SampleDataType, typename = std::enable_if_t<EnableSet && std::is_same_v<T, SampleDataType>>> | ||
| score::Result<MethodReturnTypePtr<T>> Set(SampleDataType& new_field_value) noexcept | ||
| score::Result<MethodReturnTypePtr<T>> Set(const SampleDataType& new_field_value) noexcept |
There was a problem hiding this comment.
Hm. Now I got aware, that we don't expose the zero-copy/allocate variant of method calls to the user ... I guess this would be just a small addition ... but let's wait til someone really asks for it :)
| return a + b; | ||
| }; | ||
| auto handler_with_in_args_and_return = | ||
| [](std::int32_t& return_value, const std::int32_t& a, const std::int32_t& b) { |
There was a problem hiding this comment.
maybe add a -> void ... makes it "clearer"?
4623c8f to
265b367
Compare
| write_head += in_type_1_size; | ||
| new (write_head) InType2(in_arg_2); | ||
| write_head += in_type_2_size; | ||
| new (write_head) InType2(in_arg_3); |
There was a problem hiding this comment.
the in_args_buffer_ is still sized with 2 elements here.
static constexpr std::size_t in_args_buffer_size = sizeof(InType1) + sizeof(InType2);
shouldn't this be
static constexpr std::size_t in_args_buffer_size = sizeof(InType1) + sizeof(InType2) + sizeof(InType2); ?
| "ReturnType is non void. Thus, type_erased_result needs to have a value!"); | ||
| ReturnType res = std::apply(callable_invoker, std::forward<InArgPtrTuple>(typed_in_arg_ptrs)); | ||
| SerializeArgs<ReturnType>(type_erased_return.value(), res); | ||
| auto* const typed_return_ptr = DeserializeArg<ReturnType>(type_erased_return.value()); |
There was a problem hiding this comment.
ah! we populate inplace, cool,
but, does it not weaken the "safe for trivially copyable" check?
in SerializeArgs route before we had this explicit check static_assert(std::is_trivially_copyable_v before we populate type_erased_return.
i think this is fine today though.
There was a problem hiding this comment.
Hmmm, I actually don't think we have that requirement for deserializing. Deserializing is basically just casting the type, there's no copying done. In practice, we always serialize with the function in this file which already confirms that the type is trivially copyable but in theory, you could create a type erased buffer containing a non trivially copyable type (but the caller has to take that it's correctly created in the buffer) and then deserialize it without making a copy which should not be a problem. If the user then wants to copy it out of the buffer, it's now strongly typed and so the copy constructor can be called.
| { | ||
| detail::MemoryBufferAccessor memory_buffer_accessor{src_buffer}; | ||
| auto tuple_of_args = detail::Deserialize<Arg>(memory_buffer_accessor); | ||
| SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(std::tuple_size_v<decltype(tuple_of_args)> == 1, |
There was a problem hiding this comment.
this can be a static assert?
| MethodTypeAndHandlerType<void(int), void(const int&)>, | ||
| MethodTypeAndHandlerType<void(int, int), void(const int&, const int&)>, | ||
| MethodTypeAndHandlerType<void(const double, int), void(const double&, const int&)>, | ||
| MethodTypeAndHandlerType<void(int, int), void(const int&, const int&)>, |
There was a problem hiding this comment.
This looks like a dup of https://github.com/eclipse-score/communication/pull/369/changes#diff-40f76737b28b11683a2795d7bc4658afc427cd2d105ba193b4f35133dc9fadbaR138
is this intended?
| Result<void> RegisterSetHandler(CallableType&& set_handler) | ||
| { | ||
| static_assert(std::is_invocable_v<CallableType, FieldType&>, | ||
| static_assert(std::is_invocable_r_v<void, CallableType, FieldType&>, |
There was a problem hiding this comment.
does this really do something?
somehow, my static asserts here always work even when the ret type is non void.
| score::cpp::callback<FixtureMethodType> test_callback{}; | ||
|
|
||
| std::ignore = method.RegisterHandler(std::move(test_callback)); | ||
| // // When a Register call is issued at the binding independent level |
There was a problem hiding this comment.
| // // When a Register call is issued at the binding independent level | |
| // When a Register call is issued at the binding independent level |
This is the copy variant of set which takes a field value and returns the new value via the MethodReturnTypePtr. The provided value will not be modified so it's misleading that it's non-const.
804eec6 to
beeff8d
Compare
beeff8d to
cbc493d
Compare
|
|
||
| ASSERT_TRUE(unit.my_setter_field_.Update(TestSampleType{1U}).has_value()); | ||
| ASSERT_TRUE(unit.my_setter_field_.PrepareOffer().has_value()); | ||
| TEST_F(SkeletonFieldSetHandlerTest, CallingPrepareOfferWithoutRegisteringSetHandlerReturnsError) |
There was a problem hiding this comment.
isn't this the same test as here
| return set_method_->RegisterHandler(std::move(wrapped_callback)); | ||
|
|
||
| auto state = std::make_unique<std::pair<SkeletonField*, CallableType>>( | ||
| this, std::forward<CallableType>(user_set_handler)); |
There was a problem hiding this comment.
wont this cause the same "use after free" issue?
since we store "this" during the registration into the stored set_handler and i dont think we update this when we move the SkeletonField then won't this still be pointing to the moved from object?
or am i missing something?
| "Registered method callable must have a single non-const reference argument of type FieldType&!"); | ||
|
|
||
| // Since set_handler can be an lvalue reference or an rvalue reference, we ideally would store it as a | ||
| // universal reference in the type_erased_handler. However, in C++17, this is not supported. Instead, we create |
There was a problem hiding this comment.
to me this sounds like this is C++17 only issue (storing a universal ref),
It is only used by compiler to figure out what is the type of the args, once it is figures that out it would be either an L or an R value, so can we just drop that confusing line ?
https://stackoverflow.com/questions/14757474/how-to-store-universal-references
| // Since set_handler can be an lvalue reference or an rvalue reference, we ideally would store it as a | ||
| // universal reference in the type_erased_handler. However, in C++17, this is not supported. Instead, we create | ||
| // an callable here which will be called below by another lambda which explicitly stores the callback as either | ||
| // an lvalue reference or an rvalue reference depending on how it was passed in. |
There was a problem hiding this comment.
does this also have lvalue vs rvalue branch like above? https://github.com/eclipse-score/communication/pull/369/changes#diff-5067b694039ba429e1f705db5d97521e3f0b3b0b07232117860ecd84ad9ebc80R212
Depends-on: #514