The "widget" problem


T

Tom

I don't know if that's the right name for this but it's refers to the
situation that might arise in a GUI framework where classes are used to
represents widgets like buttons and text boxes. Member functions of the
widget like button::press() are called on input. A user defined widget
may contain other widgets as members (for instance a file browsing
dialog has buttons, lists, textboxes, etc..), and may wish to do some
processing of a button press after the original widget has done its
work. Every GUI framework must at some point decide how this
capability is made available to the end user of the library.

The mostly widely used OO GUI framework for C++ is Qt enables this with
their "signals and slots" mechanism. This involves introducting new
syntax into the code and preprocessing the source with the moc (meta
object compiler) system. This doesn't prevent people from being
productive with Qt of course, but it raises the question (which I'm sure
has been addressed before) of how to do this with pure C++ in a
typesafe manner while preserving the ability to seperately compile
widgets. Also we want the process of setting up callbacks to be as
simple as e.g. passing function pointers.

Part of the issue is that one cannot make use of a pointer to a member
function without an instance of the class itself. Yet we don't want
any previously defined widgets to know anything about our new widget
or have to be recompiled,

Let me know what you think of this solution. Here "button"
and "mywidget" are two widgets in the system, mywidget contains a
button and the wants to receive the press events of the button.

class cb_base{
public:
virtual void call () = 0;
};

template<class T>
struct cb_c {

template<void (T::*fn)()>
class cb_t : public cb_base{
public:
T * t;

cb_t(T * _t):t(_t){}

virtual void call(){
((*t).*(fn))();
}
};

};

class button{
public:
cb_base * callback;

button(cb_base * _callback=NULL):callback(_callback){}

void press(){
printf("In press\n");
if(callback) callback->call();
}
};


class mywidget{
public:
typedef cb_c<mywidget> cb_type;

button * b;

mywidget(){
b = new button(new handler_cb(this));
}

void my_handler(){
printf("In my handler\n");
}

typedef cb_type::cb_t<&mywidget::my_handler> handler_cb;
};

/* An example of calling the press event, which would be done by the
windowing system */
int main(){ mywidget m; m.b->press(); return 0; }


-Thomas Flynn
 
Ad

Advertisements

Ö

Öö Tiib

Let me know what you think of this solution. Here "button"
and "mywidget" are two widgets in the system, mywidget contains a
button and the wants to receive the press events of the button.

You are inventing square wheel there. It is not simple to make good
generic solution for signaling events. One difficulty is that ideally
the lifetime of signalers and listeners is unbound. Also it is safe to
assume that the program is multi-threaded. While GUI is usually ran
in main thread the underlying program logic objects mostly act in
other threads.

Check Boost.Signals2 for implementation and ideas.
http://www.boost.org/doc/libs/1_55_0/doc/html/signals2.html
 
S

Stefan Ram

Tom said:
situation that might arise in a GUI framework where classes are used to
represents widgets like buttons and text boxes.

I've seen /objects/ of classes of for widgets, not classes.
Also we want the process of setting up callbacks to be as
simple as e.g. passing function pointers.

When member functions pointers are used, it is not abvious
how to implement helpful patterns like adapter classes
(»MouseAdapter«) in Java.
Part of the issue is that one cannot make use of a pointer to a member
function without an instance of the class itself.

Do you think of OOP in terms of classes? Then, it would have
to be called »class-oriented programming«.
Let me know what you think of this solution. Here "button"
and "mywidget" are two widgets in the system, mywidget contains a
button and the wants to receive the press events of the button.

I have not read all of your code. Let me just look at its
interface:
mywidget m; m.b->press();

Something is wrong, when an external entity needs to know
hard-coded that mywidget contains a field »b«.

I would have thought of it this way: The framework object
has a function:

register_press_listener( ... )

The initialization of the button or widget contains code like

framework->register_press_listener( button );

button is an instance of an abstract class »press_listener«
with a function

press( ... )

Now, when appropriate, the framework calls this press
function to let the button know about the press event.

(Instead of runtime-polymorphism, the framework function also
could be a template function or member of a template class,
to implement such a scheme using compile-time polymorphism.)
 
T

Thomas Flynn

Stefan said:
I've seen /objects/ of classes of for widgets, not classes.


When member functions pointers are used, it is not abvious
how to implement helpful patterns like adapter classes
(»MouseAdapter«) in Java.


Do you think of OOP in terms of classes? Then, it would have
to be called »class-oriented programming«.

What do you mean by this? I think that using member fns for the
callbacks has certain advantages in this situation. One of them being
that it also allows the user inherit from the button and modify it if
they wanted to. This example deals with the situation where a widget
contains a button, but when a widget is a button, modified in some way,
then inheriting from it and overloading press() or some other
function altogether would be more appropriate.
I have not read all of your code. Let me just look at its
interface:


Something is wrong, when an external entity needs to know
hard-coded that mywidget contains a field »b«.

I would have thought of it this way: The framework object
has a function:

register_press_listener( ... )

The initialization of the button or widget contains code like

framework->register_press_listener( button );

button is an instance of an abstract class »press_listener«
with a function

press( ... )

Now, when appropriate, the framework calls this press
function to let the button know about the press event.

(Instead of runtime-polymorphism, the framework function also
could be a template function or member of a template class,
to implement such a scheme using compile-time polymorphism.)

Yea I agree that would be the way for the windowing system to actually
become aware of the button. I was just doing m.b->press() to show that
it works, but you would really want
framework->register_press_listener(button );
for example in the button constructor.
 
S

Stefan Ram

Thomas Flynn said:
What do you mean by this? I think that using member fns for the
callbacks has certain advantages in this situation. One of them being
that it also allows the user inherit from the button and modify it if
they wanted to. This example deals with the situation where a widget
contains a button, but when a widget is a button, modified in some way,
then inheriting from it and overloading press() or some other
function altogether would be more appropriate.

