Discussion:
QObject: new signals-slots syntax
Olivier Goffart
2011-08-09 15:05:53 UTC
Permalink
Hi,

I Have been working on the new syntax for signals and slot.
http://developer.qt.nokia.com/wiki/New_Signal_Slot_Syntax

It looks like that.

connect(sender, &Sender::valueChanged,
receiver, &Receiver::updateValue );

The idea is too have more check at compile time (typos, type checking, no
issues with typedefs and namespaces)

With recent compilers, you can even do: (using lambdas)

connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
receiver->updateValue("senderValue", newValue);
} );

More information on the wiki.

I think the code is ready to be merged in Qt5, so I made a merge requests:
https://qt.gitorious.org/qt/qtbase/merge_requests/42

Feedback welcome.
--
Olivier
Richard Moore
2011-08-10 19:56:31 UTC
Permalink
Post by Olivier Goffart
https://qt.gitorious.org/qt/qtbase/merge_requests/42
Feedback welcome.
Could you outline what if anything has been changed since QCS? I
remember that there were some areas you weren't happy with and were
suggesting could be removed.

Cheers

Rich.
Olivier Goffart
2011-08-11 09:00:24 UTC
Permalink
Post by Richard Moore
Post by Olivier Goffart
I think the code is ready to be merged in Qt5, so I made a merge
requests: https://qt.gitorious.org/qt/qtbase/merge_requests/42
Feedback welcome.
Could you outline what if anything has been changed since QCS? I
remember that there were some areas you weren't happy with and were
suggesting could be removed.
Not much has changed.
I removed the possibility to handle default argument in slot (that is, if the
signal has more arguments than the slot)
I changed the return value of connect to allow disconnection.
I updated all the template code to support up to 6 number of arguments.
Peter Kuemmel
2011-09-21 09:52:03 UTC
Permalink
Post by Olivier Goffart
Hi,
I Have been working on the new syntax for signals and slot.
http://developer.qt.nokia.com/wiki/New_Signal_Slot_Syntax
It looks like that.
connect(sender, &Sender::valueChanged,
receiver, &Receiver::updateValue );
The idea is too have more check at compile time (typos, type checking, no
issues with typedefs and namespaces)
With recent compilers, you can even do: (using lambdas)
connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
receiver->updateValue("senderValue", newValue);
} );
More information on the wiki.
https://qt.gitorious.org/qt/qtbase/merge_requests/42
Feedback welcome.
I still see a problem in making signals public, this
completely breaks encapsulation, and makes it possible
for everyone to emit any signal.

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Frans Klaver
2011-09-21 10:19:14 UTC
Permalink
Post by Peter Kuemmel
I still see a problem in making signals public, this
completely breaks encapsulation, and makes it possible
for everyone to emit any signal.
I also see this as a potential problem. Making the signal public is
pretty much an invitation to misuse. Almost every text I've seen about
API design states that a good API makes misuse hard.

Of course it is currently possible for people to invoke the method
using the meta object. But a lot of people wouldn't touch the meta
objects with a ten foot pole.

Cheers,
Frans
Thiago Macieira
2011-09-21 13:12:21 UTC
Permalink
Post by Peter Kuemmel
I still see a problem in making signals public, this
completely breaks encapsulation, and makes it possible
for everyone to emit any signal.
There's no way around it. Since we're using the function pointer (actually,
the PMF) to identify the signal, you must have C++ access to the signal
function. That means you must be able to call it.

Other template-based signal libraries have the same problem. It cannot be
avoided unless you have a way to identify the signal which doesn't include
access to the actual signal implementation.

One solution to that would be to have an enum of signals, instead of
identifying by an object or by a PMF. However, handling enums is not very
easy, since extending the range becomes cumbersome -- how do you add a new
signal to a class that has been derived from?

As the old adage says, "all problems in C++ can be solved with another level
of indirection". So we can create an indirection layer to the enums (the
numeric IDs) and map at runtime. The easiest way of doing that is to have a
string-based approach. However, C++ has no reflection mechanism, so either
you're forced to write the string-matching code yourself, or you have to use a
code generator.

And then we're right back where we started: moc and the current syntax.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Thiago Macieira
2011-09-21 13:34:13 UTC
Permalink
Post by Thiago Macieira
There's no way around it. Since we're using the function pointer (actually,
the PMF) to identify the signal, you must have C++ access to the signal
function. That means you must be able to call it.
Other template-based signal libraries have the same problem. It cannot be
avoided unless you have a way to identify the signal which doesn't include
access to the actual signal implementation.
One solution to that would be to have an enum of signals, instead of
identifying by an object or by a PMF.
Another solution (also by way of adding an indirection) is to add a function
that emits, which is protected.

template <typename Klass, typename... Args>
void QObject::emitSignal(void (Klass:: *signal)(Args...), Args... args);

Then you'd write:
emitSignal(&Me::textChanged, newText);
instead of:
emit textChanged(newText);

The template magic to make that happen is already present.

However, it would be completely source-incompatible with the existing code,
which is a big no-no. It would require making the signal functions themselves
not do anything -- only serve as identifiers.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Stephen Kelly
2011-09-21 16:35:49 UTC
Permalink
Post by Thiago Macieira
Post by Thiago Macieira
There's no way around it. Since we're using the function pointer
(actually, the PMF) to identify the signal, you must have C++ access to
the signal function. That means you must be able to call it.
Other template-based signal libraries have the same problem. It cannot be
avoided unless you have a way to identify the signal which doesn't
include access to the actual signal implementation.
One solution to that would be to have an enum of signals, instead of
identifying by an object or by a PMF.
Another solution (also by way of adding an indirection) is to add a
function that emits, which is protected.
template <typename Klass, typename... Args>
void QObject::emitSignal(void (Klass:: *signal)(Args...), Args... args);
emitSignal(&Me::textChanged, newText);
emit textChanged(newText);
The template magic to make that happen is already present.
However, it would be completely source-incompatible with the existing
code, which is a big no-no. It would require making the signal functions
themselves not do anything -- only serve as identifiers.
For source compatibility they could be implemented by moc to work as
currently, no?
Stephen Kelly
2011-09-21 16:49:04 UTC
Permalink
Post by Stephen Kelly
Post by Thiago Macieira
Post by Thiago Macieira
There's no way around it. Since we're using the function pointer
(actually, the PMF) to identify the signal, you must have C++ access to
the signal function. That means you must be able to call it.
Other template-based signal libraries have the same problem. It cannot
be avoided unless you have a way to identify the signal which doesn't
include access to the actual signal implementation.
One solution to that would be to have an enum of signals, instead of
identifying by an object or by a PMF.
Another solution (also by way of adding an indirection) is to add a
function that emits, which is protected.
template <typename Klass, typename... Args>
void QObject::emitSignal(void (Klass:: *signal)(Args...), Args... args);
emitSignal(&Me::textChanged, newText);
emit textChanged(newText);
The template magic to make that happen is already present.
However, it would be completely source-incompatible with the existing
code, which is a big no-no. It would require making the signal functions
themselves not do anything -- only serve as identifiers.
For source compatibility they could be implemented by moc to work as
currently, no?
Oh, they need to keep the current implementation so that SIGNAL and SLOT
based connections still work. Never mind me.
Thiago Macieira
2011-09-21 17:20:06 UTC
Permalink
Post by Stephen Kelly
Post by Stephen Kelly
Post by Thiago Macieira
Another solution (also by way of adding an indirection) is to add a
function that emits, which is protected.
template <typename Klass, typename... Args>
void QObject::emitSignal(void (Klass:: *signal)(Args...), Args... args);
emitSignal(&Me::textChanged, newText);
emit textChanged(newText);
The template magic to make that happen is already present.
However, it would be completely source-incompatible with the existing
code, which is a big no-no. It would require making the signal functions
themselves not do anything -- only serve as identifiers.
For source compatibility they could be implemented by moc to work as
currently, no?
Oh, they need to keep the current implementation so that SIGNAL and SLOT
based connections still work. Never mind me.
The old connection mechanism is almost guaranteed to work.

