Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to implement Q_PROPERTY READ/WRITE accessors without using member functions?

Tags:

c++11

qt

qml

Generally, a lot of code does nothing but get/set class members. For that I implemented a simple container class to have getters and setters associated to a "field". At a first sight this looks pretty ok and results in far less code. This is how the container class looks like:

Member.h

#include <functional>

template <class T>
class Member
{
public:
  T data;

  using Getter_t = std::function<T(void)>;
  using Setter_t = std::function<void(T)>;
  using Notify_t = std::function<void(void)>;

  Setter_t m_setterFunc;
  Getter_t m_getterFunc;
  Notify_t m_notifyFunc;

  Member()
  {
      this->m_getterFunc  = [=] (void) -> T  { return this->data; };
      this->m_setterFunc  = [=] (T data) -> void { this->data = data; };
      this->m_notifyFunc  = [] (void) -> void { };
  }

  auto get() -> T { return this->m_getterFunc(); }
  auto set(T data) -> void { this->m_setterFunc(data); this->m_notifyFunc(); }

  auto getter(Getter_t func) -> Member& { this->m_getterFunc  = func; return *this; }
  auto setter(Setter_t func) -> Member& { this->m_setterFunc  = func; return *this; }
  auto notify(Notify_t func) -> Member& { this->m_notifyFunc  = func; return *this; }

  ~Member() { }
};

I know some things are not perfect yet but that's okay for now. The next few lines show how Member instances are defined and the simple and convenient way to access underlying data. get, set and notify functions can be replaced by lambdas or function pointers to override custom behavior.

main.cpp

#include <iostream>

#include "Member.h"

class MyClass
{
public:
    Member<int> foo;
    Member<std::string> bar;

    void barChanged() { std::cout << "bar changed\n"; }
};

auto main(int argc, const char * argv[]) -> int
{
    MyClass instance;

    instance.foo.notify([] () -> void { std::cout << "foo changed\n"; });
    instance.bar.notify(std::bind(&MyClass::barChanged, instance));

    instance.foo.set(10);
    instance.bar.set("some string");

    std::cout << instance.foo.get() << " " << instance.bar.get() << std::endl;

    return 0;
}

The problem now is that the Q_PROPERTY macro expects function names for the READ and WRITE accessors and I'm back at where I started: I have to write get and set functions for each property explicitly. Exactly what I wanted to avoid.

class MyOtherClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool flag READ getFlag WRITE setFlag NOTIFY flagChanged);
public:
    Member<bool> m_flag;
    auto getFlag() -> bool { return m_flag.get(); }
    auto setFlag(bool flag) -> void { this->m_flag.set(flag); }
};

Is it possible to directly use the already existing m_flag.get and m_flag.set functions? I tried the obvious things but they were either rejected by the moc or resulted in too much code.

Edit

As mentioned below, the MEMBER keyword makes it possible to have properties without specifying get and set functions. However, private members then only can be accessed by their names (this->property("myPropertyName")) and also there's no way to achieve more than "plain" get and set.

To make it more clear: The motivation is not to just avoid writing get and set functions but trying to implement a flexible member system which

  • by default performs get/set as expected
  • supports custom logic (for example forward newly set values to some other instance)
  • can be used for C++ class members and is compatible with Qt properties

And the only missing piece is the bridge between the Q_PROPERTY READ/WRITEaccessors and the get/set methods of the Member class.

Thanks for any help!

like image 277
qCring Avatar asked Aug 23 '15 16:08

qCring


1 Answers

I don't think that it's possible to redirect READ or WRITE property methods to some other internal or external object without writing wrappers, but if your getters and setters do not do anything except return or set data: there is MEMBER variable association at least in latest Qt versions.

From Qt Doc:

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [CONSTANT]
           [FINAL])

A READ accessor function is required if no MEMBER variable was specified. It is for reading the property value. Ideally, a const function is used for this purpose, and it must return either the property's type or a const reference to that type. e.g., QWidget::focus is a read-only property with READ function, QWidget::hasFocus().

A WRITE accessor function is optional. It is for setting the property value. It must return void and must take exactly one argument, either of the property's type or a pointer or reference to that type. e.g., QWidget::enabled has the WRITE function QWidget::setEnabled(). Read-only properties do not need WRITE functions. e.g., QWidget::focus has no WRITE function.

A MEMBER variable association is required if no READ accessor function is specified. This makes the given member variable readable and writable without the need of creating READ and WRITE accessor functions. It's still possible to use READ or WRITE accessor functions in addition to MEMBER variable association (but not both), if you need to control the variable access.

Using MEMBER you do not need to write getters and setters.

like image 176
Elohim Meth Avatar answered Oct 18 '22 12:10

Elohim Meth