dimanche 23 août 2015

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

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.

Thanks for any help!

Aucun commentaire:

Enregistrer un commentaire