The problem is to update "emit" lines, which are calls to functions that cause
the signal to be emitted.

Also note that the solution above doesn't fix the following case:

otherObject->emitSignal(&Them::textChanged, newText);

Just as it doesn't protect today:
emit otherObject->textChanged(newText);

The big difference is that "emitSignal" would be protected in QObject, so any
class deriving from QObject would have access to it.

We've been looking at this for a year. If anyone has more interesting ideas,
they are more than welcome. But please try to write the proof of concept first.

Our conclusion is that if you want compile-time checking of the arguments, you
need to give up the protectedness of the emission.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Frans Klaver
2011-09-21 21:19:43 UTC
Permalink
I've been following this discussion since the beginning, but only briefly
participated on devnet. It took a while for me to get my exact thoughts
about this lined up, and a lot has already been discussed, but here goes.
Post by Thiago Macieira
Our conclusion is that if you want compile-time checking of the
arguments, you need to give up the protectedness of the emission.
To be honest I never really understood why one would insist on
compile-time checking of something that was obviously intended to be used
and checked at run-time. However, I can't really make a proper argument
against the new syntax though if this checking is actually a hard
requirement for the future. I can only agree that as it looks now, the
solution that gives the cleanest code and is probably most natural to use
will be the one with the public signals.

Bottom line for me, though, is that I don't agree with the trade-off made
here: you're trading compiler checks on unintentionally messing about from
unrelated pieces of code for compiler checks on possible connections. One
of the two is more likely to produce weird results, and my gut says a
solution with publicly accessible signals is the one. Get some bad signal
names, code completion, and your average lazy developer (are there
non-lazy devs?) and you're set. The simple fact is that public signals
doesn't discourage you from writing bad code. Isn't API design about just
that? Making writing good and predictable code easy and bad code harder?
Consider:

otherObject->someSignal(arg1, arg2, arg3);

and the current

QMetaObject::invokeMethod(otherObject, "someSignal(int,int,int)",
arg1, arg2, arg3);

The first looks like your average piece of code and can easily be
overlooked, the second looks like messing around unless you know what
you're doing. When reviewing or debugging, I'd rather have the latter
piece of code, simply because it's suspiciously suspicious (and maybe even
wrong, didn't check that).

As far as I understand, the string based versions for run-time defined
signals and slots are still required. Sticking to the old syntax will keep
a common syntax for both use cases. If a connection is vitally important
to the workings of the system I'd just use Q_ASSERT(connect(...)).
Detection guaranteed.

There's my couple of cents.

Cheers,
Frans
Thiago Macieira
2011-09-21 21:43:34 UTC
Permalink
Post by Frans Klaver
To be honest I never really understood why one would insist on
compile-time checking of something that was obviously intended to be used
and checked at run-time.
It was never intended to be done only at runtime. It was done that way
originally because it was the only way it could be done.

Since then, moc has become useful and the syntax has solidified, so it hasn't
changed.
Post by Frans Klaver
Bottom line for me, though, is that I don't agree with the trade-off made
here: you're trading compiler checks on unintentionally messing about from
unrelated pieces of code for compiler checks on possible connections. One
What is worse for you, that someone unintentionally emits a signal, or that
you fail to connect because someone made a typo, or accidentally left the
variable names in the SIGNAL or SLOT definition. Not to mention in refactoring
this problem might creep in, because the code compiles but fails at runtime.
Don't forget you have to match *exactly* the signature, of the exact same
types, in the exact same typedef and namespace description.

You may say you're a very good developer and you check all connections (by
testing the return value or by running with QT_FATAL_WARNINGS=1). Are you sure
you tested all of the connection possibilities in your code? There is no
chance that there was a connect() call that went untested? How about your co-
workers, are they as good as you?
Post by Frans Klaver
of the two is more likely to produce weird results, and my gut says a
solution with publicly accessible signals is the one. Get some bad signal
I beg to differ. I think the lack of compile-time checking that the connection
can succeed is the one causing most issues.
Post by Frans Klaver
names, code completion, and your average lazy developer (are there
non-lazy devs?) and you're set. The simple fact is that public signals
doesn't discourage you from writing bad code. Isn't API design about just
that? Making writing good and predictable code easy and bad code harder?
otherObject->someSignal(arg1, arg2, arg3);
and the current
QMetaObject::invokeMethod(otherObject, "someSignal(int,int,int)",
arg1, arg2, arg3);
The first looks like your average piece of code and can easily be
overlooked, the second looks like messing around unless you know what
you're doing. When reviewing or debugging, I'd rather have the latter
piece of code, simply because it's suspiciously suspicious (and maybe even
wrong, didn't check that).
You know that we have a way of marking every line where we are emitting a
signal, right? We created a macro for this, it's called "emit".

If you need to muck around with the internals, you must have a good reason.
Then you might as well have a little more performance.

Not to mention that you made a serious mistake above by placing
"(int,int,int)": in invokeMethod, you do not list the signature. Imagine if
this were production code! It wouldn't work and it might go undetected for
years.
Post by Frans Klaver
As far as I understand, the string based versions for run-time defined
signals and slots are still required. Sticking to the old syntax will keep
a common syntax for both use cases. If a connection is vitally important
to the workings of the system I'd just use Q_ASSERT(connect(...)).
Detection guaranteed.
Also a VERY BAD idea. Q_ASSERT expands to empty in release builds. So if you
test in debug mode and everything works, when you compile in release mode
things will suddenly stop working!

