legality of signature changes of callback functions

R

regis

Hello,

Suppose that some type of callback function is declared
to take a pointer to some struct as one of its argument.

Is it legal to implement the callback functions as
taking a void* for this argument.

=======================================

typedef struct Foo { int x; } Foo;
typedef void (*Func) (Foo*, void*);

void MyFunc (void* foo, void* user) {
/* void* instead of Foo* -> legal ?
*/
}

....
Func func= (Func) MyFunc;
Foo foo;
....
func (& foo, NULL);

======================================

Same question with:

typedef struct Foo1 { int x1; } Foo1;
typedef struct Foo2 { Foo1 foo1 ; int x2; } Foo2;
typedef struct Foo3 { Foo2 foo2 ; int x3; } Foo3;
typedef struct Foo4 { Foo3 foo3 ; int x4; } Foo4;
....
typedef void (*Func) (Foo1*, void );

void MyFunc (XXX foo, void* user) {
/* where XXX is void*, or Foo2*, ..., or Foo4*
* instead of Foo1*
* -> legal ?
*/
}

....
Func func= (Func) MyFunc;
Foo4 foo4;
....
func (& foo4, NULL);

=======================================

(The context is that I am sometimes fed up
with all those casts in gtk and I wonder if it is
legal to replace GtkWidget* arguments of callback functions
by void* directly in the signature of the callbacks)
 
E

Eric Sosman

regis said:
Hello,

Suppose that some type of callback function is declared
to take a pointer to some struct as one of its argument.

Is it legal to implement the callback functions as
taking a void* for this argument.

No. (Usually the question is asked the other way
around, but the answer is still No.)

Reason: If you call a function via a function pointer
whose type does not match that of the called function,
the behavior is undefined.

Rationale: The caller builds an argument list that
matches the caller's idea of what the called function
expects to receive. If the called function expects one
thing and the caller has set things up differently, it's
anybody's guess what might happen.

Practicality: On many machines it will turn out that
"all pointers smell the same," meaning that they have the
same representation and are passed to functions by the
same conventions. So you're likely to get away with it --
until, in the words of the Sixth Commandment, "the day
before thy Thesis Oral or thy Big Pitch To The Client."
[...]
(The context is that I am sometimes fed up
with all those casts in gtk and I wonder if it is
legal to replace GtkWidget* arguments of callback functions
by void* directly in the signature of the callbacks)

I'm not familiar with GTK, but this seems backwards:
Something in GTK passes one of your functions a GtkWidget*,
and you want to transform this to a void*? That is, you
want to throw away the information about the type of the
thing it points to? Sounds bizarre: More commonly, one
receives a void* from a type-ignorant caller, and then
converts it to a Something* to add type information. It's
not so usual to want to destroy type information, so I sort
of wonder what you're up to ...
 
R

regis

Eric said:
No. (Usually the question is asked the other way
around, but the answer is still No.)

Reason: If you call a function via a function pointer
whose type does not match that of the called function,
the behavior is undefined.

Rationale: The caller builds an argument list that
matches the caller's idea of what the called function
expects to receive. If the called function expects one
thing and the caller has set things up differently, it's
anybody's guess what might happen.

Practicality: On many machines it will turn out that
"all pointers smell the same," meaning that they have the
same representation and are passed to functions by the
same conventions. So you're likely to get away with it --
until, in the words of the Sixth Commandment, "the day
before thy Thesis Oral or thy Big Pitch To The Client."

ok, thank you.
I'm not familiar with GTK, but this seems backwards:
Something in GTK passes one of your functions a GtkWidget*,
and you want to transform this to a void*? That is, you
want to throw away the information about the type of the
thing it points to? Sounds bizarre: More commonly, one
receives a void* from a type-ignorant caller, and then
converts it to a Something* to add type information. It's
not so usual to want to destroy type information, so I sort
of wonder what you're up to ...

I wanted to avoid to constantly cast pointers
to match the signature of the functions called inside the callback:

for example, given...

typedef struct { ... } Foo1;
typedef struct { Foo1 foo1; ...} Foo2;
typedef struct { Foo2 foo2; ...} Foo3;
typedef void (* Callback2) (Foo2*, void*)

void some_function1 (Foo1 * f);
void some_function2 (Foo2 * f);
void some_function3 (Foo3 * f);

.... a callback will typically have to downcast or upcast
its argument to functions called inside it:

void some_callback2 (Foo2 * f, void * data) {
some_function2 ( f);
some_function1 ((Foo1*) f); /* upcast needed */
some_function3 ((Foo3*) f); /* downcast needed */
}

.....So, the first step was to re-write it as:

