Allegro.cc - Online Community

Allegro.cc Forums » The Depot » Official algui thread.

This thread is locked; no one can reply to it. rss feed Print
Official algui thread.
Thomas Fjellstrom
Member #476
June 2000
avatar

And I don't understand why when Allegro 5.0 wasn't ready, there was already a 5.1 version...

Odd minor releases are always WIP or development releases. All 5.1, 5.3, 5.5, 5.7, and 5.9 releases will be development, much like 4.9 was.

5.1 is the "in progress" devel branch for 5.2, 5.3 for 5.4, etc.

Its basically the older style linux kernel version release cycle.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

axilmar
Member #1,204
April 2001

hmm.. I supposed it.. but why are you using Allegro 5.1? 5.0 has just been released.

The makefile was kindly donated by Bamcaig (thanks man!). I suppose he has Allegro 5.1?

Evert
Member #794
November 2000
avatar

axilmar said:

They are counter productive. It's simply bad code to have switch functions with many hundreds of lines of code.

This is debatable. It's certainly easier to read code that is nice and compact, but that goes for any block of code, not just switch statements.

Quote:

I am fan of callbacks (signals and slots actually).

In my library, users will be able to write callbacks which connect the appropriate widget actions to anything they want, including other widget actions, of course.

That's fine as a choice. But it's not how I would design an Allegro 5 GUI. In a sense, callbacks and handlers are how Allegro 4 did many things. Allegro 5 has an event system instead. Part of the point of having an Allegro 5 GUI, to me, would be to use the event system.
Also, be aware that in the previous thread, there was quite a bit of discussion on whether callbacks lead to bad programming style. Not everyone is going to like it if you base your GUI around callbacks.

Not that you can't put an event system on top of a callback system (just inject an event from the callback), but it does feel a bit clunky.

Thomas Fjellstrom
Member #476
June 2000
avatar

Evert said:

Part of the point of having an Allegro 5 GUI, to me, would be to use the event system.

Qt Has an underlying event system, which its Signals and Slots sit on top of. Often signals and slots are sent directly via a regular method call (at some point), but you can also queue them up, which are then sent via the event queue.

For a pure GUI app, signals and slots make the most sense IMO. But it can take some extra work to get a game to fit into it, if you don't just handle the main game stuff along side the gui.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

axilmar
Member #1,204
April 2001

Evert said:

Allegro 5 has an event system instead. Part of the point of having an Allegro 5 GUI, to me, would be to use the event system.

Do you really want to litter your code like this?

#SelectExpand
1switch (event.type) { 2 case WIDGET1_EVENT1: 3 ... 4 case WIDGET1_EVENT2: 5 ... 6 case WIDGET1_EVENT3: 7 ... 8 case WIDGET2_EVENT1: 9 ... 10 case WIDGET2_EVENT2: 11 ... 12 case WIDGET2_EVENT3: 13 ... 14 ... 15}

This kind of code becomes unmaintainable very quickly, especially if event types must be managed manually.

Quote:

there was quite a bit of discussion on whether callbacks lead to bad programming style.

I suppose you mean the comments about callbacks leading to many global variables and many type conversions whereas messages in the queue don't need all that.

It's true that with the message queue solution, you don't need global data or type conversions. However, there is a solution: an API that creates an array of void* pointers that is passed to the callback can be extremely useful. For example:

void** algui_callback_data(void *data, ...);

In the place the widget callback is set:

algui_set_button_click(button1_click, algui_callback_data(data1, data2, NULL));

And then in the callback:

void button1_click(void **callback_data) {
    void *data1 = callback_data[0];
    void *data2 = callback_data[1];
}

However, the above does not solve the problem of type conversions.

In case of using custom events, the user event type generation can be automated: when a widget is created, it allocates a new event id by calling a specific function. The event type is stored internally in the widget. For example:

ALGUI_BUTTON *algui_create_button() {
    ALGUI_BUTTON *btn;
    ...
    btn->click_event_type = algui_next_event_type();
}