So, tell me, after making two very big mistakes in one email due to the
current syntax, wouldn't you rather your compiler told you that you had made
mistakes?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Frans Klaver
2011-09-22 06:18:49 UTC
Permalink
Post by Thiago Macieira
What is worse for you, that someone unintentionally emits a signal, or that
you fail to connect because someone made a typo, or accidentally left the
variable names in the SIGNAL or SLOT definition. Not to mention in refactoring
this problem might creep in, because the code compiles but fails at runtime.
Don't forget you have to match *exactly* the signature, of the exact same
types, in the exact same typedef and namespace description.
I've been through the latter multiple times. I didn't care so far.
Post by Thiago Macieira
You may say you're a very good developer and you check all connections
(by testing the return value or by running with QT_FATAL_WARNINGS=1).
Are you sure you tested all of the connection possibilities in your code?
There is no chance that there was a connect() call that went untested?
How about your co-workers, are they as good as you?
Ooh, I think I left a wrong impression on how I think of my own abilities
as a developer. I hardly ever check the return values of connect
statements (which is probably why I fell for the Q_ASSERT trap).
Post by Thiago Macieira
Post by Frans Klaver
of the two is more likely to produce weird results, and my gut says a
solution with publicly accessible signals is the one. Get some bad signal
I beg to differ. I think the lack of compile-time checking that the
connection can succeed is the one causing most issues.
Then we disagree.
Post by Thiago Macieira
You know that we have a way of marking every line where we are emitting a
signal, right? We created a macro for this, it's called "emit".
Yes. The emit keyword is used at developer discretion. It's not a
requirement for signal emission to be successful.
Post by Thiago Macieira
If you need to muck around with the internals, you must have a good reason.
Exactly. Shouldn't the code somehow show you're going off-track?
emit someObject->someSignal()
doesn't cut it for me.
Post by Thiago Macieira
Then you might as well have a little more performance.
This just feels like another incentive towards 'abuse'. You have the
performance just where you need performance. In the internals.
Post by Thiago Macieira
Not to mention that you made a serious mistake above by placing
"(int,int,int)": in invokeMethod, you do not list the signature. Imagine if
this were production code! It wouldn't work and it might go undetected
for years.
Yikes. Maybe that explains that when I use it, I have to test five times.
Also, I tend to notice that things don't work earlier than things that
work, but behave strange every now and then.
Post by Thiago Macieira
Also a VERY BAD idea. Q_ASSERT expands to empty in release builds. So if you
test in debug mode and everything works, when you compile in release mode
things will suddenly stop working!
Right, that was me being stupid. I should really stop dry coding stuff in
e-mails... Don't Q_ASSERT then, but assert (and I do not necessarily mean
assert()).
Post by Thiago Macieira
So, tell me, after making two very big mistakes in one email due to the
current syntax, wouldn't you rather your compiler told you that you had
made mistakes?
The latter of those wouldn't be caught by the compiler. The invokeMethod
example I expected to be wrong, didn't I?

I'm still not convinced that the trade-off is worth your while, but you
obviously think it is. There's downsides and upsides to both approaches.

Cheers,
Frans
Olivier Goffart
2011-09-22 11:47:01 UTC
Permalink
On Thursday 22 September 2011 08:18:49 Frans Klaver wrote:
[...]
Post by Frans Klaver
I'm still not convinced that the trade-off is worth your while, but you
obviously think it is. There's downsides and upsides to both approaches.
Compile-time check is one thing, but there is also other advantage of the new
code.

I am putting some advantages here:

- You get automatic conversions of the arguments (eg. QString->QVariant or
int->double, ...)
- Old syntax has issues with namespaces and typedefs.
- You get all the power of connecting using tr1::bind, or to a C++11 lambda
expression.

So yes, there are some tradeoffs, like having the signals public.

How often did you compile your code, and had the error "mySignal is protected"
which was because you called a signal on another object by mistake?
And How often did you actually have valid reason to emit the signal but had to
use a workaround with one level of indirection?
Frans Klaver
2011-09-22 12:54:12 UTC
Permalink
 - You get automatic conversions of the arguments (eg. QString->QVariant or
int->double, ...)
I didn't consider this one.
 - You get all the power of connecting using tr1::bind, or to a C++11 lambda
expression.
There are some valid use cases for this.
How often did you compile your code, and had the error "mySignal is protected"
which was because you called a signal on another object by mistake?
I've come across a few badly named ones leading me to believe they
were normal functions.
And How often did you actually have valid reason to emit the signal but had to
use a workaround with one level of indirection?
One or two times, which indicated to me that the code was factored
incorrectly. Working around it would be messy.

Ah well, to me it seems the choice is made. There's still something
about it that feels dirty, though.

Cheers,
Frans
Stefan Majewsky
2011-09-24 12:44:23 UTC
Permalink
Post by Thiago Macieira
Also a VERY BAD idea. Q_ASSERT expands to empty in release builds.
Why isn't there any variant of Q_ASSERT that expands like

#define Q_ASSERT_ALT(x) x;

in release builds? If it's a simple value check like Q_ASSERT_ALT(x >
0), the compiler will optimize the effect-less statement away.

Greetings
Stefan
Joerg Bornemann
2011-09-27 07:48:29 UTC
Permalink
Post by Stefan Majewsky
Why isn't there any variant of Q_ASSERT that expands like
#define Q_ASSERT_ALT(x) x;
in release builds? If it's a simple value check like Q_ASSERT_ALT(x >
0), the compiler will optimize the effect-less statement away.
The standard assert macro in assert.h behaves the same. It does nothing
as soon as you define NDEBUG.
Having a Q_ASSERT_ALT macro (whatever its gonna be named) is IMHO
confusing and not of much value.


BR,

Jörg
Thiago Macieira
2011-09-27 07:56:12 UTC
Permalink
Post by Joerg Bornemann
Post by Stefan Majewsky
Why isn't there any variant of Q_ASSERT that expands like
#define Q_ASSERT_ALT(x) x;
in release builds? If it's a simple value check like Q_ASSERT_ALT(x >
0), the compiler will optimize the effect-less statement away.
The standard assert macro in assert.h behaves the same. It does nothing
as soon as you define NDEBUG.
Having a Q_ASSERT_ALT macro (whatever its gonna be named) is IMHO
confusing and not of much value.
If you need something like that, it's very easy to write:

bool check = your code goes here;
Q_ASSERT(check); Q_UNUSED(check);
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Alexis Menard
2011-09-28 17:05:34 UTC
Permalink
Post by Thiago Macieira
Post by Joerg Bornemann
Post by Stefan Majewsky
Why isn't there any variant of Q_ASSERT that expands like
#define Q_ASSERT_ALT(x) x;
in release builds? If it's a simple value check like Q_ASSERT_ALT(x >
0), the compiler will optimize the effect-less statement away.
The standard assert macro in assert.h behaves the same. It does nothing
as soon as you define NDEBUG.
Having a Q_ASSERT_ALT macro (whatever its gonna be named) is IMHO
confusing and not of much value.
WebKit uses ASSERT_UNUSED(variable unused, assert check).
Post by Thiago Macieira
bool check = your code goes here;
Q_ASSERT(check); Q_UNUSED(check);
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
_______________________________________________
Qt5-feedback mailing list
http://lists.qt.nokia.com/mailman/listinfo/qt5-feedback
Frans Klaver
2011-09-28 21:00:13 UTC
Permalink
On Wed, 28 Sep 2011 19:05:34 +0200, Alexis Menard
Post by Alexis Menard
WebKit uses ASSERT_UNUSED(variable unused, assert check).
That works for checking a variable. It still does not cover the case