void some_callback2 (Foo2 * f, void * data) {
void * v= f;
some_function2 (v);
some_function1 (v); /* no upcast needed */
some_function3 (v); /* no downcast needed */
}

.... and I wondered if I could re-re-write it directly as:

void callback (void * f, void * data) {
some_function2 (f);
some_function1 (f);
some_function3 (f);
}

.... but obviously, I can't do this last step,
because your answer was that it was a constraint violation.
 
E

Eric Sosman

regis said:
[...]

I wanted to avoid to constantly cast pointers
to match the signature of the functions called inside the callback:

for example, given...

typedef struct { ... } Foo1;
typedef struct { Foo1 foo1; ...} Foo2;
typedef struct { Foo2 foo2; ...} Foo3;
typedef void (* Callback2) (Foo2*, void*)

void some_function1 (Foo1 * f);
void some_function2 (Foo2 * f);
void some_function3 (Foo3 * f);

... a callback will typically have to downcast or upcast
its argument to functions called inside it:

void some_callback2 (Foo2 * f, void * data) {
some_function2 ( f);
some_function1 ((Foo1*) f); /* upcast needed */
some_function3 ((Foo3*) f); /* downcast needed */
}

What does the caller of some_callback2() provide? If
it provides a pointer to an actual Foo2 instance (which
seems likely, given the parameter list), then the call to
some_function3() is suspect: How do you know that the Foo2
is actually embedded in a Foo3? If you *do* know it, why
doesn't some_callback2() take a Foo3* argument?

In any event, there's an alternative for one of your
internal calls:

some_function1 (&f->foo1);

.... which might be just a tiny bit safer. And if you happen
to know (by some extra-linguistic means) that the argument
really *is* a Foo3*, I'd suggest

void some_callback(Foo2 *f, void *data) {
Foo3 *fp = (Foo3)f;
some_function2 (&fp->foo2);
some_function1 (&fp->foo2.foo1);
some_function3 (fp);
}

.... as an alternative to casting, an alternative that retains
as much type-safety as one can hope for.
....So, the first step was to re-write it as:

void some_callback2 (Foo2 * f, void * data) {
void * v= f;
some_function2 (v);
some_function1 (v); /* no upcast needed */
some_function3 (v); /* no downcast needed */
}

You've eliminated the casts, but you've also eliminated
all the type-checking. That strikes me as a bad idea: you
are interfering with the compiler's ability to be helpful.

Long years ago I learned it was a Good Thing to tell the
compiler (of any language) as much as possible about your
program. The compiler at hand may or may not be able to make
effective use of all the information, but the next release or
the compiler on the next machine might do a better job of
optimizing, or catch an error that eluded the first one. It
seems to me that withholding information from the compiler --
concealing information, in fact -- is a step in the wrong
direction.
 
R

regis

Eric said:
regis said:
[...]

I wanted to avoid to constantly cast pointers
to match the signature of the functions called inside the callback:

for example, given...

typedef struct { ... } Foo1;
typedef struct { Foo1 foo1; ...} Foo2;
typedef struct { Foo2 foo2; ...} Foo3;
typedef void (* Callback2) (Foo2*, void*)

void some_function1 (Foo1 * f);
void some_function2 (Foo2 * f);
void some_function3 (Foo3 * f);

... a callback will typically have to downcast or upcast
its argument to functions called inside it:

void some_callback2 (Foo2 * f, void * data) {
some_function2 ( f);
some_function1 ((Foo1*) f); /* upcast needed */
some_function3 ((Foo3*) f); /* downcast needed */
}

What does the caller of some_callback2() provide? If
it provides a pointer to an actual Foo2 instance (which
seems likely, given the parameter list), then the call to
some_function3() is suspect: How do you know that the Foo2
is actually embedded in a Foo3? If you *do* know it, why
doesn't some_callback2() take a Foo3* argument?

- A Foo3 is provided.
- The caller of the callback is the API
which will cast the Foo3* as a Foo2* for the call.
- The writer of the callback knows a Foo3 is provided
because the provider and the writer of the callback are the
same person.
- the signature of some_callback2() is given, so you cannot
change it (in fact, as Gtk is concerned, the signature
is void (* GCallback)(void) but let's forget the initial
context that inspired the question...)
In any event, there's an alternative for one of your
internal calls:
some_function1 (&f->foo1);
... which might be just a tiny bit safer. And if you happen
to know (by some extra-linguistic means) that the argument
really *is* a Foo3*, I'd suggest

void some_callback(Foo2 *f, void *data) {
Foo3 *fp = (Foo3)f;
some_function2 (&fp->foo2);
some_function1 (&fp->foo2.foo1);
some_function3 (fp);
}

