// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef DBUS_PROPERTY_H_ #define DBUS_PROPERTY_H_ #include <stdint.h> #include <map> #include <string> #include <utility> #include <vector> #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/memory/raw_ptr.h" #include "dbus/dbus_export.h" #include "dbus/message.h" #include "dbus/object_proxy.h" // D-Bus objects frequently provide sets of properties accessed via a // standard interface of method calls and signals to obtain the current value, // set a new value and be notified of changes to the value. Unfortunately this // interface makes heavy use of variants and dictionaries of variants. The // classes defined here make dealing with properties in a type-safe manner // possible. // // Client implementation classes should define a Properties structure, deriving // from the PropertySet class defined here. This structure should contain a // member for each property defined as an instance of the Property<> class, // specifying the type to the template. Finally the structure should chain up // to the PropertySet constructor, and then call RegisterProperty() for each // property defined to associate them with their string name. // // Example: // class ExampleClient { // public: // struct Properties : public dbus::PropertySet { // dbus::Property<std::string> name; // dbus::Property<uint16_t> version; // dbus::Property<dbus::ObjectPath> parent; // dbus::Property<std::vector<std::string>> children; // // Properties(dbus::ObjectProxy* object_proxy, // const PropertyChangedCallback callback) // : dbus::PropertySet(object_proxy, "com.example.DBus", callback) { // RegisterProperty("Name", &name); // RegisterProperty("Version", &version); // RegisterProperty("Parent", &parent); // RegisterProperty("Children", &children); // } // virtual ~Properties() {} // }; // // The Properties structure requires a pointer to the object proxy of the // actual object to track, and after construction should have signals // connected to that object and initial values set by calling ConnectSignals() // and GetAll(). The structure should not outlive the object proxy, so it // is recommended that the lifecycle of both be managed together. // // Example (continued): // // typedef std::map<std::pair<dbus::ObjectProxy*, Properties*>> Object; // typedef std::map<dbus::ObjectPath, Object> ObjectMap; // ObjectMap object_map_; // // dbus::ObjectProxy* GetObjectProxy(const dbus::ObjectPath& object_path) { // return GetObject(object_path).first; // } // // Properties* GetProperties(const dbus::ObjectPath& object_path) { // return GetObject(object_path).second; // } // // Object GetObject(const dbus::ObjectPath& object_path) { // ObjectMap::iterator it = object_map_.find(object_path); // if (it != object_map_.end()) // return it->second; // // dbus::ObjectProxy* object_proxy = bus->GetObjectProxy(...); // // connect signals, etc. // // Properties* properties = new Properties( // object_proxy, // base::BindRepeating(&PropertyChanged, // weak_ptr_factory_.GetWeakPtr(), // object_path)); // properties->ConnectSignals(); // properties->GetAll(); // // Object object = std::make_pair(object_proxy, properties); // object_map_[object_path] = object; // return object; // } // }; // // This now allows code using the client implementation to access properties // in a type-safe manner, and assuming the PropertyChanged callback is // propagated up to observers, be notified of changes. A typical access of // the current value of the name property would be: // // ExampleClient::Properties* p = example_client->GetProperties(object_path); // std::string name = p->name.value(); // // Normally these values are updated from signals emitted by the remote object, // in case an explicit round-trip is needed to obtain the current value, the // Get() method can be used and indicates whether or not the value update was // successful. The updated value can be obtained in the callback using the // value() method. // // p->children.Get(base::BindOnce(&OnGetChildren)); // // A new value can be set using the Set() method, the callback indicates // success only; it is up to the remote object when (and indeed if) it updates // the property value, and whether it emits a signal or a Get() call is // required to obtain it. // // p->version.Set(20, base::BindOnce(&OnSetVersion)) namespace dbus { // D-Bus Properties interface constants, declared here rather than // in property.cc because template methods use them. const char kPropertiesInterface[] = …; const char kPropertiesGetAll[] = …; const char kPropertiesGet[] = …; const char kPropertiesSet[] = …; const char kPropertiesChanged[] = …; class PropertySet; // PropertyBase is an abstract base-class consisting of the parts of // the Property<> template that are not type-specific, such as the // associated PropertySet, property name, and the type-unsafe parts // used by PropertySet. class CHROME_DBUS_EXPORT PropertyBase { … }; // PropertySet groups a collection of properties for a remote object // together into a single structure, fixing their types and name such // that calls made through it are type-safe. // // Clients always sub-class this to add the properties, and should always // provide a constructor that chains up to this and then calls // RegisterProperty() for each property defined. // // After creation, client code should call ConnectSignals() and most likely // GetAll() to seed initial values and update as changes occur. class CHROME_DBUS_EXPORT PropertySet { … }; // Property template, this defines the type-specific and type-safe methods // of properties that can be accessed as members of a PropertySet structure. // // Properties provide a cached value that has an initial sensible default // until the reply to PropertySet::GetAll() is retrieved and is updated by // all calls to that method, PropertySet::Get() and property changed signals // also handled by PropertySet. It can be obtained by calling value() on the // property. // // It is recommended that this cached value be used where necessary, with // code using PropertySet::PropertyChangedCallback to be notified of changes, // rather than incurring a round-trip to the remote object for each property // access. // // Where a round-trip is necessary, the Get() method is provided. And to // update the remote object value, the Set() method is also provided; these // both simply call methods on PropertySet. // // Handling of particular D-Bus types is performed via specialization, // typically the PopValueFromReader() and AppendSetValueToWriter() methods // will need to be provided, and in rare cases a constructor to provide a // default value. Specializations for basic D-Bus types, strings, object // paths and arrays are provided for you. template <class T> class CHROME_DBUS_EXPORT Property : public PropertyBase { … }; // Clang and GCC don't agree on how attributes should work for explicitly // instantiated templates. GCC ignores attributes on explicit instantiations // (and emits a warning) while Clang requires the visiblity attribute on the // explicit instantiations for them to be visible to other compilation units. // Hopefully clang and GCC agree one day, and this can be cleaned up: // https://llvm.org/bugs/show_bug.cgi?id=24815 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" template <> CHROME_DBUS_EXPORT Property<uint8_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<uint8_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<uint8_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<uint8_t>; template <> CHROME_DBUS_EXPORT Property<bool>::Property(); template <> CHROME_DBUS_EXPORT bool Property<bool>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<bool>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<bool>; template <> CHROME_DBUS_EXPORT Property<int16_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<int16_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<int16_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<int16_t>; template <> CHROME_DBUS_EXPORT Property<uint16_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<uint16_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<uint16_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<uint16_t>; template <> CHROME_DBUS_EXPORT Property<int32_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<int32_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<int32_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<int32_t>; template <> CHROME_DBUS_EXPORT Property<uint32_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<uint32_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<uint32_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<uint32_t>; template <> CHROME_DBUS_EXPORT Property<int64_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<int64_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<int64_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<int64_t>; template <> CHROME_DBUS_EXPORT Property<uint64_t>::Property(); template <> CHROME_DBUS_EXPORT bool Property<uint64_t>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<uint64_t>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<uint64_t>; template <> CHROME_DBUS_EXPORT Property<double>::Property(); template <> CHROME_DBUS_EXPORT bool Property<double>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<double>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<double>; template <> CHROME_DBUS_EXPORT bool Property<std::string>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<std::string>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::string>; template <> CHROME_DBUS_EXPORT bool Property<ObjectPath>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<ObjectPath>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<ObjectPath>; template <> CHROME_DBUS_EXPORT bool Property<std::vector<std::string>>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property< std::vector<std::string>>::AppendSetValueToWriter(MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::vector<std::string>>; template <> CHROME_DBUS_EXPORT bool Property<std::vector<ObjectPath>>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property< std::vector<ObjectPath>>::AppendSetValueToWriter(MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::vector<ObjectPath>>; template <> CHROME_DBUS_EXPORT bool Property<std::vector<uint8_t>>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<std::vector<uint8_t>>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::vector<uint8_t>>; template <> CHROME_DBUS_EXPORT bool Property<std::map<std::string, std::string>>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<std::map<std::string, std::string>>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::map<std::string, std::string>>; template <> CHROME_DBUS_EXPORT bool Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>:: PopValueFromReader(MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>:: AppendSetValueToWriter(MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>; template <> CHROME_DBUS_EXPORT bool Property<std::map<std::string, std::vector<uint8_t>>>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<std::map<std::string, std::vector<uint8_t>>>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::map<std::string, std::vector<uint8_t>>>; template <> CHROME_DBUS_EXPORT bool Property<std::map<uint16_t, std::vector<uint8_t>>>::PopValueFromReader( MessageReader* reader); template <> CHROME_DBUS_EXPORT void Property<std::map<uint16_t, std::vector<uint8_t>>>::AppendSetValueToWriter( MessageWriter* writer); extern template class CHROME_DBUS_EXPORT Property<std::map<uint16_t, std::vector<uint8_t>>>; #pragma GCC diagnostic pop } // namespace dbus #endif // DBUS_PROPERTY_H_