ASSERT(connect(...));

You would have to do
bool result = connect(...);
ASSERT_UNUSED(result, result);

I could probably even prefer:
if (!connect(...))
Q_ASSERT(false);

On the other hand, I've written plenty of code that goes
bool result = doSomething();
(void)result;
assert(result);

So there it goes. Out the window.

Cheers,
Frans

C***@csiro.au
2011-09-21 23:48:54 UTC
Permalink
Post by Thiago Macieira
Post by Stephen Kelly
Post by Stephen Kelly
Post by Thiago Macieira
Another solution (also by way of adding an indirection) is to add a
function that emits, which is protected.
template <typename Klass, typename... Args>
void QObject::emitSignal(void (Klass:: *signal)(Args...), Args... args);
emitSignal(&Me::textChanged, newText);
emit textChanged(newText);
The template magic to make that happen is already present.
However, it would be completely source-incompatible with the existing
code, which is a big no-no. It would require making the signal functions
themselves not do anything -- only serve as identifiers.
For source compatibility they could be implemented by moc to work as
currently, no?
Oh, they need to keep the current implementation so that SIGNAL and SLOT
based connections still work. Never mind me.
The old connection mechanism is almost guaranteed to work.
The problem is to update "emit" lines, which are calls to functions that cause
the signal to be emitted.
otherObject->emitSignal(&Them::textChanged, newText);
emit otherObject->textChanged(newText);
The big difference is that "emitSignal" would be protected in QObject, so any
class deriving from QObject would have access to it.
We've been looking at this for a year. If anyone has more interesting ideas,
they are more than welcome. But please try to write the proof of concept first.
Our conclusion is that if you want compile-time checking of the arguments, you
need to give up the protectedness of the emission.
I'll mention one approach not because I think it is the right one to use here, but rather just to potentially fuel further discussion. For reasons I won't go into, we have our own observer-based code which has similarities to what Qt's signal-slot mechanism offers. Instead of relying on string-based signatures, it uses addresses of instances of a specific event class, which we call EventID. We also have an event base class which we call ObservableEvent. Our equivalent of defining a signal is to define a subclass of ObservableEvent. We provide the EventID singleton that corresponds to an ObservableEvent subclass using a static function. A short code extract to give you an idea of how this works would be:

class SomeSignalEvent : public ObservableEvent
{
QString someString_;
public:
SomeSignalEvent(const QString& s) : someString_(s) {}

static const EventID& eventID() { ... } // Implementation hidden to keep it simple
};

class SendingClass : public CanNotifyEvents // I won't explain this, but it's similar to inheriting from QObject
{
// ...
};

class ReceivingClass
{
public:
void handleSomeSignalEvent(const SomeSignalEvent& e);
};

In our code, you would create an observer for an event on a specific object something like this:

addObserver(sender, SomeSignalEvent::eventID(), receiver & ReceivingClass::handleSomeSignalEvent);


Now, let's say that, as per this discussion thread, you wanted to limit who can emit/raise the SomeSignalEvent. Specifically, let's say you only wanted SendingClass to be able to do that. In that case, you could make the constructor for SomeSignalEvent private and add SendingClass as a friend. You leave the eventID() static function public. This means only SendingClass can create/emit the event, but anyone can connect to / listen for that signal/event.

Now, it may look like more work and yes, there are some things that the above approach makes a bit harder than the current signal-slot mechanism, but it does allow you to have full compile time checking and it does allow you to have non-public emit access while still allowing public connect/listen access. I've simplified things a bit in the examples above. In our code, we have macros and templates handle all the gory details and it is actually very simple to use. We've also very much appreciated that receivers don't have to derive from a common base class, so it's easy to add connections where we want the receiver to be a private class defined within a .cpp file, for instance.

I reiterate that I'm not advocating this as a replacement for the current signal-slot mechanism. It isn't source compatible with it, obviously, and it is a bit different in how the receiver is defined. I mention it only as an example of something that does meet the requirements of being able to control who can emit/raise a signal while still allowing anyone to connect to it, all with compile-time checking.

--
Dr Craig Scott
Computational Software Engineering Team Leader, CSIRO (CMIS)
Melbourne, Australia
C***@csiro.au
2011-09-22 01:33:36 UTC
Permalink
Sorry, slight typo in my original email. Creating an observer would be done like this:

addObserver(sender, SomeSignalEvent::eventID(), receiver, &ReceivingClass::handleSomeSignalEvent);

Also, the SomeSignalEvent class should have had an accessor function to allow the receiver to get the someString_ value. I'm sure you'd all figured that out on your own though. ;)
Post by C***@csiro.au
Post by Thiago Macieira
Post by Stephen Kelly
Post by Stephen Kelly
Post by Thiago Macieira
Another solution (also by way of adding an indirection) is to add a
function that emits, which is protected.
template <typename Klass, typename... Args>
void QObject::emitSignal(void (Klass:: *signal)(Args...), Args... args);
emitSignal(&Me::textChanged, newText);
emit textChanged(newText);
The template magic to make that happen is already present.
However, it would be completely source-incompatible with the existing
code, which is a big no-no. It would require making the signal functions
themselves not do anything -- only serve as identifiers.
For source compatibility they could be implemented by moc to work as
currently, no?
Oh, they need to keep the current implementation so that SIGNAL and SLOT
based connections still work. Never mind me.
The old connection mechanism is almost guaranteed to work.
The problem is to update "emit" lines, which are calls to functions that cause
the signal to be emitted.
otherObject->emitSignal(&Them::textChanged, newText);
emit otherObject->textChanged(newText);
The big difference is that "emitSignal" would be protected in QObject, so any
class deriving from QObject would have access to it.
We've been looking at this for a year. If anyone has more interesting ideas,
they are more than welcome. But please try to write the proof of concept first.
Our conclusion is that if you want compile-time checking of the arguments, you
need to give up the protectedness of the emission.
class SomeSignalEvent : public ObservableEvent
{
QString someString_;
SomeSignalEvent(const QString& s) : someString_(s) {}
static const EventID& eventID() { ... } // Implementation hidden to keep it simple
};
class SendingClass : public CanNotifyEvents // I won't explain this, but it's similar to inheriting from QObject
{
// ...
};
class ReceivingClass
{
void handleSomeSignalEvent(const SomeSignalEvent& e);
};
addObserver(sender, SomeSignalEvent::eventID(), receiver & ReceivingClass::handleSomeSignalEvent);
Now, let's say that, as per this discussion thread, you wanted to limit who can emit/raise the SomeSignalEvent. Specifically, let's say you only wanted SendingClass to be able to do that. In that case, you could make the constructor for SomeSignalEvent private and add SendingClass as a friend. You leave the eventID() static function public. This means only SendingClass can create/emit the event, but anyone can connect to / listen for that signal/event.
Now, it may look like more work and yes, there are some things that the above approach makes a bit harder than the current signal-slot mechanism, but it does allow you to have full compile time checking and it does allow you to have non-public emit access while still allowing public connect/listen access. I've simplified things a bit in the examples above. In our code, we have macros and templates handle all the gory details and it is actually very simple to use. We've also very much appreciated that receivers don't have to derive from a common base class, so it's easy to add connections where we want the receiver to be a private class defined within a .cpp file, for instance.
I reiterate that I'm not advocating this as a replacement for the current signal-slot mechanism. It isn't source compatible with it, obviously, and it is a bit different in how the receiver is defined. I mention it only as an example of something that does meet the requirements of being able to control who can emit/raise a signal while still allowing anyone to connect to it, all with compile-time checking.
--
Dr Craig Scott
Computational Software Engineering Team Leader, CSIRO (CMIS)
Melbourne, Australia
_______________________________________________
Qt5-feedback mailing list
http://lists.qt.nokia.com/mailman/listinfo/qt5-feedback
--
Dr Craig Scott
Computational Software Engineering Team Leader, CSIRO (CMIS)
Melbourne, Australia
Olivier Goffart
2011-09-22 12:06:05 UTC
Permalink
Post by Thiago Macieira
We've been looking at this for a year. If anyone has more interesting ideas,
they are more than welcome. But please try to write the proof of concept first.
I almost managed to get the signal private:


