Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions score/mw/com/impl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ cc_library(
"//score/mw/com:__subpackages__",
],
deps = [
":field_tags",
":method_type",
":skeleton_event",
":skeleton_field_base",
Expand All @@ -247,6 +248,7 @@ cc_library(
"//score/mw/com:__subpackages__",
],
deps = [
":field_tags",
":flag_owner",
":proxy_base",
":proxy_event",
Expand Down Expand Up @@ -286,6 +288,7 @@ cc_library(
"//score/mw/com:__subpackages__",
],
deps = [
":field_tags",
":method_type",
":proxy_event",
":proxy_event_binding",
Expand Down Expand Up @@ -689,6 +692,15 @@ cc_library(
visibility = ["//score/mw/com:__subpackages__"],
)

cc_library(
name = "field_tags",
srcs = ["field_tags.cpp"],
hdrs = ["field_tags.h"],
features = COMPILER_WARNING_FEATURES,
tags = ["FFI"],
visibility = ["//score/mw/com:__subpackages__"],
)

cc_library(
name = "service_element_type",
srcs = ["service_element_type.cpp"],
Expand Down
13 changes: 13 additions & 0 deletions score/mw/com/impl/field_tags.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
#include "score/mw/com/impl/field_tags.h"
62 changes: 62 additions & 0 deletions score/mw/com/impl/field_tags.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
#ifndef PLATFORM_AAS_MW_COM_IMPL_FIELD_TAGS_H
#define PLATFORM_AAS_MW_COM_IMPL_FIELD_TAGS_H

#include <type_traits>

namespace score::mw::com::impl
{

/// \brief Tag types used on ProxyField/SkeletonField level to accomplish overload-resolution for various signatures,
/// which depend on the availability of Get/Set/Notifier. A field must enable WithGetter or WithNotifier
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment "A field must enable WithGetter or WithNotifier (a write-only field would be invisible to consumers)." isn't really relevant here IMO. this should rather be in the documentation of the field itself. The tags themselves don't care about that invariant. The invairnat only matters in the place where the tags are used i.e. the actual field.

/// (a write-only field would be invisible to consumers).
struct WithGetter
{
};

struct WithSetter
{
};

struct WithNotifier
{
};

namespace detail
{

template <typename TargetType, typename... Tags>
struct contains_type : std::disjunction<std::is_same<TargetType, Tags>...>
{
};

// SFINAE only works when the condition depends on the function-template parameters, not the class-template
// parameters. The std::is_same<FieldType, ClassFieldType> line ties substitution to a function-template
// parameter so the check fires at overload resolution instead of class instantiation.
template <typename FieldType, typename ClassFieldType, typename TargetTag, typename... Tags>
struct is_tag_enabled : std::conjunction<contains_type<TargetTag, Tags...>, std::is_same<FieldType, ClassFieldType>>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since is_tag_enabled is a public function to be used by other targets, it shouldn't be in the detail namespace.

{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should have unit tests for any public function (so definitely is_tag_enabled and if contains_type is publicly exposed i.e. not in the detail namespace then also for it). You can look at the unit tests that I wrote for method_handler_checker_test as a reference if you want: https://github.com/eclipse-score/communication/pull/369/changes#diff-a5298ded87ed55bcf9aceb680edbe7e0c9b81700b7a5e0f24feb7b3600dac64d

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We can only support "positive" tests for the moment, but you should also write the negative tests e.g.

};

/// \brief Marker used as a disambiguator on test-only ctors to keep them distinct from production overloads.
/// Lives in detail to signal "not part of the public API"; tests reach into the detail namespace knowingly.
struct WithTestTag
{
};

} // namespace detail

} // namespace score::mw::com::impl

#endif // PLATFORM_AAS_MW_COM_IMPL_FIELD_TAGS_H
10 changes: 5 additions & 5 deletions score/mw/com/impl/generic_proxy_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ namespace score::mw::com::impl

GenericProxyEvent::GenericProxyEvent(ProxyBase& base, const std::string_view event_name)
: ProxyEventBase{base,
event_name,
ProxyBaseView{base}.GetBinding(),
GenericProxyEventBindingFactory::Create(base, event_name),
event_name}
GenericProxyEventBindingFactory::Create(base, event_name)}
{
ProxyBaseView proxy_base_view{base};
if (!binding_base_)
Expand All @@ -34,9 +34,9 @@ GenericProxyEvent::GenericProxyEvent(ProxyBase& base, const std::string_view eve
}

GenericProxyEvent::GenericProxyEvent(ProxyBase& base,
std::unique_ptr<GenericProxyEventBinding> proxy_binding,
const std::string_view event_name)
: ProxyEventBase{base, ProxyBaseView{base}.GetBinding(), std::move(proxy_binding), event_name}
const std::string_view event_name,
std::unique_ptr<GenericProxyEventBinding> proxy_binding)
: ProxyEventBase{base, event_name, ProxyBaseView{base}.GetBinding(), std::move(proxy_binding)}
{
ProxyBaseView proxy_base_view{base};
if (!binding_base_)
Expand Down
4 changes: 2 additions & 2 deletions score/mw/com/impl/generic_proxy_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class GenericProxyEvent : public ProxyEventBase
///
/// \param proxy_binding The binding that shall be associated with this proxy.
explicit GenericProxyEvent(ProxyBase& base,
std::unique_ptr<GenericProxyEventBinding> proxy_binding,
const std::string_view event_name);
const std::string_view event_name,
std::unique_ptr<GenericProxyEventBinding> proxy_binding);

/// \brief Constructs a ProxyEvent by querying the base proxie's ProxyBinding for the respective ProxyEventBinding.
///
Expand Down
10 changes: 5 additions & 5 deletions score/mw/com/impl/generic_proxy_event_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ TEST(GenericProxyEventTest, SamplePtrsToSlotDataAreConst)
ProxyBase empty_proxy(std::make_unique<mock_binding::Proxy>(),
make_HandleType(make_InstanceIdentifier(kEmptyInstanceDeployment, kEmptyTypeDeployment)));
GenericProxyEvent proxy_event{
empty_proxy, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}, kEventName};
empty_proxy, kEventName, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}};

EXPECT_CALL(mock_proxy_event, Subscribe(max_num_samples));
EXPECT_CALL(mock_proxy_event, GetNewSamples(_, _));
Expand Down Expand Up @@ -120,7 +120,7 @@ TEST(GenericProxyEventDeathTest, DieOnProxyDestructionWhileHoldingSamplePtrs)
ProxyBase empty_proxy(std::make_unique<mock_binding::Proxy>(),
make_HandleType(make_InstanceIdentifier(kEmptyInstanceDeployment, kEmptyTypeDeployment)));
auto proxy_event = std::make_unique<GenericProxyEvent>(
empty_proxy, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}, kEventName);
empty_proxy, kEventName, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)});

EXPECT_CALL(mock_proxy_event, Subscribe(max_num_samples));
EXPECT_CALL(mock_proxy_event, GetNewSamples(_, _));
Expand Down Expand Up @@ -158,7 +158,7 @@ TEST(GenericProxyEventGetSampleSizeTest, GetSampleSizeDispatchesToBinding)
ProxyBase empty_proxy(std::make_unique<mock_binding::Proxy>(),
make_HandleType(make_InstanceIdentifier(kEmptyInstanceDeployment, kEmptyTypeDeployment)));
GenericProxyEvent proxy_event{
empty_proxy, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}, kEventName};
empty_proxy, kEventName, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}};

// Expect that GetSampleSize is called once on the binding
EXPECT_CALL(mock_proxy_event, GetSampleSize()).WillOnce(Return(expected_sample_size));
Expand Down Expand Up @@ -186,7 +186,7 @@ TEST(GenericProxyEventHasSerializedFormatTest, HasSerializedFormatDispatchesToBi
ProxyBase empty_proxy(std::make_unique<mock_binding::Proxy>(),
make_HandleType(make_InstanceIdentifier(kEmptyInstanceDeployment, kEmptyTypeDeployment)));
GenericProxyEvent proxy_event{
empty_proxy, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}, kEventName};
empty_proxy, kEventName, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}};

// Expect that HasSerializedFormat is called once on the binding
EXPECT_CALL(mock_proxy_event, HasSerializedFormat()).WillOnce(Return(expected_has_serialized_format));
Expand Down Expand Up @@ -217,7 +217,7 @@ TEST(GenericProxyEventGetNewSamplesTest, GetNewSamplesContainsCorrectReceiverSig
ProxyBase empty_proxy(std::make_unique<mock_binding::Proxy>(),
make_HandleType(make_InstanceIdentifier(kEmptyInstanceDeployment, kEmptyTypeDeployment)));
GenericProxyEvent proxy_event{
empty_proxy, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}, kEventName};
empty_proxy, kEventName, std::unique_ptr<GenericProxyEventBinding>{std::move(mock_proxy_event_ptr)}};