aestetically worse than cast:
- the struct embeded as a first field for simulating
a base class is a technicality you don't want to deal with.
- you have to know that foo3 embeds a foo2
under this name and not another.
- the chain of inheritance may be long.
... as an alternative to casting, an alternative that retains
as much type-safety as one can hope for.


You've eliminated the casts, but you've also eliminated
all the type-checking. That strikes me as a bad idea: you
are interfering with the compiler's ability to be helpful.

most of some_functionx() are API functions, and when they are
are not, they are functions that in turn call API functions,
which all check their pointers at run-time and verbosely
warn on stderr when a mismatch occurs, so this is not a problem.
Long years ago I learned it was a Good Thing to tell the
compiler (of any language) as much as possible about your
program. The compiler at hand may or may not be able to make
effective use of all the information, but the next release or
the compiler on the next machine might do a better job of
optimizing, or catch an error that eluded the first one. It
seems to me that withholding information from the compiler --
concealing information, in fact -- is a step in the wrong
direction.

Oh, I know it is not recommended.
 
R

regis

Eric said:
What does the caller of some_callback2() provide? If
it provides a pointer to an actual Foo2 instance (which
seems likely, given the parameter list), then the call to
some_function3() is suspect: How do you know that the Foo2
is actually embedded in a Foo3? If you *do* know it, why
doesn't some_callback2() take a Foo3* argument?

Downcasts and Upcasts arise all the time:
for example, you have a GtkEntry
(a widget to edit a single line of text)
and you want to print to stdout its contents
whenever the user hits the 'return' key.

You have to set a callback for the signal "validate".
Gtk wants most callback to be written with signature
void OnValidate (GtkWidget * widget, void * data);
You bind the callback with:
void g_signal_connect (GObject* obj, char * sig,
GCallback cb, void * data);
but with GCallback defined as:
typedef void (* GCallback) (void);

So you will have to do:

1)
GtkWidget * entry= gtk_entry_new ();
g_signal_connect (G_OBJECT(entry), "validate",
G_CALLBACK(OnValidate), NULL);
/* here: upcast from GtkEntry to GObject ... */

2)
void OnValidate (GtkWidget * entry, void * data) {
printf ("%s\n", gtk_entry_get_text (GTK_ENTRY (entry));
/* here downcast from GtkWidget to GtkEntry */
}
 
R

Richard Bos

regis said:
I wanted to avoid to constantly cast pointers
to match the signature of the functions called inside the callback:

for example, given...

typedef struct { ... } Foo1;
typedef struct { Foo1 foo1; ...} Foo2;
typedef struct { Foo2 foo2; ...} Foo3;
typedef void (* Callback2) (Foo2*, void*)

void some_function1 (Foo1 * f);
void some_function2 (Foo2 * f);
void some_function3 (Foo3 * f);

... a callback will typically have to downcast or upcast
its argument to functions called inside it:

void some_callback2 (Foo2 * f, void * data) {
some_function2 ( f);
some_function1 ((Foo1*) f); /* upcast needed */
some_function3 ((Foo3*) f); /* downcast needed */
}

....So, the first step was to re-write it as:

You're doing iffy and unusual things to pointers. Don't try to hide
that; just use the cast. This is what casts are good for.

Either that, or try to rewrite the code to use a union instead.

Richard
 
D

David Thompson

No. (Usually the question is asked the other way
around, but the answer is still No.)
Right.

Reason: If you call a function via a function pointer
whose type does not match that of the called function,
the behavior is undefined.

Rationale: The caller builds an argument list that
matches the caller's idea of what the called function
expects to receive. If the called function expects one
thing and the caller has set things up differently, it's
anybody's guess what might happen.
I've actually used a system where void* and struct foo* had a
different representation. Passing one where the other was expected and
needed caused either a crash or access to totally wrong data.
Practicality: On many machines it will turn out that
"all pointers smell the same," meaning that they have the
same representation and are passed to functions by the
same conventions. So you're likely to get away with it --
until, in the words of the Sixth Commandment, "the day
before thy Thesis Oral or thy Big Pitch To The Client."
However, C does require that all _struct_ pointers have the same
representation. If you have routines using <G> a whole bunch of twisty
little struct types all nearly alike </> you can get away with just
using say 'struct anything *' in the signatures/pointers/etc.

I don't know whether that's enough for the OP's particular case.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
R

regis

However, C does require that all _struct_ pointers have the same
representation. If you have routines using <G> a whole bunch of twisty
little struct types all nearly alike </> you can get away with just
using say 'struct anything *' in the signatures/pointers/etc.

I don't know whether that's enough for the OP's particular case.

good to know.
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top