I am not sure whether I understand this advantage.

To make it clear what I was thinking about, I wrote the
following code.

I do not see how passing the object instead of a member
function inhibits to inherit from a button and modify it:

#include <iostream> // ::std::cout
#include <ostream> // <<
#include <vector> // ::std::vector

struct press_listener
{ virtual void pressed() = 0; };

struct button : public press_listener
{ virtual void pressed(){ ::std::cout << "pressed" << '\n'; }};

struct framework
{ ::std::vector< press_listener* >observer;
void accept( press_listener * const listener )
{ observer.push_back( listener ); }
void run()
{ for( press_listener * const listener : observer )
listener->pressed(); }};

int main()
{ framework f;
button b;
f.accept( &b ); /* a pointer to an object is passed here
instead of a pointer to a member function */
f.run(); }
 
T

Thomas Flynn

Stefan said:
I am not sure whether I understand this advantage.

To make it clear what I was thinking about, I wrote the
following code.

I do not see how passing the object instead of a member
function inhibits to inherit from a button and modify it:

#include <iostream> // ::std::cout
#include <ostream> // <<
#include <vector> // ::std::vector

struct press_listener
{ virtual void pressed() = 0; };

struct button : public press_listener
{ virtual void pressed(){ ::std::cout << "pressed" << '\n'; }};

struct framework
{ ::std::vector< press_listener* >observer;
void accept( press_listener * const listener )
{ observer.push_back( listener ); }
void run()
{ for( press_listener * const listener : observer )
listener->pressed(); }};

int main()
{ framework f;
button b;
f.accept( &b ); /* a pointer to an object is passed here
instead of a pointer to a member function */
f.run(); }

Right, my program was demonstrating for example how a user who is
defining another widget/window which may contain several buttons and
other things can catch the press events. The issue you talk about here
of how the framework is made aware that "there are buttons which can
be pressed" is separate but important as well
This code can be combined with my first post, by having my button
inherit from press_listener and then both problems will have been
addressed :)
 
Ad

Advertisements

M

Marcel Müller

You are inventing square wheel there. It is not simple to make good
generic solution for signaling events. One difficulty is that ideally
the lifetime of signalers and listeners is unbound. Also it is safe to
assume that the program is multi-threaded.

There are hundreds of signal/slot libs around. Many of them should
address the requirements you mentioned. I have my own one, because on my
platform most libs like boost won't compile. But in C++ it is quite easy
to implement weak binding because you have destructors. (In Java or .NET
it is pure pain.) Of course, this can't be done lock free and wait free.
At some point the observers need to ensure that their event handlers are
unused.
While GUI is usually ran
in main thread the underlying program logic objects mostly act in
other threads.

You can't rely on that. First of all the backend workers might send
signals to the user interface as well as the other way around.


Marcel
 
Ö

Öö Tiib

There are hundreds of signal/slot libs around. Many of them should
address the requirements you mentioned. I have my own one, because on my
platform most libs like boost won't compile. But in C++ it is quite easy
to implement weak binding because you have destructors. (In Java or .NET
it is pure pain.) Of course, this can't be done lock free and wait free.
At some point the observers need to ensure that their event handlers are
unused.

Yes, when it is finally done then it was simple. ;) However when it is in
state that OP posted here then there is *a lot* to learn.
You can't rely on that. First of all the backend workers might send
signals to the user interface as well as the other way around.

How to rely on things that I state are happening "usually" and
"mostly"? :D These words must tell anyone about unreliability either
way. There are only few exceptions like for example GUI based on
OpenGL is best to run in single thread because OpenGL is binding
rendering context to thread and transporting it between threads is
rather painful.
 
T

Thomas Flynn

Öö Tiib said:
Yes, when it is finally done then it was simple. ;) However when it is in
state that OP posted here then there is *a lot* to learn.


How to rely on things that I state are happening "usually" and
"mostly"? :D These words must tell anyone about unreliability either
way. There are only few exceptions like for example GUI based on
OpenGL is best to run in single thread because OpenGL is binding
rendering context to thread and transporting it between threads is
rather painful.

Thanks for the replies. If you guys can explain some of
the problems that might occur and how they are addressed that would be
very helpful. If I understand correctly one issue is that an object
involved may be destroyed while a callback is executing.

-Tom
 
Ö

Öö Tiib

Thanks for the replies. If you guys can explain some of
the problems that might occur and how they are addressed that would be
very helpful. If I understand correctly one issue is that an object
involved may be destroyed while a callback is executing.

Basic things we already mentioned. Loose coupling, circular observation,
thread safety, lifespan tracking. Detailed examples of every issue is
lot longer than Boost.Signals2 code so I won't dream to post it.

For example the code you posted apparently leaks that 'button' made
with 'new button' ... so don't worry, you have long way to go. ;)
 
T

Thomas Flynn

Öö Tiib said:
Basic things we already mentioned. Loose coupling, circular observation,
thread safety, lifespan tracking. Detailed examples of every issue is
lot longer than Boost.Signals2 code so I won't dream to post it.

For example the code you posted apparently leaks that 'button' made
with 'new button' ... so don't worry, you have long way to go. ;)

Thanks. I tried looking it up but could not find any
definition of "circular observation", could you elaborate on that?
 
Ad

Advertisements

Ö

Öö Tiib

Thanks. I tried looking it up but could not find any
definition of "circular observation", could you elaborate on that?

I meant it as situation where A listens signals of B, B listens signals
of C and C listens signals of A. I just coined the term but it
is common test case that somehow tends to fail. People always
make first such solution where that (perfectly normal) situation
suddenly causes deadlocks, memory leaks and so on.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top