// Expect that GetNewSamples is called once on the binding
EXPECT_CALL(mock_proxy_event, GetNewSamples(_, _))
Expand Down
20 changes: 0 additions & 20 deletions score/mw/com/impl/method_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,6 @@
namespace score::mw::com::impl
{

namespace detail
{

/// \brief Tag types used on ProxyField/SkeletonField level to accomplish overload-resolution for various signatures,
/// which depend on the availability of Get/Set/Notifier.
struct EnableBothTag
{
};
struct EnableGetOnlyTag
{
};
struct EnableSetOnlyTag
{
};
struct EnableNeitherTag
{
};

} // namespace detail

/// \brief Enum used to differentiate between regular service methods and field Get/Set methods.
enum class MethodType : std::uint8_t
{
Expand Down
2 changes: 1 addition & 1 deletion score/mw/com/impl/methods/proxy_method_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class ProxyMethodBase
{
public:
ProxyMethodBase(ProxyBase& proxy_base,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding,
std::string_view method_name,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding,
MethodType method_type = MethodType::kMethod) noexcept
: proxy_base_{proxy_base},
method_name_{method_name},
Expand Down
24 changes: 12 additions & 12 deletions score/mw/com/impl/methods/proxy_method_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class ProxyMethodTestFixture : public ::testing::Test
ProxyMethodTestFixture& GivenAValidProxyMethod()
{
unit_ = std::make_unique<ProxyServiceMethodType>(
proxy_base_, std::make_unique<mock_binding::ProxyMethodFacade>(proxy_method_binding_mock_), kMethodName);
proxy_base_, kMethodName, std::make_unique<mock_binding::ProxyMethodFacade>(proxy_method_binding_mock_));
return *this;
}

Expand Down Expand Up @@ -160,8 +160,8 @@ TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, Construction)
// Then the ProxyMethod can be constructed
using ProxyMethodType = ProxyMethod<TypeParam>;
ProxyMethodType{this->proxy_base_,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_),
kMethodName};
kMethodName,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_)};
}

TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, WhenMoveConstructingProxyMethodUpdateMethodIsCalled)
Expand All @@ -171,8 +171,8 @@ TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, WhenMoveConstructingProxyMe
// Given a proxy method
auto proxy_method =
ProxyMethodType{this->proxy_base_,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_),
kMethodName};
kMethodName,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_)};

// When movie-constructing this method
auto moved_method{std::move(proxy_method)};
Expand All @@ -190,15 +190,15 @@ TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, WhenMoveAssigningProxyMetho
// Given a proxy method
auto proxy_method =
ProxyMethodType{this->proxy_base_,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_),
kMethodName};
kMethodName,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_)};

ProxyBase other_proxy_base = {std::make_unique<mock_binding::Proxy>(), this->config_store_.GetHandle()};

auto other_proxy_method =
ProxyMethodType{other_proxy_base,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_),
"this_method_will_be_overwritten_soon"};
"this_method_will_be_overwritten_soon",
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_)};

// When move-assigning this method
other_proxy_method = std::move(proxy_method);
Expand All @@ -218,8 +218,8 @@ TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture,
// Given a proxy method
auto proxy_method =
ProxyMethodType{this->proxy_base_,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_),
kMethodName};
kMethodName,
std::make_unique<mock_binding::ProxyMethodFacade>(this->proxy_method_binding_mock_)};

auto same_method_ptr = &proxy_method;
// When move-assigning this method to itself
Expand Down Expand Up @@ -263,7 +263,7 @@ TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, InvalidBindingInConstructor
using ProxyMethodType = ProxyMethod<TypeParam>;

// When a proxy method is created with an invalid binding
auto proxy_method = std::make_unique<ProxyMethodType>(this->proxy_base_, nullptr, kMethodName);
auto proxy_method = std::make_unique<ProxyMethodType>(this->proxy_base_, kMethodName, nullptr);