ALGUI_BUTTON *btn1 = algui_create_button();
ALGUI_BUTTON *btn2 = algui_create_button();

But then, in the message loop, one would have to do the following:

if (event.type == algui_get_button_click_event_type(btn1)) {
}
else if (event.type == algui_get_button_click_event_type(btn2)) {
}

This is slower than callbacks, and perhaps leading to giant if-then-else statements.

Anyway...I can't tell which is worse, at this point. Let me think about it. Additional comments are welcomed.

What do you think about the automated event type generation?

Evert
Member #794
November 2000
avatar

axilmar said:

Do you really want to litter your code like this?

That rather depends. For some widgets, I may not be interested and simply discard their events. I would probably wrap the entire thing up into a function as well.
It all depends on how customisable I want the dialog box to be.

Perhaps the best way to do it is to be able to register, or not, a widget as an event source.

Quote:

I suppose you mean the comments about callbacks leading to many global variables and many type conversions whereas messages in the queue don't need all that.

It's true that with the message queue solution, you don't need global data or type conversions. However, there is a solution: an API that creates an array of void* pointers that is passed to the callback can be extremely useful.

I would go so far as to say that you need something like this in a callback situation. Type conversions aren't such a big deal, in a sense they're simply a consequence of using C rather than C++.
Note that even Allegro's event system (if you use the user event API) passes custom data around using void pointers.

axilmar
Member #1,204
April 2001

Evert, What do you think about the automated event type generation?

EDIT:

I guess widget events better be Allegro events, i.e. placed in a queue.

But widget messages should not be Allegro events. The algui message system is about object orientation, not about events.

I really like the automated event type generation: it allows the programmer to do all the work from the main loop, but it also solves the problem of event type management.

AMCerasoli
Member #11,955
May 2010
avatar

Sorry for interrupt your deep conversation about programming...

But I want to know, I compiled an Allegro 5.1 Windows binaries, it seems that works fine... (they are attached, if someone want to test it), but I still getting:

$ make
gcc -shared -Wl,-soname,libalgui.so -o lib/libalgui.so.1 obj/algui_list.o obj/al
gui_rect.o obj/algui_tree.o obj/algui_widget.o `pkg-config --libs allegro-5.1 al
legro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1`
Package allegro-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro-5.1.pc'
to the PKG_CONFIG_PATH environment variable
etc...
etc...
etc...

What are those "allegro-5.1.pc" files?

EDIT: Binaries doesn't work, when I run my program using them, It ask me for freetype6.dll. I did something wrong when compiling allegro... FreeType should be static or something.

SiegeLord
Member #7,827
October 2006
avatar

Those are files used for pkg-config program. I don't think they are made anywhere else except in Linux. Just ditch the Makefile and compile it using gcc.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Peter Wang
Member #23
April 2000

axilmar said:

I am using ASCII strings for widget ids. Does anyone think that ASCII strings are not enough for such a job?

I agree, but..

Quote:

for me, handling ASCII strings for widget ids has the following advantages:

None of the advantages you listed have anything to do with ASCII or not, except the last. Even that is dubious. If your important characters are all within the ASCII range -- in practice, yes -- a UTF-8 string looks just like an 8-bit extended ASCII string. Non-ASCII characters are just passed through.

Anyway, this is verging on off-topic.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

axilmar said:

It's not as trivial as you mention. You have to manage notification ids per widget: for every new command or option you put in the program, you have to allocate a new notification id. And this has to be done manually by the programmer. This is something that I have experience on due to using MFC, and let me tell you that it is very difficult and time consuming.

Actually it is rather trivial to do. I'll use my GUI as an example since I use this system quite easily and it adds very little overhead and only requires minimal effort on the part of the user.

It starts with the WidgetMessage class :

