[C++] Function pointers with a variable number of arguments (and types)

Started by
5 comments, last by Antheus 15 years, 8 months ago
I want users (= developers, I'm building a library) to be able to pass all sorts of functions with variable number of arguments and where the type arguments have can be variable too. Users would pass 2 functions pointers as parameters, both with unknown parameters...at some point I will pass the arguments passed to function 1, to be passed onto function 2...so I think I'm in dangerous area here (function 1 can pass more parameters then function 2 can take!). This seems to work, but I have no idea if it's valid:
void function(int a, int b)
{
    std::cout << a << b;
}

int main()
{
    void (*f)(...) = (void (*)(...)) function;
    f(5, 4);
    f(1, 2, 3); // boom? prints '12'
    return 0;
}

This would mean I can build a vector containing multiple function pointers, which would all be executed whenever a certain function is called (emit(), I'm building a signal and slot system). It seems Qt has the most elegant way of signals and slots, 3 other projects (including boost) used a way in which one signal object defines the parameters for all functions pointers it can store using templates, not really useful I find, since functions can have different parameters each time and it would mean multiple signal objects just for one button...which one comes first? how do we know we have enough objects? I can aswell make one object per number of arguments (since that system has a new class defined per number of arguments), but that would be a waste. So I decided to make one myself (Qt isn't open source), and I try to recreate a signal slot system with the ease of use Qt has. Suggestions? Is the code I provided fail-safe? Or is it completely unpredictable? Thanks.
[size="2"]SignatureShuffle: [size="2"]Random signature images on fora
Advertisement
Casting function pointers is extremely dangerous unless you approximately 110% sure of what you are doing.

Laying aside the "how" for a second, I am wondering how the calling code would work. How are you generating the arguments? How can the types vary? How will you call the functions?
Quote:Is the code I provided fail-safe? Or is it completely unpredictable?


It's hammering the round peg into square hole.

Quote:since functions can have different parameters each time


Now think about this logically. Button triggers OnPressed(Source, Key), where source and key are populated by the button.

What purpose would redefining it as OnPressed(Milk, Pizza, Jedi, Apple) have? How will you call this function? Where will parameters come from?

The event system works like this:
Input -> Button --OnPressed--> UserCode1                           --> UserCode2                           --> UserCode3


Or better yet, I want to listen to OnPressed(Source, Key), and I receive GreatestHitsOfBarryManilow as Key....

The problem you're trying to solve doesn't exist, nor does it make sense from functional perspective.
Quote:Original post by Antheus
The problem you're trying to solve doesn't exist, nor does it make sense from functional perspective.


That might be true, Qt did use it that way so I thought it would be handy... What would source and key contain in your example?

I also though of just passing an integer, with which you can then write switches inside the function or w/e :-/. I could aswell have no parameters at all for the functions...unsure if thats a good idea.

I can't really imagine the logic in my head, somehow...can't grasp what would be easy and usefull to use...:S

[size="2"]SignatureShuffle: [size="2"]Random signature images on fora
Original post by Decrius

Quote:What would source and key contain in your example?
Source is the pointer to Button sending the event, Key is a class, containing description of key.

Milk (bool isFatFree) and Pizza (std::vector<Toppings>) have common subclass of Food (bool isGood), Jedi is a std::set<Midichlorians> and Apple extends Fruit (isPeelable() and keepsDoctorAway() both return true), which extends Food. Why this wouldn't make even a slightest shred of sense in context of Button's OnPressed() event should be obvious.

It's an absurd example, but this is exactly the purpose of your example. Anything really is anything.

Quote:I also though of just passing an integer, with which you can then write switches inside the function or w/e :-/.


Quote:OnInt(int whatever) {
switch (whatever) {
case NowWhat : ;
}
};
Who sent it, what was the event, why was it sent, and even if button did send onPressed event, it may contain 55 random characters and an enum.

Quote:I could aswell have no parameters at all for the functions...unsure if thats a good idea.
Again: *how*, not *why*.


Signal/Slot systems are well thought out, and have gone through decades of use in one way or another (post office used them long before then).

Imagine you sending a letter. When post receives it, they put it into one of the boxes based on receiver. Someone else then empties entire box, and delivers it.

Now imagine you write, as recipient, "42". Letter still looks the same (function pointer), but its address (the function parameters) no longer make sense.

There might, under very limited circumstances, be implied context. in small village, someone will recognize your handwriting, and know that 42 is your friend's house number, so they'll deliver it.

But this implied knowledge doesn't apply to generic messaging systems. Even if it does, it's fully and completely covered by boost::bind.
Quote:Original post by Antheus
Source is the pointer to Button sending the event, Key is a class, containing description of key.


Thanks for the reply Antheus :), so, source is a pointer to the Component class from which the Button class derives? But what is the Key doing? And why would one need a pointer to the source?

Also, the thing I was wondering about, what if we have a counter, and when users change it, it emits the new value? Or is this the purpose of the source pointer so that the receiver can get the new value through the pointer? Yeah, that looks like a very good solution, still wondering what you mean with the Key class :).

Thanks!
[size="2"]SignatureShuffle: [size="2"]Random signature images on fora
Quote:Original post by Decrius

source is a pointer to the Component class from which the Button class derives? But what is the Key doing? And why would one need a pointer to the source?


It's whatever you need it to be.

Why?

Let's say you have 1 single handler, that changes color of button which has been pressed.

When you receive the pressed event, you need to determine whose color to change. So:
onButtonPresset(Component c){  if (c is Button) {    ((Button)c).color = RED;  }}


And then it doesn't matter which button was pressed, you handle them the same.

Quote:Also, the thing I was wondering about, what if we have a counter, and when users change it, it emits the new value? Or is this the purpose of the source pointer so that the receiver can get the new value through the pointer?


That's implementation detail, and is used with bus architectures that provide latest-only.

Users express interest into a certain value, and are notified when it changes. Such systems are usually used with polling, so each value is a tuple (changed, value). Users then poll periodically. If they determine 'changed = true', they read the value, and set changed to false.

This type of systems are sometimes used in real-time or embedded settings, since it's easier to control the resources, and backlog is less of an issue.

Quote:Yeah, that looks like a very good solution, still wondering what you mean with the Key class :).


Events need to be classified, to know what has happened. For example:
- ButtonEvent(ButtonState)
or
- ButtonPressed
- ButtonReleased
or
- ButtonDown
- Buttonup
- ButtonPressed (down+up in sequence)

or even
- Event(EventType, ...)

This later example is least preferred in C++ and type-safe systems, where more robust solutions can be made.

The parameters provided by event however will depend on event type. It's customary to provide event source (explicit or implicit).

This topic is closed to new replies.

Advertisement