fix: add missing c-tor for nesting Variants (#467)

This allows nesting variants, i.e. allows a variant to contain another variant as its value. Until now, there was no such possibility, since the default generated copy constructor would be invoked which would create a copy of source variant instead of embed the source variant as a value in the destination variant. The default generated copy constructor is kept, for it makes sense too, but a new tag-based overload is added for embedding the source variant into the destination variant.
This commit is contained in:
Stanislav Angelovič
2024-11-20 23:40:30 +01:00
committed by GitHub
parent 02ca7212d1
commit a1419ee45d
10 changed files with 67 additions and 7 deletions

View File

@ -106,6 +106,9 @@ namespace sdbus {
// Tag denoting a call where the reply shouldn't be waited for
struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; };
inline constexpr dont_expect_reply_t dont_expect_reply{};
// Tag denoting that the variant shall embed the other variant as its value, instead of creating a copy
struct embed_variant_t { explicit embed_variant_t() = default; };
inline constexpr embed_variant_t embed_variant{};
// Helper for static assert
template <class... _T> constexpr bool always_false = false;

View File

@ -67,6 +67,14 @@ namespace sdbus {
msg_.seal();
}
Variant(const Variant& value, embed_variant_t) : Variant()
{
msg_.openVariant<Variant>();
msg_ << value;
msg_.closeVariant();
msg_.seal();
}
template <typename _Struct>
explicit Variant(const as_dictionary<_Struct>& value) : Variant()
{

View File

@ -81,3 +81,12 @@ TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHa
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
}
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWriteVariantPropertySuccessfully)
{
sdbus::Variant newActionValue{5678};
this->m_proxy->actionVariant(newActionValue);
ASSERT_THAT(this->m_proxy->actionVariant().template get<int>(), Eq(5678));
}

View File

@ -141,9 +141,10 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
ASSERT_THAT(properties, SizeIs(4));
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
@ -161,9 +162,10 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfac
});
const auto properties = future.get();
ASSERT_THAT(properties, SizeIs(3));
ASSERT_THAT(properties, SizeIs(4));
EXPECT_THAT(properties.at(STATE_PROPERTY).get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
@ -173,9 +175,10 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfac
auto properties = future.get();
ASSERT_THAT(properties, SizeIs(3));
ASSERT_THAT(properties, SizeIs(4));
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
@ -252,17 +255,19 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterface
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
#endif
signalReceived = true;
};
@ -288,17 +293,19 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
#endif
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
#endif
signalReceived = true;
};

View File

@ -42,6 +42,7 @@ const ObjectPath OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
const ObjectPath OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
const PropertyName STATE_PROPERTY{"state"};
const PropertyName ACTION_PROPERTY{"action"};
const PropertyName ACTION_VARIANT_PROPERTY{"actionVariant"};
const PropertyName BLOCKING_PROPERTY{"blocking"};
const std::string DIRECT_CONNECTION_SOCKET_PATH{std::filesystem::temp_directory_path() / "sdbus-cpp-direct-connection-test"};
@ -58,6 +59,7 @@ const int UNIX_FD_VALUE = 0;
const std::string DEFAULT_STATE_VALUE{"default-state-value"};
const uint32_t DEFAULT_ACTION_VALUE{999};
const std::string DEFAULT_ACTION_VARIANT_VALUE{"ahoj"};
const bool DEFAULT_BLOCKING_VALUE{true};
constexpr const double DOUBLE_VALUE{3.24L};

View File

@ -266,6 +266,16 @@ void TestAdaptor::action(const uint32_t& value)
m_action = value;
}
sdbus::Variant TestAdaptor::actionVariant()
{
return m_actionVariant;
}
void TestAdaptor::actionVariant(const sdbus::Variant& value)
{
m_actionVariant = value;
}
bool TestAdaptor::blocking()
{
return m_blocking;

View File

@ -89,6 +89,8 @@ protected:
uint32_t action() override;
void action(const uint32_t& value) override;
sdbus::Variant actionVariant() override;
void actionVariant(const sdbus::Variant& value) override;
bool blocking() override;
void blocking(const bool& value) override;
std::string state() override;
@ -101,6 +103,7 @@ private:
const std::string m_state{DEFAULT_STATE_VALUE};
uint32_t m_action{DEFAULT_ACTION_VALUE};
bool m_blocking{DEFAULT_BLOCKING_VALUE};
sdbus::Variant m_actionVariant{"ahoj"};
public: // for tests
// For dont-expect-reply method call verifications
@ -152,6 +155,8 @@ protected:
uint32_t action() override { return {}; }
void action(const uint32_t&) override {}
sdbus::Variant actionVariant() override { return {}; }
void actionVariant(const sdbus::Variant&) override {}
bool blocking() override { return {}; }
void blocking(const bool&) override {}
std::string state() override { return {}; }

View File

@ -63,6 +63,7 @@ protected:
, sdbus::registerSignal("signalWithMap").withParameters<std::map<int32_t, std::string>>("aMap")
, sdbus::registerSignal("signalWithVariant").withParameters<sdbus::Variant>("aVariant")
, sdbus::registerProperty("action").withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL)
, sdbus::registerProperty("actionVariant").withGetter([this](){ return this->actionVariant(); }).withSetter([this](const sdbus::Variant& value){ this->actionVariant(value); }).withUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL)
, sdbus::registerProperty("blocking").withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); })
, sdbus::registerProperty("state").withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE)
).forInterface(INTERFACE_NAME);
@ -113,7 +114,9 @@ private:
private:
virtual uint32_t action() = 0;
virtual sdbus::Variant actionVariant() = 0;
virtual void action(const uint32_t& value) = 0;
virtual void actionVariant(const sdbus::Variant& value) = 0;
virtual bool blocking() = 0;
virtual void blocking(const bool& value) = 0;
virtual std::string state() = 0;

View File

@ -225,6 +225,16 @@ public:
m_proxy.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
}
sdbus::Variant actionVariant()
{
return m_proxy.getProperty("actionVariant").onInterface(INTERFACE_NAME).get<sdbus::Variant>();
}
void actionVariant(const sdbus::Variant& value)
{
m_proxy.setProperty("actionVariant").onInterface(INTERFACE_NAME).toValue({value, sdbus::embed_variant});
}
bool blocking()
{
return m_proxy.getProperty("blocking").onInterface(INTERFACE_NAME).get<bool>();

View File

@ -375,6 +375,9 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
if (propertyAccess == "readwrite" || propertyAccess == "write")
{
if (propertySignature == "v")
propertyArg = "{" + propertyArg + ", sdbus::embed_variant}";
const std::string realRetType = (asyncSet ? (futureSet ? "std::future<void>" : "sdbus::PendingAsyncCall") : "void");
propertySS << tab << realRetType << " " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl