[Interest] Requesting QObject::bind() method

Jean-Michaël Celerier jeanmichael.celerier at gmail.com
Wed Mar 22 13:50:47 CET 2017


>  QObject::bind(sender, &Sender::signalName, receiver, &Receiver::setter);

> This is exactly connect() we already have.

I think that bind adds one more step : it does the set. ie. bind would be :

     QObject::bind(sender, &Sender::signalName, &Sender::getter, receiver,
&Receiver::setter);

and would be equivalent to :

    QObject::connect(sender, &Sender::signalName, receiver,
&Receiver::setter);
    receiver->setter(sender->getter());

Now, about the overall string problem, I think that most people would
rather do :

     QObject::bind(sender, "source", receiver, "target");

And actually, with modern C++, this can be made typesafe : for instance the
boost.metaparse guys are
able to convert (constexpr) strings to types :
http://www.boost.org/doc/libs/1_63_0/doc/html/metaparse.html
at compile time. So it would not be unthinkable to have moc generate the
appropriate parser and "compile-time property" class

At the end of this mail there is an example implementation (that uses a
string literal from boost.hana) and allows to do the following at
compile-time:  (requires c++17; I tested with clang 4 and I believe gcc 7
would work)

    bind(sender, "source"_s, receiver, "compatibleTarget"_s);

While this won't compile

    bind(sender, "source"_s, receiver, "incompatibleTarget"_s);

So the SIGNAL() and SLOT() macros could maybe be modified this way (Qt6
anyone ? ;p) to have compile-checked versions (SSIGNAL() / SSLOT()).

#include <string>
#include <type_traits>

#define  BOOST_HANA_CONFIG_ENABLE_STRING_UDL
#include <boost/hana.hpp>
#include <boost/hana/string.hpp>
using namespace boost::hana::literals;

template<typename Param1, typename Param2>
static const constexpr bool constructible = std::is_constructible<typename
Param2::type, typename Param1::type>::value;
template<typename Param1, typename Param2>
using is_compatible = std::enable_if_t<std::is_constructible<typename
Param2::type, typename Param1::type>::value>;
template<typename Param1, typename Param2>
using not_compatible = std::enable_if_t<!std::is_constructible<typename
Param2::type, typename Param1::type>::value>;

class foo
{
  public:
  int getProp1() { return 0; }
  void setProp1(int) { }
  void notifyProp1(int);

  std::string getProp2() { return ""; }
  void setProp2(std::string) { }
  void notifyProp2(std::string);

  // Generate the following struct / functions with moc
  // or send a bottle of champagne + chocolates to ~ogoffart to put it in
Verdigris and
  // integrate it with W_PROPERTY

  struct foo_prop1
  {
    using type = int;
    static const constexpr auto name = "prop1"_s;
    static const constexpr auto getter = &foo::getProp1;
    static const constexpr auto setter = &foo::setProp1;
    static const constexpr auto notifier = &foo::setProp1;
  };

  struct foo_prop2
  {
    using type = std::string;
    static const constexpr auto name = "prop2"_s;
    static const constexpr auto getter = &foo::getProp2;
    static const constexpr auto setter = &foo::setProp2;
    static const constexpr auto notifier = &foo::setProp2;
  };

  template<typename T1, typename Param1, typename T2, typename Str2>
  static constexpr void bind_receiver(T1& obj1, const Param1&, T2& obj2,
Str2 str2)
  {
    if constexpr (str2 == foo_prop1::name)
    {
      (obj2.*foo_prop1::setter)((obj1.*Param1::getter)());
    }
    else if constexpr(str2 == foo_prop2::name)
    {
      (obj2.*foo_prop2::setter)((obj1.*Param1::getter)());
    }
  }

  template<typename T1, typename Str1, typename T2, typename Str2>
  static constexpr void bind_sender(T1& obj1, Str1 str1, T2& obj2, Str2
str2)
  {
    if constexpr (str1 == foo_prop1::name)
    {
      obj2.bind_receiver(obj1, foo_prop1{}, obj2, str2);
    }
    else if constexpr (str1 == foo_prop2::name)
    {
      obj2.bind_receiver(obj1, foo_prop2{}, obj2, str2);
    }
  }



  template<typename Param1, typename Str2>
  static constexpr bool can_bind_receiver()
  {
    if constexpr (Str2{} == foo_prop1::name)
    {
      return constructible<foo_prop1, Param1>;
    }
    else if constexpr(Str2{} == foo_prop2::name)
    {
      return constructible<foo_prop2, Param1>;
    }
    else
    {
      return false;
    }
  }