class Foo {
public:
class Signal { //This would be in the Q_OBJECT macro.
~Signal() {}; // private destructor
friend class Foo; //<- problem: Q_OBJECT macto does not know "Foo"
};

Signal bar(); //New way of declaring signals, moc detect the return type
};

int main()
{
Foo::Signal (Foo::*sig)() = &Foo::bar; // works
Foo f; f.bar(); // error, ~Signal is private
return 0;
}


But this mean signals are now private instead of protected (which i think is
maybe a good thing) and that Q_OBJECT should be changed to know which class
it it in so it can put the friend.
But the error is not really friendly, and signal cannot return real value
anymore (which work currently despite not being documented).
Post by Thiago Macieira
Our conclusion is that if you want compile-time checking of the arguments,
you need to give up the protectedness of the emission.
Yes, I think public signals are not a huge issue.
Frans Klaver
2011-09-22 13:07:28 UTC
Permalink
Post by Olivier Goffart
class Foo {
   class Signal {      //This would be in the Q_OBJECT macro.
       ~Signal() {}; // private destructor
       friend class Foo; //<- problem: Q_OBJECT macto does not know "Foo"
   };
   Signal bar(); //New way of declaring signals, moc detect the return type
};
int main()
{
   Foo::Signal (Foo::*sig)() =  &Foo::bar; // works
   Foo f;  f.bar(); // error, ~Signal is private
   return 0;
}
But this mean signals are now private instead of protected (which i think is
maybe a good thing)  and that Q_OBJECT should be changed to know which class
it it in so it can put the friend.
Looks interesting.
- How would one go about using arguments to the signal? Templates?
- What would we be doing with for example the dataChanged(...) signal
from QAbstractItemModel? A protected emitDataChanged(...) function?
Post by Olivier Goffart
But the error is not really friendly,
That would be:
error: ‘Foo::Signal::~Signal()’ is private

I agree that, although pretty explicit, it is rather obscure in this context.
Post by Olivier Goffart
and signal cannot return real value
anymore (which work currently despite not being documented).
Would this be a big problem?
Thiago Macieira
2011-09-22 15:33:20 UTC
Permalink
Post by Olivier Goffart
class Foo {
class Signal { //This would be in the Q_OBJECT macro.
~Signal() {}; // private destructor
Q_DISABLE_COPY(Signal)
Post by Olivier Goffart
friend class Foo; //<- problem: Q_OBJECT macto does not know "Foo"
};
Signal bar(); //New way of declaring signals, moc detect the return type
};
int main()
{
Foo::Signal (Foo::*sig)() = &Foo::bar; // works
Foo f; f.bar(); // error, ~Signal is private
return 0;
}
But this mean signals are now private instead of protected (which i think is
maybe a good thing) and that Q_OBJECT should be changed to know which
class it it in so it can put the friend.
That requires changing every single header defining a QObject-derived class. It
might be automatable, but I'm not sure it will catch every case.

Making signals private is IMHO much worse than making them public. Take for
example signal QIODevice::readyRead(): it's emitted from almost all classes
deriving from QIODevice (notable exception is QFile). In order to fix that
case, we'd need to add a signal-emitting function. But in any case, this is
way source-incompatible and I doubt it is automatable at all.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
BRM
2011-09-22 16:55:24 UTC
Permalink
----- Original Message -----
Sent: Thursday, September 22, 2011 11:33 AM
Subject: Re: [Qt5-feedback] QObject: new signals-slots syntax
Post by Olivier Goffart
class Foo {
    class Signal {     //This would be in the Q_OBJECT macro.
        ~Signal() {}; // private destructor
Q_DISABLE_COPY(Signal)
Post by Olivier Goffart
        friend class Foo; //<- problem: Q_OBJECT macto does not know
"Foo"
Post by Olivier Goffart
    };
    Signal bar(); //New way of declaring signals, moc detect the return
type
Post by Olivier Goffart
};
int main()
{
    Foo::Signal (Foo::*sig)() =  &Foo::bar; // works
    Foo f;  f.bar(); // error, ~Signal is private
    return 0;
}
But this mean signals are now private instead of protected (which i think
is
Post by Olivier Goffart
maybe a good thing)  and that Q_OBJECT should be changed to know which
class it it in so it can put the friend.
That requires changing every single header defining a QObject-derived class. It
might be automatable, but I'm not sure it will catch every case.
Making signals private is IMHO much worse than making them public. Take for
example signal QIODevice::readyRead(): it's emitted from almost all classes
deriving from QIODevice (notable exception is QFile). In order to fix that
case, we'd need to add a signal-emitting function. But in any case, this is
way source-incompatible and I doubt it is automatable at all.
This is why I'd like to see SIGNALS be supported at all three levels - public, protected, and private.
That would allow you to have some signals - like QIODevice::readyRead() that are fully public - and other signals - like an internal read that buffers the data - that are protected; and you could have other signals that are private for whatever you needed internally. Granted, of that, the private is the least likely to be used, but protected signals could be very beneficial.

I think when I brought it up last, it was pointed out that class functions could do that without signals, but that would leave you without being able to call other objects you are utilizing via signal/slots.

This would also be source compatible since it would not be required that you have protected signals. The easy way to do it would be to allow Q_SIGNALS to continue in the same public signals behavior and introduce a new one (Q_PROTECTED_SIGNALS) to add the new behavior. Ideally it would be good to use the Q_SIGNALS macro like one uses the Q_SLOTS macro, but that would be source incompatible - this could at least provide that functionality until the next round where binary or source compatibility is not required.

$0.02

Ben
l***@nokia.com
2011-09-23 20:13:34 UTC
Permalink
Post by Thiago Macieira
Post by Olivier Goffart
class Foo {
class Signal { //This would be in the Q_OBJECT macro.
~Signal() {}; // private destructor
Q_DISABLE_COPY(Signal)
Post by Olivier Goffart
friend class Foo; //<- problem: Q_OBJECT macto does not know "Foo"
};
Signal bar(); //New way of declaring signals, moc detect the return type
};
int main()
{
Foo::Signal (Foo::*sig)() = &Foo::bar; // works
Foo f; f.bar(); // error, ~Signal is private
return 0;
}
But this mean signals are now private instead of protected (which i think is
maybe a good thing) and that Q_OBJECT should be changed to know which
class it it in so it can put the friend.
That requires changing every single header defining a QObject-derived class. It
might be automatable, but I'm not sure it will catch every case.
Making signals private is IMHO much worse than making them public. Take for
example signal QIODevice::readyRead(): it's emitted from almost all classes
deriving from QIODevice (notable exception is QFile). In order to fix that
case, we'd need to add a signal-emitting function. But in any case, this is
way source-incompatible and I doubt it is automatable at all.
Fully agree with Thiago. Making signals private would break an awful lot
of existing code and is in many cases very inconvenient.

I would also prefer if we could continue keeping signals protected, but as
Olivier pointed out there are lots of advantages with the new connect
syntax.

Cheers,
Lars
Peter Kuemmel
2011-09-24 12:01:35 UTC
Permalink
Post by l***@nokia.com
Fully agree with Thiago. Making signals private would break an awful lot
of existing code and is in many cases very inconvenient.
I would also prefer if we could continue keeping signals protected, but as
Olivier pointed out there are lots of advantages with the new connect
syntax.
I would add the statically checked connect optionally to the old
connect with protected signals. Then we would need moc generated
headers, but nobody is forced to use them. This would be 100%
backward compatible.

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Thiago Macieira
2011-09-24 14:59:54 UTC
Permalink
Post by Peter Kuemmel
Post by l***@nokia.com
Fully agree with Thiago. Making signals private would break an awful lot
of existing code and is in many cases very inconvenient.
I would also prefer if we could continue keeping signals protected, but as
Olivier pointed out there are lots of advantages with the new connect
syntax.
I would add the statically checked connect optionally to the old
connect with protected signals. Then we would need moc generated
headers, but nobody is forced to use them. This would be 100%
backward compatible.
Olivier did the original work.

I like his solution and apparently so does Lars. If you think there's a better
solution, we need to see how it would work. A patch implementing it would be
preferable, but a proof of concept would be enough.

Several people have made suggestions, but all of them have one fatal problem
or another. I don't want to dismiss ideas, but I also don't spend time
pointing out problems that would be found in the first attempt.

Please be sure to check that:
1) it works on at least two compilers

2) you can implement a few parameters or use everything with variadic
templates

3) type promotion happens correctly (QString -> QVariant, int -> long, etc.)

4) pointer promotion happens correctly (QWidget* in a signal connects to a
QObject* in a slot, but not vice-versa)

5) const- and ref-correctness (can't bind a non-const ref to const rvalue,
can't bind lvalue ref to rvalue ref, etc.)

6) provide a good rvalue-ref solution or at least make sure it doesn't compile
until we find one

7) source compatibility with the current solution is kept:
in the .cpp: emit signalName(params...);
in the .h: Q_OBJECT
it's ok to add new things in order to enable the new syntax, but it would
preferable if nothing changed to enable it (aside from C++11). Definitely not
boilerplate code.

8) addition of new signals is binary compatible
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Peter Kuemmel
2011-09-24 16:02:44 UTC
Permalink
-------- Original-Nachricht --------
Datum: Sat, 24 Sep 2011 16:59:54 +0200
Post by Peter Kuemmel
I would add the statically checked connect optionally to the old
connect with protected signals. Then we would need moc generated
headers, but nobody is forced to use them. This would be 100%
backward compatible.
Olivier did the original work.
With public signals.

...
Definitely no boilerplate code.
I only see a chance with generated headers, or does 'boilerplate code'
mean only hand written code?

Peter
8) addition of new signals is binary compatible
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Thiago Macieira
2011-09-24 17:57:37 UTC
Permalink
Post by Peter Kuemmel
Definitely no boilerplate code.
I only see a chance with generated headers, or does 'boilerplate code'
mean only hand written code?
Boilerplate means stuff you have to write to get things up and running.

As an example of what I don't like, check out GObject.