// Then calling AreBindingsValid returns false
EXPECT_FALSE(ProxyBaseView{this->proxy_base_}.AreBindingsValid());
Expand Down
10 changes: 5 additions & 5 deletions score/mw/com/impl/methods/proxy_method_with_in_args.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,18 @@ class ProxyMethod<void(ArgTypes...)> final : public ProxyMethodBase
public:
ProxyMethod(ProxyBase& proxy_base, std::string_view method_name) noexcept
: ProxyMethod(proxy_base,
method_name,
ProxyMethodBindingFactory<void(ArgTypes...)>::Create(proxy_base.GetHandle(),
ProxyBaseView{proxy_base}.GetBinding(),
method_name,
MethodType::kMethod),
method_name)
MethodType::kMethod))
{
}

ProxyMethod(ProxyBase& proxy_base,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding,
std::string_view method_name) noexcept
: ProxyMethodBase(proxy_base, std::move(proxy_method_binding), method_name, MethodType::kMethod),
std::string_view method_name,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding) noexcept
: ProxyMethodBase(proxy_base, method_name, std::move(proxy_method_binding), MethodType::kMethod),
are_in_arg_ptrs_active_(kCallQueueSize)
{
auto proxy_base_view = ProxyBaseView{proxy_base};
Expand Down
22 changes: 11 additions & 11 deletions score/mw/com/impl/methods/proxy_method_with_in_args_and_return.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
namespace score::mw::com::impl
{

template <typename, bool, bool, bool>
template <typename, typename...>
class ProxyField;

/// \brief Partial specialization of ProxyMethod for function signatures with arguments and non-void return
Expand All @@ -55,10 +55,10 @@ class ProxyMethod<ReturnType(ArgTypes...)> final : public ProxyMethodBase
// coverity[autosar_cpp14_a11_3_1_violation]
friend class ProxyMethodView;

friend class ProxyField<ReturnType, true, true, true>;
friend class ProxyField<ReturnType, true, true, false>;
friend class ProxyField<ReturnType, false, true, false>;
friend class ProxyField<ReturnType, false, true, true>;
// ProxyField needs to instantiate ProxyMethod via the private FieldOnlyConstructorEnabler tag.
// coverity[autosar_cpp14_a11_3_1_violation]
template <typename, typename...>
friend class ProxyField;

struct FieldOnlyConstructorEnabler
{
Expand All @@ -68,11 +68,11 @@ class ProxyMethod<ReturnType(ArgTypes...)> final : public ProxyMethodBase
ProxyMethod(ProxyBase& proxy_base, std::string_view method_name) noexcept
: ProxyMethodBase(
proxy_base,
method_name,
ProxyMethodBindingFactory<ReturnType(ArgTypes...)>::Create(proxy_base.GetHandle(),
ProxyBaseView{proxy_base}.GetBinding(),
method_name,
MethodType::kMethod),
method_name,
MethodType::kMethod),
are_in_arg_ptrs_active_(kCallQueueSize)
{
Expand All @@ -86,9 +86,9 @@ class ProxyMethod<ReturnType(ArgTypes...)> final : public ProxyMethodBase
}

ProxyMethod(ProxyBase& proxy_base,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding,
std::string_view method_name) noexcept
: ProxyMethodBase(proxy_base, std::move(proxy_method_binding), method_name, MethodType::kMethod),
std::string_view method_name,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding) noexcept
: ProxyMethodBase(proxy_base, method_name, std::move(proxy_method_binding), MethodType::kMethod),
are_in_arg_ptrs_active_(kCallQueueSize)
{
auto proxy_base_view = ProxyBaseView{proxy_base};
Expand All @@ -101,10 +101,10 @@ class ProxyMethod<ReturnType(ArgTypes...)> final : public ProxyMethodBase
}

ProxyMethod(ProxyBase& proxy_base,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding,
std::string_view method_name,
std::unique_ptr<ProxyMethodBinding> proxy_method_binding,
FieldOnlyConstructorEnabler) noexcept
: ProxyMethodBase(proxy_base, std::move(proxy_method_binding), method_name, MethodType::kSet),
: ProxyMethodBase(proxy_base, method_name, std::move(proxy_method_binding), MethodType::kSet),
are_in_arg_ptrs_active_(kCallQueueSize)
{
auto proxy_base_view = ProxyBaseView{proxy_base};
Expand Down
Loading