  template<typename Str1, typename T2, typename Str2>
  static constexpr bool can_bind_sender()
  {
    if constexpr (Str1{} == foo_prop1::name)
    {
      return T2::template can_bind_receiver<foo_prop1, Str2>();
    }
    else if constexpr (Str1{} == foo_prop2::name)
    {
      return T2::template can_bind_receiver<foo_prop2, Str2>();
    }
    else
    {
      return false;
    }
  }
};

template<typename T1, typename Str1, typename T2, typename Str2>
constexpr void bind(T1& obj1, Str1 str1, T2& obj2, Str2 str2)
{
  static_assert(T1::template can_bind_sender<Str1, T2, Str2>(), "The types
are not compatible");
  T1::bind_sender(obj1, str1, obj2, str2);
}

int main()
{
  foo f1;
  foo f2;

  bind(f1, "prop1"_s, f2, "prop1"_s); // works
//  bind(f1, "prop1"_s, f2, "prop2"_s); // does not compile

  return 0;
}


Best,
-------
Jean-Michaël Celerier
http://www.jcelerier.name

On Wed, Mar 22, 2017 at 8:13 AM, Prashanth Udupa <prashanth.udupa at gmail.com>
wrote:

> Hi Thiago,
>
>
>> But let me put it this way: will not accept new text-based API for signal,
>> slots and properties in QObject. You can do that externally, as you've
>> done
>> it, but I won't take it in QtCore.
>
>
> Ok.
>
>
>> So we need the compile-time checking. What can we use to identify the
>> property
>> changing? A read-only property that changes has two C++ identifiers: the
>> getter
>> and the notify signal. And what can we use to identify the receiving
>> property?
>
> I agree, QObject API should ideally have compile time checks rather than
> runtime debug dumps or bool isValid()s.
>
>> The getter and the setter.
>> So this new functionality would be:
>>         QObject::bind(sender, &Sender::signalName, receiver,
>> &Receiver::setter);
>> This is exactly connect() we already have.
>>
>
> True. Makes sense.
>
> Thanks,
> Prashanth
>
>
> On Wed, Mar 22, 2017 at 12:19 PM, Thiago Macieira <
> thiago.macieira at intel.com> wrote:
>
>> Em terça-feira, 21 de março de 2017, às 22:58:38 PDT, Thiago Macieira
>> escreveu:
>> > Em terça-feira, 21 de março de 2017, às 19:38:19 PDT, Prashanth Udupa
>> >
>> > escreveu:
>> > > QSlider *slider = ...
>> > > QLabel *label = ....
>> > > QObject::bind(slider, "value", label, "text”);
>> >
>> > This is a bad example because "text" is a string and would require a
>> > conversion. But let's say we're connecting a slider to a QProgressBar.
>> You
>> > can just write:
>> >
>> > QObject::connect(slider, &QSlider:valueChanged,
>> >               &progressBar, &QProgressBar::setValue);
>>
>> And your other example:
>>
>> > QSlider *slider = ....
>> > QLabel *label = ....
>> > QObject::bind(slider, "value", label, "text", [](const QVariant &v) {
>> return
>> > v.toInt()*2; });
>>
>> Would be:
>>
>> QObject::connect(slider, &QSlider::valueChanged, &label, [label](int
>> value) {
>>                 label->setText(QString::number(value * 2));
>>         });
>>
>> But let me put it this way: will not accept new text-based API for signal,
>> slots and properties in QObject. You can do that externally, as you've
>> done
>> it, but I won't take it in QtCore.
>>
>> So we need the compile-time checking. What can we use to identify the
>> property
>> changing? A read-only property that changes has two C++ identifiers: the
>> getter
>> and the notify signal. And what can we use to identify the receiving
>> property?
>> The getter and the setter.
>>
>> So this new functionality would be:
>>
>>         QObject::bind(sender, &Sender::signalName, receiver,
>> &Receiver::setter);
>>
>> This is exactly connect() we already have.
>>
>> --
>> Thiago Macieira - thiago.macieira (AT) intel.com
>>   Software Architect - Intel Open Source Technology Center
>>
>> _______________________________________________
>> Interest mailing list
>> Interest at qt-project.org
>> http://lists.qt-project.org/mailman/listinfo/interest
>>
>
>
>
> --
> Thanks and Warm Regards,
> Prashanth N Udupa
> URL: http://www.prashanthudupa.com
>
> Scan the QR Code below using KitApp
> <https://play.google.com/store/apps/details?id=me.kitapp.kitapp> for
> Android & message me on my phone without revealing your phone number!
>
>
>
> _______________________________________________
> Interest mailing list
> Interest at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/interest
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20170322/27c6b1ce/attachment.html>


More information about the Interest mailing list