WidgetMessage.hpp#SelectExpand
1/* Returns the next available message id value to prevent message collision. 2 Use TOPIC_* naming with 3 4 extern const unsigned int TOPIC_WHATEVER; 5 in the header and 6 7 const unsigned int TOPIC_WHATEVER = NextFreeTopicId(); 8 in the source module. 9*/ 10unsigned int NextFreeTopicId(); 11 12 13 14class WidgetBase; 15 16class WidgetMsg { 17 typedef unsigned int UINT; 18 19private : 20 WidgetBase* from; // Address of the widget returning this message. 21 UINT topic;// Whatever this is in regards to, like it being about a dialog. Each widget class is 22 // assigned a value for this field from the NextFreeTopicId function, as well as 23 // there being a USER topic for all generic notifications to the user. 24 int msgs; // Bitfield for setting message flags, or single message id. Up to the widget class. 25 26public : 27 28 WidgetMsg(); 29 WidgetMsg(WidgetBase* widget_address , UINT widget_topic , int messages); 30 WidgetMsg(const WidgetMsg& wmsg); 31 32 ~WidgetMsg() {} 33 34 inline WidgetBase* const From() const {return from;} 35 inline const UINT Topic() const {return topic;} 36 inline const int Messages() const {return msgs;} 37 38 inline bool IsAbout(const WidgetBase* widget , UINT widget_topic) const { 39 return ((from == widget) && (topic == widget_topic)); 40 } 41 42 inline bool IsMessageTopic(UINT widget_topic , int message) const { 43 return ((topic == widget_topic) && (msgs == message)); 44 } 45 46 inline WidgetMsg& operator=(const WidgetMsg& wmsg) { 47 from = wmsg.from; 48 topic = wmsg.topic; 49 msgs = wmsg.msgs; 50 return (*this); 51 } 52 53 inline bool operator==(const WidgetMsg& wmsg) const { 54 return ((from == wmsg.from) && (topic == wmsg.topic) && (msgs == wmsg.msgs)); 55 } 56 57 friend std::ostream& operator<<(std::ostream& os , const WidgetMsg& wmsg); 58 59};

WidgetMessage.cpp#SelectExpand
1 2#define NONE 0 3 4 5 6unsigned int NextFreeTopicId() { 7 static unsigned int id = NONE; 8 id += 1; 9 return id; 10}

Each widget has its own TOPIC and an enum describing the messages it may send :

In Widget.hpp

extern const unsigned int TOPIC_BUTTON_WIDGET;

enum BUTTON_MSGS {
   BUTTON_CLICKED  = 0, // Sent whenever a spring button is pushed down.
   BUTTON_HELD     = 1, // Sent whenever a spring button is held down.
   BUTTON_RELEASED = 2, // Sent whenever a spring button is released.
   BUTTON_TOGGLED  = 3  // Sent whenever a toggle button is toggled, poll for the current state.
};

In Widget.cpp

const unsigned int TOPIC_BUTTON_WIDGET = NextFreeTopicId();

When something important happens, the widget queues a WidgetMessage. QueueUserMessage is a virtual function that each widget may or may not redefine. The base class implementation of QueueUserMessage sends the message to it's parent if it has one, or if it is a top level widget then it adds the message to a list that can be retrieved by the user.

In Widget.cpp, using a very basic button as an example :

WidgetMsg Button::CheckInputs(int msx , int msy) {
   UINT ret = DIALOG_OKAY;
   if (area.Contains(msx,msy) && input_mouse_press(LMB)) {
      QueueUserMessage(WidgetMsg(this , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED));
      ret |= DIALOG_INPUT_USED;
   }
   return WidgetMsg(this , TOPIC_DIALOG , ret);
}

And finally, how the user uses the message :

In main.cpp :

#SelectExpand
1Button btn1; 2WidgetHandler gui; 3gui << &btn1;// Adds button 1 to the gui 4 5 6const WidgetMsg btn1click(&btn1 , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED); 7 8// in logic loop 9 WidgetMsg wmsg; 10 while (gui.HasMessages()) { 11 wmsg = gui.TakeNextMessage(); 12 13 if (wmsg == btn1click) { 14 do_anything_we_want_here(); 15 } 16 17 }

All in all, it's very simple and all the user has to do is compare a WidgetMsg to see if a specific action occurred. If you're not interested in a specific message, don't check for it. Sure if you have a lot of messages then the comparisons can get long, but there's no reason you have to use a switch statement that includes every case of action represented by a WidgetMsg.

Contrast this to using callbacks and/or signals and slots. To do this, the user has to create a struct, bind the addresses of affected data to that struct, set the address of that struct(s) for their callbacks, set the callback to their handler function, and then write the code for their callback/slot. If you decide to change what you're doing inside that callback, then you need to modify the struct to reflect the new data, modify your callback to reflect the new struct, and modify the callback to reflect the new procedure.

This is all a whole lot more work than it is worth when you could have just compared a single message and then written local code to accomplish what you want to do.

My junior high algebra teacher taught me an important principle, KISS.
Keep It Simple Stupid. I think that principle applies here, and it's a good one to follow.

Matthew Leverton
Supreme Loser
January 1999
avatar

I actually took a look at my source code again, and I see I also wrote this function:

void algui_observe(void *component, int event_type, bool (*proc)(void *, void *));

used like:

bool button_onclick(void *self, void *event)
{
  ALGUI_BUTTON *button = (ALGUI_BUTTON *) self;
  ALGUI_CLICK_EVENT *e = (ALGUI_CLICK_EVENT *) event;
  // ...
  return true;
}

algui_observe(button, ALGUI_CLICK_EVENT_ID, button_onclick);

The return code determines whether or not the event bubbles up to the parent. For example, you could observe the mouse click event on the root node of the GUI to process every click.

This step happens after the widget implementation has a chance to process events internally. That is, there's also an onclick entry in the button's vtable. It gets called when the button is clicked, which allows it to do something related to its implementation or behavior.

It can then choose to emit the event to the user, which can handle it by the above code.

I do also support processing events via an ALLEGRO_EVENT_QUEUE, but after reviewing my code and remembering past thoughts, I tend to agree with axilmar that it's not very useful (particularly as the project gets large).

axilmar
Member #1,204
April 2001

@Peter Wang:

these advantages have nothing to do with ascii indeed. They have to do with the default string type C has. Even so, I think that skin files should be kept as simple as possible, following the KISS principle.

@Edgar Reynaldo:

if this was c++, I would definitely use signals and slots, because in c++ you have the ability to bind variables to functions. Your system is good, but signals and slots is alot more simpler.

However, in C, things are different.Since C lacks any kind of templates or lambdas, handling events from the main event queue makes sense, because it allows access to the program context.

But there are cases where the event queue cannot be used. For example, when a text is inserted in a textbox, the usual approach is to invoke a user callback that validates the insertion. I cannot see how this will work with the event queue.

This thing with messages/callbacks/events needs more thought.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

axilmar said:

if this was c++, I would definitely use signals and slots, because in c++ you have the ability to bind variables to functions.

Just because you can do something doesn't mean you should.

Edgar said:

Contrast this to using callbacks and/or signals and slots. To do this, the user has to create a struct, bind the addresses of affected data to that struct, set the address of that struct(s) for their callbacks, set the callback to their handler function, and then write the code for their callback/slot. If you decide to change what you're doing inside that callback, then you need to modify the struct to reflect the new data, modify your callback to reflect the new struct, and modify the callback to reflect the new procedure.

Like I said, using callbacks/signals and slots is more work than it's worth. Why spend the time binding data into a completely different scope just to alter it? Why? ???

axilmar said:

Your system is good, but signals and slots is alot more simpler.

Did you even read my post?

What's simpler than this?

if (wmsg == WidgetMsg(&button1 , TOPIC_BUTTON , BUTTON_CLICKED)) {
   do_anything_here();
}

Give me an example of how using signals and slots or callbacks is simpler than what I just showed you.

axilmar said:

However, in C, things are different.Since C lacks any kind of templates or lambdas, handling events from the main event queue makes sense, because it allows access to the program context.

At least we agree on this.

axilmar said:

But there are cases where the event queue cannot be used. For example, when a text is inserted in a textbox, the usual approach is to invoke a user callback that validates the insertion. I cannot see how this will work with the event queue.

This is one of the few times that an immediate response is necessary and should bypass a message system. However, the widget could be designed to validate the data itself as well.

axilmar
Member #1,204
April 2001

What's simpler than this?

This is a lot simpler (in c++):

button.click += slot(myFunction, myData1, myData2);

EDIT:

Added the following things in the library:

  • a resource manager; it uses reference counting to manage named resources. This is necessary in order to share resources (bitmaps and fonts) between widgets, especially if loaded through a skin.


  • a logging API. Used for debugging.


  • a 'display' widget, which serves as a top-level widget. It has a background color and an optional background bitmap.

I modified the test skin to load resources for the display widget.

The example contains many changes:

  • a game loop that refreshes 60 times per second.


  • init and cleanup functions.


  • the display widget in action.

Here is an image:

{"name":"YDchK.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/7\/5725c68df582c50869db47eccef7ecd8.png","w":652,"h":518,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/7\/5725c68df582c50869db47eccef7ecd8"}YDchK.png

The background is loaded from the skin.

AMCerasoli
Member #11,955
May 2010
avatar

Cheese!! I want it! I want it!

I can't manage to install the library!

Can I compile the library without using pkg-config?

I downloaded the GTK+ binaries so now I have pkg-config but still don't working...

All-in-one bundles
If you find choosing, downloading and unpacking the individual zip archives below a chore, there are all-in-one bundles of the GTK+ stack including 3rd-party dependencies, both of GTK+ 2.16 and 2.22. The bundles contain both run-time and developer files. Many of the developer files are relatively irrelevant. If you intend to redistribute the GTK+ run-time, you need to figure out which files you can leave out yourself. A new bundle will ideally be provided here whenever one of the member packages has been updated.

SiegeLord said:

Those are files used for pkg-config program. I don't think they are made anywhere else except in Linux. Just ditch the Makefile and compile it using gcc.

I don't know how makefile works :-[

Thomas Fjellstrom
Member #476
June 2000
avatar

I downloaded the GTK+ binaries so now I have pkg-config but still don't working...

Why would you need or want GTK? I'm confused.

Quote:

I don't know how makefile works

You don't need to know how make works.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

SiegeLord
Member #7,827
October 2006
avatar

By ditching the Makefile I meant deleting it from your system with extreme prejudice.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

AMCerasoli
Member #11,955
May 2010
avatar

Well first I saw this:

Package allegro-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro-5.1.pc'
to the PKG_CONFIG_PATH environment variable

Then I saw this

LIBS = `pkg-config --libs allegro-5.1 allegro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1`

etc...

${OBJDIR}/_main.o: _main.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_list.o: ${SRCDIR}/algui_list.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_rect.o: ${SRCDIR}/algui_rect.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_tree.o: ${SRCDIR}/algui_tree.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_widget.o: ${SRCDIR}/algui_widget.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

Then went to Google an read this:

Quote:

pkg-config is computer software that provides a unified interface for querying installed libraries for the purpose of compiling software from its source code. pkg-config was originally designed for Linux but is now also available for the various BSDs, Microsoft Windows, Mac OS X, and Solaris.

And thought... "Oh I need pkg-config", still reading and found out that came with GTK+ A library to create GUI, And thought "yhea make sense" and I download it... And now I'm here...

Is something wrong in my brain algorithm?

Thomas Fjellstrom
Member #476
June 2000
avatar

Is something wrong in my brain algorithm?

Yes.. Instead of trying to fetch GTK to get pkg-config, you could have just grabbed pkg-config, or edited out the pkg-config bits and replaced then with the right -lblah strings.

Or manually compiled the code at the command line.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

axilmar said:

This is a lot simpler (in c++):

button.click += slot(myFunction, myData1, myData2);

You forgot all the code that goes with it :

#SelectExpand
1class myData1 { 2public : 3 int* int1; 4 double* double1; 5 myData1(int* int_ptr , double* double_ptr) : int1(int_ptr) , double1(double_ptr) {} 6} 7 8class myData2 { 9public : 10 bool* bool1; 11 Object* object1; 12 myData2(bool* bool_ptr , Object* object_ptr) : bool1(bool_ptr) , object1(object_ptr) {} 13} 14 15void MySpecialFunction(myData1* md1 , myData2* md2); 16 17// in main 18int special_int; 19double special_double; 20bool special_bool; 21Object special_object; 22 23myData1 md1(&special_int , &special_double); 24myData2 md2(&special_bool , &special_object); 25 26button.click += slot(MySpecialFunction , &md1 , &md2); 27 28// after main 29void MySpecialFunction(myData1* md1 , myData2* md2) { 30 if (*(md2->bool1)) { 31 md2->object1->DoStuff(*(md1->int1) , *(md1->double1)); 32 } 33}

Contrast that with this :

// in main
int special_int;
double special_double;
bool special_bool;
Object special_object;

// in logic loop
   if (wmsg == WidgetMsg(&button1 , TOPIC_BUTTON , BUTTON_CLICKED)) {
      if (special_bool) {
         special_object.DoStuff(special_int , special_double);
      }
   }

So by using a message system, you saved yourself the trouble of writing two classes, initializing two objects, writing a special function just to deal with that data, and setting up a slot.

If you want to modify the behaviour of your button, it's much easier to modify the code inside an if (wmsg ==) clause rather than modifying your classes, modifying your class instances, modifying your special function and resetting the slot to reflect the new function and data.

The whole point of writing a function is to either modularize code, or to make reusable code. Functions that use special classes holding data specific to your program will almost never be reusable, so the only reason left is to modularize code. The problem is, code inside an if clause is just as modular as code inside a function, so there's really no point in making up a function, classes, extra object instances just to refer to data, and signals and slots to connect local data with your function.

So you tell me which is simpler. Like I said before, why bind local data to another scope to deal with it there when you could have just dealt with it locally to begin with?

axilmar
Member #1,204
April 2001

Edgar, why did you go to all that trouble? you could have simply written this:

button->click += slot(bind(my_function, special_int, special_double, special_bool, ref(special_object)));

why bind local data to another scope to deal with it there when you could have just dealt with it locally to begin with?

Signals and slots are a lot more simpler and better than handling events in a queue:

  • no need to manage event ids.

  • no need to write huge switch or if-then-else statements.

  • no need to have two types of callbacks (immediate callbacks and events from the event queue).

  • signals and slots allow for putting together the initialization of an object and its signals connections; with events in a queue, this binding is far away from the place objects are initialized.

  • signals and slots are better for the model-view-controller pattern: objects can be instantiated at one place and then passed to views, and those views can monitor the model by adding signals to them; signals and slots is a more object-oriented solution.

  • signals and slots work independently of the main event loop: objects can have signals and slots in other threads that don't have an event loop.

  • less memory footprint: with events, you need to constantly allocate and free memory for each event structure, fragmenting memory; with signals, you do the allocation once (for the slot) and then reuse the same slot.

  • calling callbacks immediately leads to easier debugging; with events in a queue, it is a lot more complex: at the time the event is handled, the state that produced the event might have change, leading to confusion.

  • signals and slots are better for time-sensitive loops because slots are executed immediately, unlike events which will be executed whenever their turn comes.

  • signals and slots are better for debugging because the slot execution order does not change, unlike events which may come in any order.

  • signals and slots can be created at run-time by reading a configuration file, unlike events, because event handling code doesn't have a name, whereas slots can have a name (the name of the function or method that is bound).

  • signals and slots can be created at compile-time from a configuration file, from a GUI IDE creator, like Qt Creator does. No such luck with events.

  • C++0x will have lambda functions, which will make the bind function redundant. And lambda functions can access the local state. And lambda functions can be slots, unlike events, so there is no need to modify existing code to accommodate lambda functions.

  • when using signals and slots, objects in slots can be bound using weak pointers; when the objects are deleted, the slots are invalidated and removed from the signals they belong, and thus only the necessary signals remain to run. This can't be done with events: the event code will be there, even if it will not be executed since the object it references is no longer alive.

  • signals and slots is a lot more flexible system: slots can be added to signals from any place in a program; with events, all the necessary event handling code must be in the same place. You can't add new event handling code in a running system.

  • signals and slots works better with plugins (i.e. DLLs): just pass the signal to the plugin object, which will add a slot to invoke the necessary plugin function. With events, you have to imitate slots (i.e. registering callbacks).

  • signals and slots can be chained: signals can be slots. Can't do this with events.

  • signals can be used as functions in functional programming style. Can't do this with events.

  • signals can be bound to arguments, producing function objects with less parameters. This leads to better code reuse than events.

  • signals are better documented than events: signals are visible as object members, and can be documented accordingly. Events are more difficult to document, since there is an event id and event struct declared separately.

  • signals are much easier to declare than events: signals are declared as one liners (signal<int, double, string>) vs events which are declared with an event type id and associated struct.

  • slots can be generic function objects, for more code reuse; generic function objects can be used in many places besides slots: callbacks, lambda functions, message queues, etc.

  • signals can be emitted in one thread, and slots can be executed in another thread, using the same expression (signal += slot). You can do this with events, but you need to invoke different functions (usually it's put-event vs post-event).

  • signals and slots don't need an event queue: you can take code that uses signals and slots from the main thread and put it in another thread without changing anything. No such luck with events.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

axilmar said:

Edgar, why did you go to all that trouble? you could have simply written this:

Pretend what I wrote was in C. How else would you have done it then?

axilmar said:

button->click += slot(bind(my_function, special_int, special_double, special_bool, ref(special_object)));

And how are you going to recreate that same functionality in your GUI library using C?

axilmar said:

Lots of stuff about signals and slots...

But you're using C. How will you recreate signals and slots in C?

bamccaig
Member #7,536
July 2006
avatar

SiegeLord said:

Comments about the build system:

  • Handmade Makefiles were never okay, especially those that hard-code the source files in them

  • Makefile wants Allegro 5.1, there's no good reason for Allegro 5.1 to be necessary for this library

  • stricmp wasn't found on my Ubuntu

As was pointed out, the Makefile was contributed by me. If you don't like the Makefile there then fix it or replace it with something better. That's the best that I know how to do right now, but it's better than plain bash scripts or manually typing commands every time. :P

As for the Allegro 5.1 usage, I honestly don't know anything about it... All I know is that at some point somebody (probably in IRC) pointed out 5.1 (or maybe I just saw the branch in Subversion) and I started using it. I have no objections to updating the Makefile to use 5.0 (I'd do it now, but I'm in Windows still ripping CDs).

I'm getting this:

*snip*

I'm using MSYS

The Makefile was written in Linux (Fedora 13). It's not very robust and not platform-independent. The good news is that it's open source so go ahead and fix it and contribute your fixes. ;) Or if you don't care about a build system and just want to build it, compile it manually; on the command-line or using your favorite IDE. The pkg-config system is used in Linux to fill in the link libraries in a pretty way. Allegro 4 did something similar with allegro-config, but again, I think that was Linux only. Have you considered switching to Linux? :P

axilmar
Member #1,204
April 2001

Edgar, C is a different language than C++. We were talking about C++. Whatever applies to C++ does not apply to C and vice versa.

For now, I am thinking about a simple system: callbacks. Each widget will have one or more callbacks for its 'events'.

People that like events can always install callbacks that post events to the event queue.

I prefer callbacks because there is a more universal solution than events. I like my library to be consistent.

EDIT:

Version 0.0.0.8 is available.

I changed the internals of the skin and resource manager APIs to use UTF-8. Now everything uses UTF-8.



Go to: