You are not logged in.

#1 2005-04-14 15:21:43

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

function pointer question

ok, let it be known that I am generally a c++ programmer, so some parts of c I am a bit unfamiliar with.  this generally includes generic (un-typesafe) casting and things of that nature (as c++ is supposed to be typesafe).

So here's my question.  I'm trying to strap a c interface on top of something.  The internal code allows for "generic" slots to be attached to a signal.... errm let me demonstrate with pseudo-code:

void func1(int);
bool func2(int,long,char*);

add_slot("signal1",func1);
add_slot("signal2",func2);

now... in the c++ portion, I can take advantage of templates in order to make it generic.  But when interfacing to C, I can't do that.  I was thinking I should just do something like:

typedef void* slot_func;
add_slot(char*,slot_func);

and just enforce the call at runtime.  it's what I have to do with the c++ portion, as due to the generic-ness (?) it can only be enforced at runtime...

can any c programmer point me in a better direction (if there is one)? I checked out some other code-bases, and things like irssi defines the "slot_func" as void f(void*,void*,void*,void*,void*); which just seems messy to me... *shrug*

thanks ina dvance for comments...

Offline

#2 2005-04-14 15:58:26

i3839
Member
Registered: 2004-02-04
Posts: 1,185

Re: function pointer question

At first glance it doesn't make sense. How is whatever handles the signals knowing what type the functions it calls are (assuming that it is something like that)?

If it's some kind of plugin system where the plugin knows what it's talking about, and the users using that plugin do, but it's all squeezed through some sort of generic "signal" layer, then it looks like the design is broken.

If, on the other hand, the types for each signal function are predefined, but they just must be registered with a generic function add_slot() then just use void* pointers as the generic part won't care what it gets anyway.

Take a step back and look what you try to achieve, or at least tell us so we know what it's about.

void f(void*,void*,void*,void*,void*); is very messy indeed, and looks like they were either desperate or didn't care...

Offline

#3 2005-04-14 16:20:49

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

well, in the c++ portion of the code, each signal has a general generic type, and are all thrown into a collection of signal_base objects...
that is (first type is return type, next is param1, param2, etc):

signal<bool,int,int> a; //bool foo(int,int);
and
signal<void> b; //void foo();

are added to the same container.

Now, of course, one can say "hey attach 'bool foo(int,int)' to signal b and it will work fine under this generic implementation" - and yes it will.  except it will fail at runtime (it's the same as passing a function to a void* really... except that will just seg fault if called wrong).

the main accomplishment here is the ability to have a plugin system in which each plugin can register generic functions with the core... I think in my head I have something along the lines of an IRC client (the reason I checked irssi):
* load plugin foo.plugin
* foo.plugin registers alias "square" and "hello"
* /square returns "error: try /square <num>"
* /square 5 returns "25"
* /hello returns "hi"
* /hello bob returns "hi, bob"

but a bit more generic... the irc plugins know they take input text and output text.. so there's some common ground there...

I'd like to do it without defining the slot format... (void*,void*,void*...blah) and making it too strict...

I guess I could always just enforce c++ in all portions... that wouldn't be that bad...

EDIT:  it might also work to just pass the params as strings... (vecotr<string>) but that won't work in all cases... maybe I can split it into two implementations...

there's also implementations all over the internet of a "union list" type... which might be kinda cool... the union list is defined with an open-ended set of types (union_list<int, union_list<string, union_list<double,list_end> > >) and then used as a type itself to basically say "I can be any of these listed types..." - which is kinda cool...

Offline

#4 2005-04-14 17:29:37

i3839
Member
Registered: 2004-02-04
Posts: 1,185

Re: function pointer question

For instnce, in this case:
* /square returns "error: try /square <num>"
* /square 5 returns "25"

Do you call twice the same function for signal "square", or two different ones (sort of signal overloading)?

Worst case should be something like "int func(char s[], int x, int y, void*)", instead of the 4 or 5 void*'s. What about stdargs? (man stdargs) It's a bit ugly and not nice for the plugin devs, but it is runtime typesafe.

The union won't buy you much except making you feel comfortable for avoiding void* usage. If they mess it up with void* they'll also mess it up with the union. Though if you only have two or three types of functions then it isn't a bad idea.

If you want runtime typesafety then it may be better to make an enum with all types which are supported and store that together with the function pointer (because you can't see from the pointer what type it is at runtime).

The more interesting question is why the storage thing needs to know the type of the things it stores. Or if it doesn't, then how does the code using those pointers know what type of function they point to.

In other words, if on one side of the storage thing you have the generic layer, and on the other the plugins, then how are you handling function pointers of different types? I mean, the generic layer probably generates just a certain event with certain info, do you want to preformat that in the way the plugin wants it or something? Isn't it better to more or less pass on the raw data, enforcing a uniform CB func prototype and letting the plugin do the data handling?

Offline

#5 2005-04-14 18:20:07

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

with the "/square" example, basically the first error was a runtime error because the parameters didn't match.

And yeah, the union list seems a bit crappy to me... it's cool in principal, but I'd never use it.

As for the type question - yeah, for most of the internals, I can define parameter numbers and types... but there's alot... and I wanted to keep it extensible...

I guess you've kinda brought me to realise that making it this generic would be a bit more of a hassle than it's worth... perhaps I need to rethink that part a bit... *scratches chin*

Offline

#6 2005-04-14 18:55:41

i3839
Member
Registered: 2004-02-04
Posts: 1,185

Re: function pointer question

It's a communication problem. You have two sides, the generic part and the plugin part, and they need to understand eachother. Now you have two choices:

1) Let the plugin side define the interface.

2) Let the generic side define the interface.

