chromium/dbus/property.h

// 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_