And as an example of how I'd like it to be, check out my blog on a string
table with no relocations in C++11.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Peter Kuemmel
2011-09-21 14:06:13 UTC
Permalink
Post by Thiago Macieira
Post by Peter Kuemmel
I still see a problem in making signals public, this
completely breaks encapsulation, and makes it possible
for everyone to emit any signal.
There's no way around it. Since we're using the function pointer (actually,
One way around it would be "on-the-fly inheriting":

https://qt.gitorious.org/~syntheticpp/qt/qtbase-staging-improvements/commit/c1cf86f08fca7efbbf03106756d4c5bbd991664c

Maybe this commit could be the starting point for a better
'QObject::connect' replacement.

Peter
Post by Thiago Macieira
the PMF) to identify the signal, you must have C++ access to the signal
function. That means you must be able to call it.
Other template-based signal libraries have the same problem. It cannot be
avoided unless you have a way to identify the signal which doesn't include
access to the actual signal implementation.
One solution to that would be to have an enum of signals, instead of
identifying by an object or by a PMF. However, handling enums is not very
easy, since extending the range becomes cumbersome -- how do you add a new
signal to a class that has been derived from?
As the old adage says, "all problems in C++ can be solved with another level
of indirection". So we can create an indirection layer to the enums (the
numeric IDs) and map at runtime. The easiest way of doing that is to have a
string-based approach. However, C++ has no reflection mechanism, so either
you're forced to write the string-matching code yourself, or you have to use a
code generator.
And then we're right back where we started: moc and the current syntax.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
Jetzt informieren: http://www.gmx.net/de/go/freephone
Thiago Macieira
2011-09-21 14:35:10 UTC
Permalink
Post by Peter Kuemmel
https://qt.gitorious.org/~syntheticpp/qt/qtbase-staging-improvements/commit/
c1cf86f08fca7efbbf03106756d4c5bbd991664c
Maybe this commit could be the starting point for a better
'QObject::connect' replacement.
If you can do on-the-fly inheritance to access a protected in order to connect,
you can also do it to emit the signal. And you can do that today in Qt 4 (like
Q3DnsUgleHack
<https://qt.gitorious.org/qt/qt/blobs/master/src/qt3support/network/q3dns.cpp#line806>)

If I get you correctly, you want the syntax to be:

connect<SenderClass>::signalName(senderObject, receiverObject,
&ReceiverClass::slotFunction);

However, there's a fatal flaw in that design: you need the connect<SenderClass>
class to be entirely defined in the header. Your code has (line 4494ff):
4494 /*
4495 generated by moc
4496 */
4497 template<>
4498 struct connect<ProtectedSignal>

You need to move this struct to the header, which means either boilerplate
code written by the developer or an include of a moc-generated file in a public
header. Both options would suck IMHO.

Another option is to use macros:

class MyClass
{
Q_OBJECT
Q_SIGNAL(signalName, (const QString &textChanged, int changeCount))
};

With something like:
#define Q_SIGNAL(name, args) \
protected: name args; \
public: name ## _connect args; \
private:

And make moc generate these two functions. We'd have the connection as:

QObject::connect(sender, &MyClass::signalName_connect, receiver, [](){});

and the emission:
emit signalName(newText, ++count);

I don't like it either.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Peter Kuemmel
2011-09-21 15:57:26 UTC
Permalink
...
Post by Thiago Macieira
I don't like it either.
I know, both solutions aren't very elegant, but I hope
someone gets inspired and finds a better solution.

And I still prefer to have things checked by the compiler
and not to trust developers ("I promise I never emitted
aboutToQuit() by accident").

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Andre Somers
2011-09-21 16:47:38 UTC
Permalink
Post by Peter Kuemmel
...
Post by Thiago Macieira
I don't like it either.
I know, both solutions aren't very elegant, but I hope
someone gets inspired and finds a better solution.
And I still prefer to have things checked by the compiler
and not to trust developers ("I promise I never emitted
aboutToQuit() by accident").
Peter
Stupid question, but perhaps it can trigger an idea in somebody more
into the material and smarter than me?

Is it really nessecairy that _everyone_ has public access to the signal?
What if only QObject had access to it? Would that work? If so, it could
work to just make QObject a friend class of every class that has the
Q_OBJECT macro defined, right? Could that work?

André
Thiago Macieira
2011-09-21 17:09:11 UTC
Permalink
Post by Andre Somers
Is it really nessecairy that _everyone_ has public access to the signal?
What if only QObject had access to it? Would that work? If so, it could
work to just make QObject a friend class of every class that has the
Q_OBJECT macro defined, right? Could that work?
I don't know what you mean here. The access rights are only these: public,
protected, private.

If I need to write &MyClass::signalName in my code, then I need signalName to
be public or that my code is a friend of MyClass. Listing all possible classes
that may want to connect to the signals is an impossible task.

So public is the only option.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Andre Somers
2011-09-21 17:51:26 UTC
Permalink
Post by Thiago Macieira
Post by Andre Somers
Is it really nessecairy that _everyone_ has public access to the signal?
What if only QObject had access to it? Would that work? If so, it could
work to just make QObject a friend class of every class that has the
Q_OBJECT macro defined, right? Could that work?
I don't know what you mean here. The access rights are only these: public,
protected, private.
If I need to write&MyClass::signalName in my code, then I need signalName to
be public or that my code is a friend of MyClass. Listing all possible classes
that may want to connect to the signals is an impossible task.
So public is the only option.
Ah... Bug in my thinking. You need to have the access at the place where
you make the connect, not so much in the connect function itself. Never
mind, carry on, nothing to see here...

André
Wolf-Michael Bolle
2011-09-22 06:31:39 UTC
Permalink
Post by Thiago Macieira
Post by Andre Somers
Is it really nessecairy that _everyone_ has public access to the signal?
What if only QObject had access to it? Would that work? If so, it could
work to just make QObject a friend class of every class that has the
Q_OBJECT macro defined, right? Could that work?
I don't know what you mean here. The access rights are only these: public,
protected, private.
If I need to write &MyClass::signalName in my code, then I need signalName
to be public or that my code is a friend of MyClass. Listing all possible
classes that may want to connect to the signals is an impossible task.
So public is the only option.
Why doesn't a class simply provide public connector method templates for the
not so public signals?

// Very simplified example
class Xyz
{
private:
void eventHasHappened(); // A signal
public:
template<...>
void connectToEventHasHappened(...)
{
connect(...);
}
};

Xyz xyz;
xyz.connectToEventHasHappened(...);

I admit that figuring out the template syntax is not something for everybody.
Frans Klaver
2011-09-22 07:25:06 UTC
Permalink
On Thu, Sep 22, 2011 at 8:31 AM, Wolf-Michael Bolle
Post by Wolf-Michael Bolle
Why doesn't a class simply provide public connector method templates for the
not so public signals?
// Very simplified example
class Xyz
{
   void eventHasHappened();   // A signal
   template<...>
   void connectToEventHasHappened(...)
   {
       connect(...);
   }
};
Xyz xyz;
xyz.connectToEventHasHappened(...);
That would mean having to declare a connectTo... definition for every
declared signal, right?
Wolf-Michael Bolle
2011-09-22 07:35:44 UTC
Permalink
Post by Frans Klaver
On Thu, Sep 22, 2011 at 8:31 AM, Wolf-Michael Bolle
Post by Wolf-Michael Bolle
Why doesn't a class simply provide public connector method templates for
the not so public signals?
// Very simplified example
class Xyz
{
void eventHasHappened(); // A signal
template<...>
void connectToEventHasHappened(...)
{
connect(...);
}
};
Xyz xyz;
xyz.connectToEventHasHappened(...);
That would mean having to declare a connectTo... definition for every
declared signal, right?
For every non-public signal, right.

That is not very nice. I understand. Maybe though with the help of some
creative macros, this can be made less painful.
Peter Kuemmel
2011-09-24 11:33:43 UTC
Permalink
Post by Thiago Macieira
4494 /*
4495 generated by moc
4496 */
4497 template<>
4498 struct connect<ProtectedSignal>
You need to move this struct to the header, which means either boilerplate
code written by the developer or an include of a moc-generated file in a
public header. Both options would suck IMHO.
Thinking about your "this sucks" I came to a other conclusion:

With Qt5 it is possible to introduce new features which make
things possible which are impossible with Qt4.
And such a new feature would be moc-generated headers.

I know, most C++-purists don't like it, and at the moment there
is the idea to make Qt5 more C++/std-only. But Qt will always
need the moc preprocessor, and this is because of the lack of
some features in C++.

C++ is a multi-paradigm programming language, and moc only adds
another paradigm. So why not introducing more code-generation by
supporting moc-generated headers? I'm sure this will simplify
things in future, and makes new things possible, because with the C-preprocessor and templates not all ideas could be implemented.

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Thiago Macieira
2011-09-24 14:39:05 UTC
Permalink
Post by Peter Kuemmel
With Qt5 it is possible to introduce new features which make
things possible which are impossible with Qt4.
And such a new feature would be moc-generated headers.
Yes, we could do that. I just don't like it.
Post by Peter Kuemmel
I know, most C++-purists don't like it, and at the moment there
is the idea to make Qt5 more C++/std-only. But Qt will always
need the moc preprocessor, and this is because of the lack of
some features in C++.
C++ is a multi-paradigm programming language, and moc only adds
another paradigm. So why not introducing more code-generation by
supporting moc-generated headers? I'm sure this will simplify
things in future, and makes new things possible, because with the
C-preprocessor and templates not all ideas could be implemented.
Uic does exactly that: it produces code you're supposed to #include and use in
your own code. Some other code generators do the same thing.

As for moc doing the same... why would we need it? What is the need? I pointed
out the flaw in your design, but I still don't like the new syntax. We're still
trying to keep source compatibility...

What's more, if this is an *installed* header, we need to make sure that the
output from moc stays binary compatible on all compilers until Qt 6. We've
done changes to moc several times during the 4.x lifetime.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Peter Kuemmel
2011-09-24 16:11:47 UTC
Permalink
Post by Thiago Macieira
Post by Peter Kuemmel
With Qt5 it is possible to introduce new features which make
things possible which are impossible with Qt4.
And such a new feature would be moc-generated headers.
Yes, we could do that. I just don't like it.
Template based code always looks different, but
connect(ptr, SIGNAL(...), receiver, SLOT(...)))
also looks alien to C++ purists.
Post by Thiago Macieira
Post by Peter Kuemmel
I know, most C++-purists don't like it, and at the moment there
is the idea to make Qt5 more C++/std-only. But Qt will always
need the moc preprocessor, and this is because of the lack of
some features in C++.
C++ is a multi-paradigm programming language, and moc only adds
another paradigm. So why not introducing more code-generation by
supporting moc-generated headers? I'm sure this will simplify
things in future, and makes new things possible, because with the
C-preprocessor and templates not all ideas could be implemented.
Uic does exactly that: it produces code you're supposed to #include and use in
your own code. Some other code generators do the same thing.
As for moc doing the same... why would we need it? What is the need? I pointed
out the flaw in your design, but I still don't like the new syntax. We're still
trying to keep source compatibility...
Old QObject::connect code for source compatibility,
new code for typed connects, why should this be a problem?
Post by Thiago Macieira
What's more, if this is an *installed* header, we need to make sure that the
output from moc stays binary compatible on all compilers until Qt 6. We've
done changes to moc several times during the 4.x lifetime.
Then the generated header should only changed binary compatible.
I don't see a problem there.


When there is any chance for generated headers I try complete by demo code.

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
l***@nokia.com
2011-09-24 18:36:49 UTC
Permalink
Post by Peter Kuemmel
Post by Thiago Macieira
What's more, if this is an *installed* header, we need to make sure that the
output from moc stays binary compatible on all compilers until Qt 6. We've
done changes to moc several times during the 4.x lifetime.
Then the generated header should only changed binary compatible.
I don't see a problem there.
When there is any chance for generated headers I try complete by demo code.
I do not like any solution involving generated headers at all.

The reason is simple: Generated headers are simply not something I can see
us adding to library code. The current headers are nice and relatively
clean.

If we add generated headers, we make deployment of libraries to 3rd
parties a lot harder. In addition, there's a chance you might get very
strange things showing up in your code completion in Creator or any other
IDE.

I believe that both the old syntax (runtime connect's) and the new one
where connect's are done at compile time have it's valid uses. So I do
like adding compile time connect's to Qt.

To be able to add this, we'll need to do some compromises. public signals
is IMO far less of a compromise than generated headers.

Cheers,
Lars
Peter Kuemmel
2011-09-25 03:27:38 UTC
Permalink
Post by l***@nokia.com
I do not like any solution involving generated headers at all.
The reason is simple: Generated headers are simply not something I can see
us adding to library code. The current headers are nice and relatively
clean.
If we add generated headers, we make deployment of libraries to 3rd
parties a lot harder. In addition, there's a chance you might get very
strange things showing up in your code completion in Creator or any other
IDE.
In principle no problems that can't be solved.
Post by l***@nokia.com
I believe that both the old syntax (runtime connect's) and the new one
where connect's are done at compile time have it's valid uses. So I do
like adding compile time connect's to Qt.
Compile time connects are definitely an improvement!
Post by l***@nokia.com
To be able to add this, we'll need to do some compromises. public signals
is IMO far less of a compromise than generated headers.
OK, maybe too much trouble for protected signals.

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Peter Kuemmel
2011-09-24 18:23:35 UTC
Permalink
Post by Thiago Macieira
Post by Peter Kuemmel
Definitely no boilerplate code.
I only see a chance with generated headers, or does 'boilerplate code'
mean only hand written code?
Boilerplate means stuff you have to write to get things up and running.
OK, despite all the other points you mentioned I attach here a file which
demonstrates the idea I'm talking about. In principle it only "frees"
the signal and forwards the call to QObject::connect, which is already
implemented by Oliver, so I assume it supports all features of Oliver's
code. No boilerplate code to write, only the 'new and ugly' connect
call.

Peter
Post by Thiago Macieira
As an example of what I don't like, check out GObject.
And as an example of how I'd like it to be, check out my blog on a string
table with no relocations in C++11.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurÃŒck-Garantie!
Jetzt informieren: http://www.gmx.net/de/go/freephone
Thiago Macieira
2011-09-24 18:38:38 UTC
Permalink
Post by Peter Kuemmel
Post by Thiago Macieira
Post by Peter Kuemmel
Definitely no boilerplate code.
I only see a chance with generated headers, or does 'boilerplate code'
mean only hand written code?
Boilerplate means stuff you have to write to get things up and running.
OK, despite all the other points you mentioned I attach here a file which
demonstrates the idea I'm talking about. In principle it only "frees"
the signal and forwards the call to QObject::connect, which is already
implemented by Oliver, so I assume it supports all features of Oliver's
code. No boilerplate code to write, only the 'new and ugly' connect
call.
The same technique can be used to bypass the signal protection and emit the
signal.

So I'm skeptical about the advantage and still afraid of the drawbacks.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Peter Kuemmel
2011-09-25 03:28:43 UTC
Permalink
-------- Original-Nachricht --------
Datum: Sat, 24 Sep 2011 20:38:38 +0200
Betreff: Re: [Qt5-feedback] QObject: new signals-slots syntax
Post by Peter Kuemmel
Post by Thiago Macieira
Post by Peter Kuemmel
Definitely no boilerplate code.
I only see a chance with generated headers, or does 'boilerplate
code'
Post by Peter Kuemmel
Post by Thiago Macieira
Post by Peter Kuemmel
mean only hand written code?
Boilerplate means stuff you have to write to get things up and
running.
Post by Peter Kuemmel
OK, despite all the other points you mentioned I attach here a file
which
Post by Peter Kuemmel
demonstrates the idea I'm talking about. In principle it only "frees"
the signal and forwards the call to QObject::connect, which is already
implemented by Oliver, so I assume it supports all features of Oliver's
code. No boilerplate code to write, only the 'new and ugly' connect
call.
The same technique can be used to bypass the signal protection and emit the
signal.
So I'm skeptical about the advantage and still afraid of the drawbacks.
OK, thread closed.

Peter
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
Loading...