The plugin side is dynamic and unknown, while you have full control over the generic part. So the obvious solution is to let the generic part decide how to communicate and in which way. Then everything is clear. Sure, the plugins may grumble because they'd prefer it some other way, or whatever, but that's better than chaos.

Watch out that you don't make the generic part too generic, or you may accidentally implement a VM and a scripting language.

Offline

#7 2005-04-14 19:10:26

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

i3839 wrote:

Sure, the plugins may grumble because they'd prefer it some other way, or whatever, but that's better than chaos.

Watch out that you don't make the generic part too generic, or you may accidentally implement a VM and a scripting language.

yeah that's about where your last post got me (not the wording exactly, but seeing the idea from another's point of view) - I started thinking that I would end up with some sort of pseudo scripting language (which would be fun... but it's not the time for scope-creep).

So I think I'll just define a subset of the possible functionality for now... maybe I can pass a pseudo "int argc, char** argv" to some of them... that might be a worthwhile one to define...

Actually, with predefining types, not much changes... beyond the loading mechanism... and that's not that much.

Offline

#8 2005-04-14 19:16:48

i3839
Member
Registered: 2004-02-04
Posts: 1,185

Re: function pointer question

Is this for your WM?

Anyway, good luck.

Offline

#9 2005-04-14 19:38:36

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

i3839 wrote:

Is this for your WM?

Anyway, good luck.

yes and no... yes, because that's the motivation behind it, and no, because I was thinking to create a sort-of "drop in" modular plugin system... as there's nothing out there now to do that... it's be nice to say "hey, libplugin.so - load this plugin and give me some callbacks of this format" - but that's not going to happen anytime soon... and I was getting too far off on a tangent...

Offline

#10 2005-04-14 20:22:12

i3839
Member
Registered: 2004-02-04
Posts: 1,185

Re: function pointer question

If you only want to make a generic layer which provides a way to connect and combine multiple plugins, then it's much easier, I think. All it needs to do is let plugins find eachother and provide a communication channel.

Then it more or less boils down to two things, depending on the grand scheme: Plugins can generate events, and handle events.

All specific event details aren't important for the generic layer, that is something the plugins choose and implement. In this case each event is handled as a unique type: The plugin which handles it should know how to handle the data, if any. Think (void* data, int length), or just void* data, if you don't have variable length data but only strings and pointers to structures and stuff.

The trickier part is what to do with multiple plugins handling the same event.

Of course this way plugins may have too much freedom and the whole may be too dynamic, so you could restrict how plugins communicate with eachother.

Offline

#11 2005-04-14 20:58:48

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

i3839 wrote:

If you only want to make a generic layer which provides a way to connect and combine multiple plugins, then it's much easier, I think. All it needs to do is let plugins find eachother and provide a communication channel.

Then it more or less boils down to two things, depending on the grand scheme: Plugins can generate events, and handle events.

yup, that's exactly what I was thinking...

i3839 wrote:

All specific event details aren't important for the generic layer, that is something the plugins choose and implement. In this case each event is handled as a unique type: The plugin which handles it should know how to handle the data, if any. Think (void* data, int length), or just void* data, if you don't have variable length data but only strings and pointers to structures and stuff.

yeah, again dead-on to what I was thinking.  that would work, but I find it a bit sloppy... well... maybe it would *appear* cleaner doing:

typedef void* callback_data;
typedef bool (*callback)(callback_data);
i3839 wrote:

The trickier part is what to do with multiple plugins handling the same event.

the internals would be c++ signals and slots (I may actually use a different implementation than boost or libsigc++) in which case, they are basically linked lists of slots assigned to a signal... the reason I defined the callback as a bool is because it's not hard to make a signal interruptable... and it's not hard to simply add a slot or add a slot at a certain position in the list (think priority!)... that way the following would happen:

signalX.emit(data);
   //slot[0] called, returns true
   //slot[1] called, returns true
   //slot[2] called, returns false - emit ends
   //slot[3] never called
i3839 wrote:

Of course this way plugins may have too much freedom and the whole may be too dynamic, so you could restrict how plugins communicate with eachother.

yes, agreed... which is part of the reason I was straying from the raw "void*" stream of data...

I was trying to make it so specific parameters can be defined by each signal... and documented by each plugin itself:
pluginA defines a signal with the signature: void foo(int,void*), named "signalA"
pluginB tells the core to connect "void bar(int,void*)" to "signalA" (by name)
pluginC tells the core to connect "bool baz(struct stat)" to "signalA"

at runtime, the connection made by pluginB will succeed and work perfectly.  however, the connection made by pluginC will not work (it's undetectable at compile time... sigh), yet this is found as soon as signalA is emitted (called) - an error is reported and that slot is disconnected from the signal

this is, of course, all implemented in C++ and uses some RTTI (yeah yeah, I know... there's only space overhead for classes with virtual members.. which I don't use often)

Offline

#12 2005-04-14 22:10:51

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

to illustrate what I last said (removed all the template crap for readability):

template <...>
inline sigc::signal<...>* sigtype_check(const sigc::signal_base& sig)
{
    if(typeid(sig) != typeid(sigc::signal<...>)) return 0;
    return static_cast<sigc::signal<...>*>(&sig);
}

that will verify the typeids and return 0 if they don't match... so then the connect function can look like this:

template <...>
bool connect(const std::string& name, sigc::slot<...>& s)
{
    signal_iterator it = _signals.find(name);
    if(it != _signals.end())
    {
        sigc::signal<...>* sig;
        if(sig = sigtype_check<...>(it->second))
        {
            sig->connect(s);
            return true;
        }
    }
    return false;
}

so trying to attach a slot of the form "bool foo(int,void*)" to a signal of the form "void bar(long)" will just return false and not do anything...

I'll do a similar check on the emit call.. just because that can fail as well

Offline

#13 2005-04-14 22:38:13

i3839
Member
Registered: 2004-02-04
Posts: 1,185

Re: function pointer question

However nice runtime typechecking may be (either with some help of C++, or explicitly with enums or something), it doesn't really add anything. There can be two events with type (char*, int), but if the one thinks that the char is a string, and the other thinks that it's data with length int then bad things still happen.

The thing is, there's a miscommunication somewhere, or someone did something stupid. You should handle each kind of event as a seperate type, only then you know for sure that there's no miscommunication. But as you already must say which event you want to handle, by providing its name, you should already know all about it, including the function pointer type.

So what's left is protecting people against themselves, but we don't do that in C. ;-)

If on the other hand you want to protect the app against bad plugins, then you should restrict the interface in such way that they can't cause havoc. Or catch SIGSEGV and unload the evil plugin (though the damage may already be done then).

I'd worry more about race conditions and proper locking if you're going to make it multithreaded, instead of easy to solve silly type mistakes.

[Edit]
Also the chain of callback functions with true or false and priorities to handle which one comes first may look good at first glance, but I'm afraid it will be a race for the highest priority between the plugins (e.g. new plugin versions having only increasing prio values).

How should a plugin know if the other ones may be executed or not? Why does it have the power to say so? How do you know if it has the correct priority, and did you try all combinations of plugins to figure that out?

All in all I'd minimize the impact the plugins can have on eachother, other than by generating events. Events either can have multiple handlers, or not. Same for event handlers: they either can work with others, or not.

Offline

#14 2005-04-15 15:47:57

phrakture
Arch Overlord
From: behind you
Registered: 2003-10-29
Posts: 7,879
Website

Re: function pointer question

i3839 wrote:

There can be two events with type (char*, int), but if the one thinks that the char is a string, and the other thinks that it's data with length int then bad things still happen.

The thing is, there's a miscommunication somewhere, or someone did something stupid. [...] But as you already must say which event you want to handle, by providing its name, you should already know all about it, including the function pointer type.

I agree to an extent - I feel the runtime checking would still be worthwhile, to prevent the core itself from segfaulting... (I could make a plugin called 'segfault' that just attaches to random events with void foo() if that checking weren't there).  Miscommunication is another big problem, but I think enforcing c++ plugins would make it slightly more preventable (i.e. passing a string class or passing a buffer class would mean different things - both classes are *almost* the same... buffer is mine though)

i3839 wrote:

Or catch SIGSEGV and unload the evil plugin (though the damage may already be done then).

I thought alot about that... I figure with enough reporting it'd be fine... any typical plugin-oriented app can be taken down by poor plugins...  hell, I can write a goofy vim script and have it take down vim hard... I figure I'll let crappy plugins do what they will, for now

i3839 wrote:

I'd worry more about race conditions and proper locking if you're going to make it multithreaded, instead of easy to solve silly type mistakes.

yeah... gotta work on that once I begin adding in threaded plugins...

i3839 wrote:

All in all I'd minimize the impact the plugins can have on eachother, other than by generating events. Events either can have multiple handlers, or not. Same for event handlers: they either can work with others, or not.

I think I was going to use the priority stuff only internally (so I can strap in, say, a debug handler which will just output "x event generated" when the signal is emitted...) but I'm not even sure I'll need that...

Offline

Board footer

Powered by FluxBB