PDA

View Full Version : External Acces to Private Slot



stefanadelbert
16th July 2010, 08:08
Should class A really be able to connect to a private slot in class B?

Here's some pseudo code to illustrate what I mean:

class A : public QObject
{
A() {
instanceOfB = new B();
connect(this, SIGNAL(mySignal()), instanceOfB, SLOT(MySlot()));
}

signals:
void mySignal();

private:
B* instanceOfB;
}

class B : public QObject
{
private slots:
void MySlot();
}

This effectively means that a class B private function can be accessed by class A, which is not really in the spirit of private function access specifier.

squidge
16th July 2010, 09:07
You can't call the slot directly, but you can call it via the slot mechanism as those are resolved during run time.

borisbn
16th July 2010, 11:45
slots are not a usual functions. they are a part of metaObject. when you emitting signal slot is not called directly like this

obj->slotName( params );
emitting signal say to object to call it's function like this:


obj->invokeMethod( "slotName" // you see - in quotes
, params );

franz
17th July 2010, 14:02
Maybe as an extra idea:


connect(A, SIGNAL(foo()), B, SLOT(bar()));
If object A emits signal foo(), then object B should execute function bar().

This system is an implementation of a language level observer pattern (http://en.wikipedia.org/wiki/Observer_pattern), in which object B reacts to something that object A does, rather than object A triggering something in object B.

ahmdsd_ostora
18th July 2010, 14:19
Signal-Slot pattern aim is that the sender DOESN'T KNOW which slots will be called via its signal.
so actually the sender doesn't call the slot, he just emits the signal and the environment deals with it.

stefanadelbert
19th July 2010, 01:24
You all make valid points. I'm definitely not criticizing Qt's SIGNAL / SLOT mechanism.

My observation is merely that class A would be able to execute a private slot on class B. There is no way that class B can prevent its private slot (which is actually just a private function that is registered as part of the associated metaObject) from being run by any other SIGNAL / SLOT aware class.

Say for instance that I want to create a private slot on class B that should only be run by class B:



class B : public QObject
{
private slots:
void MyVeryPrivatePersonalSlot(); // I, class B, should be the only class able to run the code in this function
}


But the private slot can be "called" (or triggered) by any class that can emit signals. So really the slot is not private at all.



class A : public QObject
{
A() {
instanceOfB = new B();
connect(this, SIGNAL(mySignal()), instanceOfB, SLOT(MyVeryPrivatePersonalSlot()));
}

signals:
void mySignal();

private:
B* instanceOfB;
}


Is there any way to prevent this behaviour, i.e. keep private slots truly private?


Signal-Slot pattern aim is that the sender DOESN'T KNOW which slots will be called via its signal.
so actually the sender doesn't call the slot, he just emits the signal and the environment deals with it.

I agree with you and this is all well and good when slot bar() is public. But when slot bar() is private, class B has no control over who can trigger the execution of the private slot's code.


Signal-Slot pattern aim is that the sender DOESN'T KNOW which slots will be called via its signal.
so actually the sender doesn't call the slot, he just emits the signal and the environment deals with it.

But in the case when class A, the sender (signaller), connect's its signal to class B's private slot (as in my example), then it does know exactly which function should be executed and it can choose to have code in private functions executed. In C++ this can be achieved using friends, but still class B would decide who its friends are so it still have the control.

I understand that the code isn't being called directly - it's being done through the metaObject framework, but the end effect is that a private function is exposed.


slots are not a usual functions. they are a part of metaObject. when you emitting signal slot is not called directly like this

obj->slotName( params );
emitting signal say to object to call it's function like this:


obj->invokeMethod( "slotName" // you see - in quotes
, params );


I agree and it's useful framework and I do understand (more or less) how the framework actually works. My concern is that private slots are available to anyone and everyone, and this breaks part of the C++ class member access pattern.


You can't call the slot directly, but you can call it via the slot mechanism as those are resolved during run time.

I disagree, but I'm very happy to be proved wrong.

A slot is nothing other than a function on a class and can be called the same way that any other function on a class would be called. But it is also considered when building the metaObject and is made available through the signal / slot mechanism. When the slot is called as a normal function the access specifier (public, protected, private) is respected. When the slot is triggered by a signal (whether the class' own signal or a signal from another class), the access specifier is not respected, resulting in private functions (slots) being runnable (triggerable) from outside the class.

The net effect is that private slots can be run by anyone.

squidge
19th July 2010, 08:34
Maybe you were confused by my response, as it basically states exactly the same as what you just replied with. Eg. that obj->func() will not work from outside the class but obj->InvokeMethod will.

To have your slots truly private, create them in another private class, and contain a pointer to this class in the private part of your public class. So if you need to access your private slots you can do d->slot() whilst people outside your class can not, as 'd' would be private.

This also means your class is more flexible - you can either have data for each instance of your class, or you can share data between instances of your class (like the Qt object classes).

ahmdsd_ostora
19th July 2010, 09:46
I think that Qt should prevent connecting to private slots outside their classes.

SixDegrees
19th July 2010, 09:53
Slots, by their nature, are public, period. Private slots make no sense; private members are not accessible outside the class they belong to, so signals and slots make no sense, since a direct function call will do just as well in such instances. Slots are inherently public; they are meant explicitly for inter-class communication.

The whole pedantic discussion above would make more sense if protected slots were the topic. For private slots, it's just silly. And the protected case is actually more interesting; here, in order to preserve the traditional C++ meaning of "protected", the metadata system would have to keep track of object inheritance to ensure that only related classes could signal one another - which, again, is somewhat silly since the whole point is inter-object communication, but here it's somewhat less silly than it is when the topic is private slots. In the end, however, one would have to reproduce a signficant chunk of the C++ compiler in order to pull this sort of thing off.

And for what purpose? The goto statement is also considered "bad", but lots of people still use it, and even modern languages like Java support it despite being able to do perfectly well without it through the use of other, "better" constructs. Varying degrees of access protection are useful, but insisting on strict adherence to this paradigm in a signalling system is pointless. Anyone concerned about this is, presumably, a programmer. By implication, that means you should be aware of the potential pitfalls, and should simply avoid them.

SixDegrees
19th July 2010, 09:55
Why? What practical problem are you trying to solve through such a constraint? And why are you using slots within a class to begin with? There's no point in using a signalling mechanism inside a single class; you've already got access to plain, old-fashioned function calls.

Slots are inherently public, by their very nature; see above.

squidge
19th July 2010, 10:43
I think that Qt should prevent connecting to private slots outside their classes.Considering how the connect() statement works, this would be impossible to do at compile time, and we know that a LOT of people simply ignore the standard output, so wouldn't see the error message about connecting to a private slot at runtime. The end result would just be confusion: "Why isn't my slot being called?". We have enough of this questions on this board already, we don't need more by making the system even more restrictive. If you really want private slots there is a way of doing it by using a private class (see above), else the documentation for your class structure would clearly indicate that the slot you are trying to use is private, and surely you don't blindly bind signals to slot without checking the docs first?

stefanadelbert
20th July 2010, 00:33
Maybe you were confused by my response, as it basically states exactly the same as what you just replied with. Eg. that obj->func() will not work from outside the class but obj->InvokeMethod will.

To have your slots truly private, create them in another private class, and contain a pointer to this class in the private part of your public class. So if you need to access your private slots you can do d->slot() whilst people outside your class can not, as 'd' would be private.

This also means your class is more flexible - you can either have data for each instance of your class, or you can share data between instances of your class (like the Qt object classes).

Nice suggestion. Thanks.


I think that Qt should prevent connecting to private slots outside their classes.

This is basically what I'm saying. I don't believe that it should be possible for a private slot to be accessible outside that defining class.


Slots, by their nature, are public, period. Private slots make no sense; private members are not accessible outside the class they belong to, so signals and slots make no sense, since a direct function call will do just as well in such instances. Slots are inherently public; they are meant explicitly for inter-class communication.

The whole pedantic discussion above would make more sense if protected slots were the topic. For private slots, it's just silly. And the protected case is actually more interesting; here, in order to preserve the traditional C++ meaning of "protected", the metadata system would have to keep track of object inheritance to ensure that only related classes could signal one another - which, again, is somewhat silly since the whole point is inter-object communication, but here it's somewhat less silly than it is when the topic is private slots. In the end, however, one would have to reproduce a signficant chunk of the C++ compiler in order to pull this sort of thing off.

And for what purpose? The goto statement is also considered "bad", but lots of people still use it, and even modern languages like Java support it despite being able to do perfectly well without it through the use of other, "better" constructs. Varying degrees of access protection are useful, but insisting on strict adherence to this paradigm in a signalling system is pointless. Anyone concerned about this is, presumably, a programmer. By implication, that means you should be aware of the potential pitfalls, and should simply avoid them.

Thanks for your (fueled) response and your good points. Let's not get started on the inheritance of signals and slots though. It's clearly something that the Qt Keepers are aware of and it might be fixed up down the line.

I take your point about Qt signals and slots being an inter-class communication mechanism, but it is also very useful in other situations. For example,

Class A has a QTimer. It wants to do something private (modify some or other private member) when the timer times out. It connects signal QTimer::timeout() to its (private) slot MyVeryPrivateSlot(). But now that private slot can be connected to and triggered by anyone outside the class. Sure class A could call private function MyVeryPrivateFunction(), but how else could class A respond to QTimer::timeout() other than with a slot?

It was not my intention to enter into a ridiculously pedantic debate on the intricacies of Qt's signalling framework. I'm a fan, else I wouldn't be here. Just wanted to get some opinions on the topic and find out if there were any creative solutions.

wysota
20th July 2010, 01:11
There is a nice button on this forum under every post called "Multi-Quote This Message". Please use it next time instead of posting multiple posts.

As for the discussion: remember that access protection is done purely during compilation based on a very simple rule, there is no actual check made (run-time) whether one can call a particular method or not hence enforcing such check during run-time for Qt slots would only create overhead without any practical benefits. Many languages (like Python) have simply ignored access protection and treat all methods and fields as public. Working around protection in C++ requires 4-5 lines of code so if someone wants to have access to a private or protected member of some class, there is practically no way of stopping him. On pure academic level I can understand that Qt should support access protection to its signals and slots but as an engineer I say there is no practical argument to support this claim.


but how else could class A respond to QTimer::timeout() other than with a slot?
Through events.