0

Owned class-properties using std::unique_ptr and optionally cascading properties.

This is intended to support the work in this CL: cl/2982960

Bug: None
Change-Id: Ifc0ea84b5d32cb9d55d0fdbbb029290567d38072
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3016342
Commit-Queue: Allen Bauer <kylixrd@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Peter Boström <pbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#901150}
This commit is contained in:
Allen Bauer
2021-07-13 20:15:22 +00:00
committed by Chromium LUCI CQ
parent 0d8764978e
commit cc00d9a76b
5 changed files with 161 additions and 39 deletions

@@ -871,7 +871,8 @@ void Window::SetNativeWindowProperty(const char* key, void* value) {
} }
void* Window::GetNativeWindowProperty(const char* key) const { void* Window::GetNativeWindowProperty(const char* key) const {
return reinterpret_cast<void*>(GetPropertyInternal(key, 0)); return reinterpret_cast<void*>(GetPropertyInternal(key, 0,
/*search_parent=*/false));
} }
void Window::OnDeviceScaleFactorChanged(float old_device_scale_factor, void Window::OnDeviceScaleFactorChanged(float old_device_scale_factor,

@@ -7,6 +7,8 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include "base/notreached.h"
namespace ui { namespace ui {
PropertyHandler::PropertyHandler() = default; PropertyHandler::PropertyHandler() = default;
@@ -28,7 +30,7 @@ int64_t PropertyHandler::SetPropertyInternal(const void* key,
PropertyDeallocator deallocator, PropertyDeallocator deallocator,
int64_t value, int64_t value,
int64_t default_value) { int64_t default_value) {
int64_t old = GetPropertyInternal(key, default_value); int64_t old = GetPropertyInternal(key, default_value, false);
if (value == default_value) { if (value == default_value) {
prop_map_.erase(key); prop_map_.erase(key);
} else { } else {
@@ -53,12 +55,30 @@ void PropertyHandler::ClearProperties() {
prop_map_.clear(); prop_map_.clear();
} }
PropertyHandler* PropertyHandler::GetParentHandler() const {
// If you plan on using cascading properties, you must override this method
// to return the "parent" handler. If you want to use cascading properties in
// scenarios where there isn't a notion of a parent, just override this method
// and return null.
NOTREACHED();
return nullptr;
}
int64_t PropertyHandler::GetPropertyInternal(const void* key, int64_t PropertyHandler::GetPropertyInternal(const void* key,
int64_t default_value) const { int64_t default_value,
auto iter = prop_map_.find(key); bool search_parent) const {
if (iter == prop_map_.end()) const PropertyHandler* handler = this;
return default_value; while (handler) {
return iter->second.value; auto iter = handler->prop_map_.find(key);
if (iter == handler->prop_map_.end()) {
if (!search_parent)
break;
handler = handler->GetParentHandler();
continue;
}
return iter->second.value;
}
return default_value;
} }
std::set<const void*> PropertyHandler::GetAllPropertyKeys() const { std::set<const void*> PropertyHandler::GetAllPropertyKeys() const {

@@ -45,6 +45,18 @@
// If the properties are used outside the file where they are defined // If the properties are used outside the file where they are defined
// their accessor methods should also be declared in a suitable header // their accessor methods should also be declared in a suitable header
// using DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(FOO_EXPORT, MyType) // using DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(FOO_EXPORT, MyType)
//
// Cascading properties:
//
// Use the DEFINE_CASCADING_XXX macros to create a class property type that
// will automatically search up an instance hierarchy for the first defined
// property. This only affects the GetProperty() call. SetProperty() will
// still explicitly set the value on the given instance. This is useful for
// hierarchies of instances which a single set property can effect a whole sub-
// tree of instances.
//
// In order to use this feature, you must override GetParentHandler() on the
// class that inherits PropertyHandler.
namespace ui { namespace ui {
@@ -55,6 +67,7 @@ template<typename T>
struct ClassProperty { struct ClassProperty {
T default_value; T default_value;
const char* name; const char* name;
bool cascading = false;
PropertyDeallocator deallocator; PropertyDeallocator deallocator;
}; };
@@ -79,7 +92,9 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler {
// Sets the |value| of the given class |property|. Setting to the default // Sets the |value| of the given class |property|. Setting to the default
// value (e.g., NULL) removes the property. The lifetime of objects set as // value (e.g., NULL) removes the property. The lifetime of objects set as
// values of unowned properties is managed by the caller (owned properties are // values of unowned properties is managed by the caller (owned properties are
// freed when they are overwritten or cleared). // freed when they are overwritten or cleared). NOTE: This should NOT be
// for passing a raw pointer for owned properties. Prefer the std::unique_ptr
// version below.
template<typename T> template<typename T>
void SetProperty(const ClassProperty<T>* property, T value); void SetProperty(const ClassProperty<T>* property, T value);
@@ -97,8 +112,16 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler {
template <typename T> template <typename T>
void SetProperty(const ClassProperty<T*>* property, T&& value); void SetProperty(const ClassProperty<T*>* property, T&& value);
// Sets the |value| of the given class |property|, which must be an owned
// property and of pointer type. Use std::make_unique<> or base::WrapUnique to
// ensure proper ownership transfer.
template <typename T>
T* SetProperty(const ClassProperty<T*>* property, std::unique_ptr<T> value);
// Returns the value of the given class |property|. Returns the // Returns the value of the given class |property|. Returns the
// property-specific default value if the property was not previously set. // property-specific default value if the property was not previously set.
// The return value is the raw pointer useful for accessing the value
// contents.
template<typename T> template<typename T>
T GetProperty(const ClassProperty<T>* property) const; T GetProperty(const ClassProperty<T>* property) const;
@@ -115,6 +138,10 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler {
virtual void AfterPropertyChange(const void* key, int64_t old_value) {} virtual void AfterPropertyChange(const void* key, int64_t old_value) {}
void ClearProperties(); void ClearProperties();
// Override this function when inheriting this class on a class or classes
// in which instances are arranged in a parent-child relationship and
// the intent is to use cascading properties.
virtual PropertyHandler* GetParentHandler() const;
// Called by the public {Set,Get,Clear}Property functions. // Called by the public {Set,Get,Clear}Property functions.
int64_t SetPropertyInternal(const void* key, int64_t SetPropertyInternal(const void* key,
@@ -122,7 +149,13 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler {
PropertyDeallocator deallocator, PropertyDeallocator deallocator,
int64_t value, int64_t value,
int64_t default_value); int64_t default_value);
int64_t GetPropertyInternal(const void* key, int64_t default_value) const; // |search_parent| is required here for the setters to be able to look up the
// current value of property only on the current instance without searching
// the parent handler. This value is sent with the AfterPropertyChange()
// notification.
int64_t GetPropertyInternal(const void* key,
int64_t default_value,
bool search_parent) const;
private: private:
// Value struct to keep the name and deallocator for this property. // Value struct to keep the name and deallocator for this property.
@@ -177,11 +210,13 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHelper {
(*property->deallocator)(old); (*property->deallocator)(old);
} }
} }
template<typename T> template <typename T>
static T Get(const ::ui::PropertyHandler* handler, static T Get(const ::ui::PropertyHandler* handler,
const ::ui::ClassProperty<T>* property) { const ::ui::ClassProperty<T>* property,
bool allow_cascade) {
return ClassPropertyCaster<T>::FromInt64(handler->GetPropertyInternal( return ClassPropertyCaster<T>::FromInt64(handler->GetPropertyInternal(
property, ClassPropertyCaster<T>::ToInt64(property->default_value))); property, ClassPropertyCaster<T>::ToInt64(property->default_value),
property->cascading && allow_cascade));
} }
template<typename T> template<typename T>
static void Clear(::ui::PropertyHandler* handler, static void Clear(::ui::PropertyHandler* handler,
@@ -202,13 +237,13 @@ template <typename T>
void PropertyHandler::SetProperty(const ClassProperty<T*>* property, void PropertyHandler::SetProperty(const ClassProperty<T*>* property,
const T& value) { const T& value) {
// Prevent additional heap allocation if possible. // Prevent additional heap allocation if possible.
T* const old = GetProperty(property); T* const old = subtle::PropertyHelper::Get<T*>(this, property, false);
if (old) { if (old) {
T temp(*old); T temp(*old);
*old = value; *old = value;
AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp)); AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp));
} else { } else {
SetProperty(property, new T(value)); SetProperty(property, std::make_unique<T>(value));
} }
} }
@@ -216,16 +251,26 @@ template <typename T>
void PropertyHandler::SetProperty(const ClassProperty<T*>* property, void PropertyHandler::SetProperty(const ClassProperty<T*>* property,
T&& value) { T&& value) {
// Prevent additional heap allocation if possible. // Prevent additional heap allocation if possible.
T* const old = GetProperty(property); T* const old = subtle::PropertyHelper::Get<T*>(this, property, false);
if (old) { if (old) {
T temp(std::move(*old)); T temp(std::move(*old));
*old = std::forward<T>(value); *old = std::forward<T>(value);
AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp)); AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp));
} else { } else {
SetProperty(property, new T(std::forward<T>(value))); SetProperty(property, std::make_unique<T>(std::forward<T>(value)));
} }
} }
template <typename T>
T* PropertyHandler::SetProperty(const ClassProperty<T*>* property,
std::unique_ptr<T> value) {
// This form only works for 'owned' properties.
DCHECK(property->deallocator);
T* value_ptr = value.get();
subtle::PropertyHelper::Set<T*>(this, property, value.release());
return value_ptr;
}
} // namespace ui } // namespace ui
// Macros to declare the property getter/setter template functions. // Macros to declare the property getter/setter template functions.
@@ -253,7 +298,7 @@ void PropertyHandler::SetProperty(const ClassProperty<T*>* property,
template <> \ template <> \
EXPORT T \ EXPORT T \
PropertyHandler::GetProperty(const ClassProperty<T>* property) const { \ PropertyHandler::GetProperty(const ClassProperty<T>* property) const { \
return subtle::PropertyHelper::Get<T>(this, property); \ return subtle::PropertyHelper::Get<T>(this, property, true); \
} \ } \
template <> \ template <> \
EXPORT void PropertyHandler::ClearProperty( \ EXPORT void PropertyHandler::ClearProperty( \
@@ -267,18 +312,36 @@ void PropertyHandler::SetProperty(const ClassProperty<T*>* property,
#define DEFINE_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ #define DEFINE_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
static_assert(sizeof(TYPE) <= sizeof(int64_t), "property type too large"); \ static_assert(sizeof(TYPE) <= sizeof(int64_t), "property type too large"); \
const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \ const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, false, \
nullptr}; \
const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value; const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value;
#define DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ #define DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
namespace { \ namespace { \
void Deallocator##NAME(int64_t p) { \ void Deallocator##NAME(int64_t p) { \
enum { type_must_be_complete = sizeof(TYPE) }; \ enum { type_must_be_complete = sizeof(TYPE) }; \
delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \ delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \
} \ } \
const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, \ const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, false, \
&Deallocator##NAME}; \ &Deallocator##NAME}; \
} /* namespace */ \ } /* namespace */ \
const ::ui::ClassProperty<TYPE*>* const NAME = &NAME##_Value;
#define DEFINE_CASCADING_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
static_assert(sizeof(TYPE) <= sizeof(int64_t), "property type too large"); \
const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, true, \
nullptr}; \
const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value;
#define DEFINE_CASCADING_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
namespace { \
void Deallocator##NAME(int64_t p) { \
enum { type_must_be_complete = sizeof(TYPE) }; \
delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \
} \
const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, true, \
&Deallocator##NAME}; \
} /* namespace */ \
const ::ui::ClassProperty<TYPE*>* const NAME = &NAME##_Value; const ::ui::ClassProperty<TYPE*>* const NAME = &NAME##_Value;
#endif // UI_BASE_CLASS_PROPERTY_H_ #endif // UI_BASE_CLASS_PROPERTY_H_

@@ -22,7 +22,9 @@ namespace {
class TestProperty { class TestProperty {
public: public:
TestProperty() {} TestProperty() = default;
TestProperty(const TestProperty&) = delete;
TestProperty& operator=(const TestProperty&) = delete;
~TestProperty() { ~TestProperty() {
last_deleted_ = this; last_deleted_ = this;
} }
@@ -30,7 +32,18 @@ class TestProperty {
private: private:
static void* last_deleted_; static void* last_deleted_;
DISALLOW_COPY_AND_ASSIGN(TestProperty); };
class TestCascadingProperty {
public:
explicit TestCascadingProperty(ui::PropertyHandler* handler)
: handler_(handler) {}
~TestCascadingProperty() = default;
ui::PropertyHandler* handler() { return handler_; }
private:
ui::PropertyHandler* handler_;
}; };
void* TestProperty::last_deleted_ = nullptr; void* TestProperty::last_deleted_ = nullptr;
@@ -66,11 +79,15 @@ DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TestProperty, kOwnedKey, nullptr)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(AssignableTestProperty, DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(AssignableTestProperty,
kAssignableKey, kAssignableKey,
nullptr) nullptr)
DEFINE_CASCADING_OWNED_UI_CLASS_PROPERTY_KEY(TestCascadingProperty,
kCascadingOwnedKey,
nullptr)
} // namespace } // namespace
DEFINE_UI_CLASS_PROPERTY_TYPE(TestProperty*) DEFINE_UI_CLASS_PROPERTY_TYPE(TestProperty*)
DEFINE_UI_CLASS_PROPERTY_TYPE(AssignableTestProperty*) DEFINE_UI_CLASS_PROPERTY_TYPE(AssignableTestProperty*)
DEFINE_UI_CLASS_PROPERTY_TYPE(TestCascadingProperty*)
namespace ui { namespace ui {
namespace test { namespace test {
@@ -79,15 +96,20 @@ namespace {
class TestPropertyHandler : public PropertyHandler { class TestPropertyHandler : public PropertyHandler {
public: public:
TestPropertyHandler() = default;
explicit TestPropertyHandler(TestPropertyHandler* parent) : parent_(parent) {}
~TestPropertyHandler() override = default;
int num_events() const { return num_events_; } int num_events() const { return num_events_; }
protected: protected:
void AfterPropertyChange(const void* key, int64_t old_value) override { void AfterPropertyChange(const void* key, int64_t old_value) override {
++num_events_; ++num_events_;
} }
PropertyHandler* GetParentHandler() const override { return parent_; }
private: private:
int num_events_ = 0; int num_events_ = 0;
TestPropertyHandler* parent_ = nullptr;
}; };
const int kDefaultIntValue = -2; const int kDefaultIntValue = -2;
@@ -133,24 +155,23 @@ TEST(PropertyTest, OwnedProperty) {
{ {
PropertyHandler h; PropertyHandler h;
EXPECT_EQ(NULL, h.GetProperty(kOwnedKey)); EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey));
void* last_deleted = TestProperty::last_deleted(); void* last_deleted = TestProperty::last_deleted();
TestProperty* p1 = new TestProperty(); TestProperty* p1 =
h.SetProperty(kOwnedKey, p1); h.SetProperty(kOwnedKey, std::make_unique<TestProperty>());
EXPECT_EQ(p1, h.GetProperty(kOwnedKey)); EXPECT_EQ(p1, h.GetProperty(kOwnedKey));
EXPECT_EQ(last_deleted, TestProperty::last_deleted()); EXPECT_EQ(last_deleted, TestProperty::last_deleted());
TestProperty* p2 = new TestProperty(); TestProperty* p2 =
h.SetProperty(kOwnedKey, p2); h.SetProperty(kOwnedKey, std::make_unique<TestProperty>());
EXPECT_EQ(p2, h.GetProperty(kOwnedKey)); EXPECT_EQ(p2, h.GetProperty(kOwnedKey));
EXPECT_EQ(p1, TestProperty::last_deleted()); EXPECT_EQ(p1, TestProperty::last_deleted());
h.ClearProperty(kOwnedKey); h.ClearProperty(kOwnedKey);
EXPECT_EQ(NULL, h.GetProperty(kOwnedKey)); EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey));
EXPECT_EQ(p2, TestProperty::last_deleted()); EXPECT_EQ(p2, TestProperty::last_deleted());
p3 = new TestProperty(); p3 = h.SetProperty(kOwnedKey, std::make_unique<TestProperty>());
h.SetProperty(kOwnedKey, p3);
EXPECT_EQ(p3, h.GetProperty(kOwnedKey)); EXPECT_EQ(p3, h.GetProperty(kOwnedKey));
EXPECT_EQ(p2, TestProperty::last_deleted()); EXPECT_EQ(p2, TestProperty::last_deleted());
} }
@@ -162,8 +183,8 @@ TEST(PropertyTest, AcquireAllPropertiesFrom) {
std::unique_ptr<PropertyHandler> src = std::make_unique<PropertyHandler>(); std::unique_ptr<PropertyHandler> src = std::make_unique<PropertyHandler>();
void* last_deleted = TestProperty::last_deleted(); void* last_deleted = TestProperty::last_deleted();
EXPECT_FALSE(src->GetProperty(kOwnedKey)); EXPECT_FALSE(src->GetProperty(kOwnedKey));
TestProperty* p1 = new TestProperty(); TestProperty* p1 =
src->SetProperty(kOwnedKey, p1); src->SetProperty(kOwnedKey, std::make_unique<TestProperty>());
src->SetProperty(kIntKey, INT_MAX); src->SetProperty(kIntKey, INT_MAX);
// dest will take ownership of the owned property. Existing properties with // dest will take ownership of the owned property. Existing properties with
@@ -296,5 +317,21 @@ TEST(PropertyTest, PropertyChangedEvent) {
EXPECT_EQ(6, h.num_events()); EXPECT_EQ(6, h.num_events());
} }
TEST(PropertyTest, CascadingProperties) {
TestPropertyHandler h;
TestPropertyHandler h2(&h);
// Set the property on the parent handler.
h.SetProperty(kCascadingOwnedKey,
std::make_unique<TestCascadingProperty>(&h));
// Get the property value from the child handler.
auto* value = h2.GetProperty(kCascadingOwnedKey);
EXPECT_TRUE(value);
// The property value should have a reference to |h|.
EXPECT_EQ(&h, value->handler());
}
} // namespace test } // namespace test
} // namespace ui } // namespace ui

@@ -7,6 +7,7 @@ component("stub") {
deps = [ deps = [
"//base", "//base",
"//ui/base",
"//ui/gfx", "//ui/gfx",
"//ui/gfx/geometry", "//ui/gfx/geometry",
"//ui/platform_window", "//ui/platform_window",