Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #60946 > unrolled thread
| Started by | samuelallenalexander@gmail.com |
|---|---|
| First post | 2015-04-23 13:39 -0700 |
| Last post | 2015-04-25 10:57 -0400 |
| Articles | 19 — 10 participants |
Back to article view | Back to comp.lang.c
Casting function pointers to different signatures samuelallenalexander@gmail.com - 2015-04-23 13:39 -0700
Re: Casting function pointers to different signatures Barry Schwarz <schwarzb@dqel.com> - 2015-04-23 14:01 -0700
Re: Casting function pointers to different signatures James Kuyper <jameskuyper@verizon.net> - 2015-04-23 17:10 -0400
Re: Casting function pointers to different signatures samuelallenalexander@gmail.com - 2015-04-23 14:39 -0700
Re: Casting function pointers to different signatures James Kuyper <jameskuyper@verizon.net> - 2015-04-23 18:04 -0400
Re: Casting function pointers to different signatures samuelallenalexander@gmail.com - 2015-04-23 15:49 -0700
Re: Casting function pointers to different signatures James Kuyper <jameskuyper@verizon.net> - 2015-04-23 19:22 -0400
Re: Casting function pointers to different signatures samuelallenalexander@gmail.com - 2015-04-24 10:42 -0700
Re: Casting function pointers to different signatures James Kuyper <jameskuyper@verizon.net> - 2015-04-24 14:18 -0400
Re: Casting function pointers to different signatures samuelallenalexander@gmail.com - 2015-04-24 12:36 -0700
Re: Casting function pointers to different signatures James Kuyper <jameskuyper@verizon.net> - 2015-04-24 15:50 -0400
Re: Casting function pointers to different signatures Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-04-24 13:18 -0700
Re: Casting function pointers to different signatures Siri Cruz <chine.bleu@yahoo.com> - 2015-04-23 16:08 -0700
Re: Casting function pointers to different signatures Keith Thompson <kst-u@mib.org> - 2015-04-23 18:36 -0700
Re: Casting function pointers to different signatures Siri Cruz <chine.bleu@yahoo.com> - 2015-04-23 19:00 -0700
Re: Casting function pointers to different signatures raltbos@xs4all.nl (Richard Bos) - 2015-04-25 10:48 +0000
Re: Casting function pointers to different signatures Tim Rentsch <txr@alumni.caltech.edu> - 2015-04-25 04:05 -0700
Re: Casting function pointers to different signatures Lőrinczy Zsigmond <nospam@for.me> - 2015-04-24 06:51 +0200
Re: Casting function pointers to different signatures Richard Damon <Richard@Damon-Family.org> - 2015-04-25 10:57 -0400
| From | samuelallenalexander@gmail.com |
|---|---|
| Date | 2015-04-23 13:39 -0700 |
| Subject | Casting function pointers to different signatures |
| Message-ID | <9938e88b-965b-4d07-8e86-62b3afa07849@googlegroups.com> |
Suppose I have a generic array-printing function like this,
void print_array( void (*printer) (void *), void **array )
{
for ( ; *array; array++ )
(*printer)(*array);
}
And suppose I have a "print-with-brackets" function like this,
void print_with_brackets( char *x )
{
printf( "[%s]\n", x );
}
Is it legal/defined-behavior if I cast print_with_brackets to void (*) (void*) and pass it as the first argument to print_array, e.g. as in the following?
void print_abc( void )
{
char **buf = malloc(sizeof(char*)*4);
buf[0] = "a", buf[1] = "b", buf[2] = "c", buf[3] = NULL;
print_array( (void (*) (void*))print_with_brackets, (void**)buf );
}
Regards,
Sam
[toc] | [next] | [standalone]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2015-04-23 14:01 -0700 |
| Message-ID | <9dmijalf54cop2pfb79k6pckabt6v4e06u@4ax.com> |
| In reply to | #60946 |
On Thu, 23 Apr 2015 13:39:41 -0700 (PDT),
samuelallenalexander@gmail.com wrote:
>Suppose I have a generic array-printing function like this,
>
>void print_array( void (*printer) (void *), void **array )
>{
> for ( ; *array; array++ )
> (*printer)(*array);
>}
>
>And suppose I have a "print-with-brackets" function like this,
>
>void print_with_brackets( char *x )
>{
> printf( "[%s]\n", x );
>}
>
>Is it legal/defined-behavior if I cast print_with_brackets to void (*) (void*) and pass it as the first argument to print_array, e.g. as in the following?
>
>void print_abc( void )
>{
> char **buf = malloc(sizeof(char*)*4);
> buf[0] = "a", buf[1] = "b", buf[2] = "c", buf[3] = NULL;
> print_array( (void (*) (void*))print_with_brackets, (void**)buf );
>}
While it is a requirement that char* and void* have the same
representation and alignment, the same is not true for void** and
char**.
Furthermore, there is no requirement that a void* and a char* be
passed to a function the same way. print_with_brackets may be
expecting to receive the pointer to char x in register Q while
print_array may be passing the pointer to void *array in register H.
--
Remove del for email
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-04-23 17:10 -0400 |
| Message-ID | <55395FD4.90707@verizon.net> |
| In reply to | #60946 |
On 04/23/2015 04:39 PM, samuelallenalexander@gmail.com wrote:
> Suppose I have a generic array-printing function like this,
>
> void print_array( void (*printer) (void *), void **array )
> {
> for ( ; *array; array++ )
> (*printer)(*array);
> }
>
> And suppose I have a "print-with-brackets" function like this,
>
> void print_with_brackets( char *x )
> {
> printf( "[%s]\n", x );
> }
>
> Is it legal/defined-behavior if I cast print_with_brackets to void (*) (void*) and pass it as the first argument to print_array, e.g. as in the following?
>
> void print_abc( void )
> {
> char **buf = malloc(sizeof(char*)*4);
> buf[0] = "a", buf[1] = "b", buf[2] = "c", buf[3] = NULL;
> print_array( (void (*) (void*))print_with_brackets, (void**)buf );
> }
>
"A pointer to a function of one type may be converted to a pointer to a
function of another type and back again; the result shall compare equal
to the original pointer. If a converted pointer is used to call a
function whose type is not compatible with the referenced type, the
behavior is undefined." (6.3.2.3p8)
Passing it as an argument to print_array() is not a problem. However,
calling it inside print_array() through a pointer that a function type
that is incompatible with the actual type of print_with_brackets() has
undefined behavior. "void" is not compatible with "char", so "void*" is
not compatible with "char*"= (6.7.6.1p2), so void (*)(void*) is not
compatible with void (*)(char*) (6.7.6.3p15).
Note that buf in print_abc has the type char**, while array in
print_array() has the type void**. Those are incompatible types, too.
Now, void* and char* are required to have the same representation and
same alignment requirement (6.2.5p28). Footnote 47 notes that "The same
representation and alignment requirements are meant to imply
interchangeability as arguments to functions, return values from
functions, and members of unions.". However, that's merely a
non-normative suggestion. Nothing in the standard actually requires that
the types be interchangeable. They could, for example, be passed between
function using different registers.
As a practical matter, you can generally count on void* and char* to be
interchangeable - but I think you'd be doing better to make sure that
that the declared type for print_with_brackets() actually matches the
declared type for *printer in print_array(), and similarly for buf in
print_abc() and array in print_array(). Do the conversion from void* to
char* somewhere else (such as in the body of print_with_brackets()).
[toc] | [prev] | [next] | [standalone]
| From | samuelallenalexander@gmail.com |
|---|---|
| Date | 2015-04-23 14:39 -0700 |
| Message-ID | <ed979da3-069e-4ccb-a99d-951a7fa34ae2@googlegroups.com> |
| In reply to | #60952 |
On Thursday, April 23, 2015 at 10:10:51 PM UTC+1, James Kuyper wrote:
> On 04/23/2015 04:39 PM, Sam wrote:
> > Suppose I have a generic array-printing function like this,
> >
> > void print_array( void (*printer) (void *), void **array )
> > {
> > for ( ; *array; array++ )
> > (*printer)(*array);
> > }
> >
> > And suppose I have a "print-with-brackets" function like this,
> >
> > void print_with_brackets( char *x )
> > {
> > printf( "[%s]\n", x );
> > }
> >
> > Is it legal/defined-behavior if I cast print_with_brackets to void (*) (void*) and pass it as the first argument to print_array, e.g. as in the following?
> >
> > void print_abc( void )
> > {
> > char **buf = malloc(sizeof(char*)*4);
> > buf[0] = "a", buf[1] = "b", buf[2] = "c", buf[3] = NULL;
> > print_array( (void (*) (void*))print_with_brackets, (void**)buf );
> > }
> >
>
> "A pointer to a function of one type may be converted to a pointer to a
> function of another type and back again; the result shall compare equal
> to the original pointer. If a converted pointer is used to call a
> function whose type is not compatible with the referenced type, the
> behavior is undefined." (6.3.2.3p8)
>
> Passing it as an argument to print_array() is not a problem. However,
> calling it inside print_array() through a pointer that a function type
> that is incompatible with the actual type of print_with_brackets() has
> undefined behavior. "void" is not compatible with "char", so "void*" is
> not compatible with "char*"= (6.7.6.1p2), so void (*)(void*) is not
> compatible with void (*)(char*) (6.7.6.3p15).
>
> Note that buf in print_abc has the type char**, while array in
> print_array() has the type void**. Those are incompatible types, too.
>
> Now, void* and char* are required to have the same representation and
> same alignment requirement (6.2.5p28). Footnote 47 notes that "The same
> representation and alignment requirements are meant to imply
> interchangeability as arguments to functions, return values from
> functions, and members of unions.". However, that's merely a
> non-normative suggestion. Nothing in the standard actually requires that
> the types be interchangeable. They could, for example, be passed between
> function using different registers.
>
> As a practical matter, you can generally count on void* and char* to be
> interchangeable - but I think you'd be doing better to make sure that
> that the declared type for print_with_brackets() actually matches the
> declared type for *printer in print_array(), and similarly for buf in
> print_abc() and array in print_array(). Do the conversion from void* to
> char* somewhere else (such as in the body of print_with_brackets()).
Thanks for the very detailed answer with references and everything.
It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C.
-Sam
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-04-23 18:04 -0400 |
| Message-ID | <55396C7E.3070403@verizon.net> |
| In reply to | #60954 |
On 04/23/2015 05:39 PM, samuelallenalexander@gmail.com wrote: > On Thursday, April 23, 2015 at 10:10:51 PM UTC+1, James Kuyper wrote: >> On 04/23/2015 04:39 PM, Sam wrote: ... >> "A pointer to a function of one type may be converted to a pointer to a >> function of another type and back again; the result shall compare equal >> to the original pointer. If a converted pointer is used to call a >> function whose type is not compatible with the referenced type, the >> behavior is undefined." (6.3.2.3p8) >> >> Passing it as an argument to print_array() is not a problem. However, >> calling it inside print_array() through a pointer that a function type >> that is incompatible with the actual type of print_with_brackets() has >> undefined behavior. "void" is not compatible with "char", so "void*" is >> not compatible with "char*"= (6.7.6.1p2), so void (*)(void*) is not >> compatible with void (*)(char*) (6.7.6.3p15). >> >> Note that buf in print_abc has the type char**, while array in >> print_array() has the type void**. Those are incompatible types, too. >> >> Now, void* and char* are required to have the same representation and >> same alignment requirement (6.2.5p28). Footnote 47 notes that "The same >> representation and alignment requirements are meant to imply >> interchangeability as arguments to functions, return values from >> functions, and members of unions.". However, that's merely a >> non-normative suggestion. Nothing in the standard actually requires that >> the types be interchangeable. They could, for example, be passed between >> function using different registers. >> >> As a practical matter, you can generally count on void* and char* to be >> interchangeable - but I think you'd be doing better to make sure that >> that the declared type for print_with_brackets() actually matches the >> declared type for *printer in print_array(), and similarly for buf in >> print_abc() and array in print_array(). Do the conversion from void* to >> char* somewhere else (such as in the body of print_with_brackets()). > > Thanks for the very detailed answer with references and everything. > > It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C. How is the door closed? It's trivial to make the function types compatible, and if you do, the code will work. Could you give an example of code using one of those idioms where it's excessively difficult to avoid the problem? Your current example does NOT qualify.
[toc] | [prev] | [next] | [standalone]
| From | samuelallenalexander@gmail.com |
|---|---|
| Date | 2015-04-23 15:49 -0700 |
| Message-ID | <6ac6f8b2-cf77-4112-94ac-f5f0d8d82060@googlegroups.com> |
| In reply to | #60961 |
On Thursday, April 23, 2015 at 11:04:53 PM UTC+1, James Kuyper wrote:
> On 04/23/2015 05:39 PM, Sam wrote:
> > On Thursday, April 23, 2015 at 10:10:51 PM UTC+1, James Kuyper wrote:
> >> On 04/23/2015 04:39 PM, Sam wrote:
> ...
> >> "A pointer to a function of one type may be converted to a pointer to a
> >> function of another type and back again; the result shall compare equal
> >> to the original pointer. If a converted pointer is used to call a
> >> function whose type is not compatible with the referenced type, the
> >> behavior is undefined." (6.3.2.3p8)
> >>
> >> Passing it as an argument to print_array() is not a problem. However,
> >> calling it inside print_array() through a pointer that a function type
> >> that is incompatible with the actual type of print_with_brackets() has
> >> undefined behavior. "void" is not compatible with "char", so "void*" is
> >> not compatible with "char*"= (6.7.6.1p2), so void (*)(void*) is not
> >> compatible with void (*)(char*) (6.7.6.3p15).
> >>
> >> Note that buf in print_abc has the type char**, while array in
> >> print_array() has the type void**. Those are incompatible types, too.
> >>
> >> Now, void* and char* are required to have the same representation and
> >> same alignment requirement (6.2.5p28). Footnote 47 notes that "The same
> >> representation and alignment requirements are meant to imply
> >> interchangeability as arguments to functions, return values from
> >> functions, and members of unions.". However, that's merely a
> >> non-normative suggestion. Nothing in the standard actually requires that
> >> the types be interchangeable. They could, for example, be passed between
> >> function using different registers.
> >>
> >> As a practical matter, you can generally count on void* and char* to be
> >> interchangeable - but I think you'd be doing better to make sure that
> >> that the declared type for print_with_brackets() actually matches the
> >> declared type for *printer in print_array(), and similarly for buf in
> >> print_abc() and array in print_array(). Do the conversion from void* to
> >> char* somewhere else (such as in the body of print_with_brackets()).
> >
> > Thanks for the very detailed answer with references and everything.
> >
> > It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C.
>
> How is the door closed? It's trivial to make the function types
> compatible, and if you do, the code will work. Could you give an example
> of code using one of those idioms where it's excessively difficult to
> avoid the problem? Your current example does NOT qualify.
Yes, you could make the individual printer functions have signature void (*) (void*), but that makes those individual printer functions a lot more opaque on their own, and it's very repetitive that you have to repeat the casts over and over.
By contrast, if my OP was defined, it would be easy to hide the casts with macros, e.g.,
#define PRINT_ARRAY(fnc,buf) do{print_array((void (*) (void*))(fnc),(void**)(buf));}while(0)
and then the interface between C and OOP/FP would be almost totally seamless.
-Sam
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-04-23 19:22 -0400 |
| Message-ID | <55397EAE.1040009@verizon.net> |
| In reply to | #60962 |
On 04/23/2015 06:49 PM, samuelallenalexander@gmail.com wrote: > On Thursday, April 23, 2015 at 11:04:53 PM UTC+1, James Kuyper wrote: >> On 04/23/2015 05:39 PM, Sam wrote: ... >>> Thanks for the very detailed answer with references and everything. >>> >>> It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C. >> >> How is the door closed? It's trivial to make the function types >> compatible, and if you do, the code will work. Could you give an example >> of code using one of those idioms where it's excessively difficult to >> avoid the problem? Your current example does NOT qualify. > > Yes, you could make the individual printer functions have signature > void (*) (void*), but that makes those individual printer functions a > lot more opaque on their own, and it's very repetitive that you have > to repeat the casts over and over. That comment suggests a possible misunderstanding. If the function types were, in fact, compatible, there would be no need for a cast, the function would already have the correct type.
[toc] | [prev] | [next] | [standalone]
| From | samuelallenalexander@gmail.com |
|---|---|
| Date | 2015-04-24 10:42 -0700 |
| Message-ID | <fab5222e-49e5-4458-a5d0-4fa69c4c6d14@googlegroups.com> |
| In reply to | #60966 |
On Friday, April 24, 2015 at 12:22:30 AM UTC+1, James Kuyper wrote:
> On 04/23/2015 06:49 PM, Sam wrote:
> > On Thursday, April 23, 2015 at 11:04:53 PM UTC+1, James Kuyper wrote:
> >> On 04/23/2015 05:39 PM, Sam wrote:
> ...
> >>> Thanks for the very detailed answer with references and everything.
> >>>
> >>> It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C.
> >>
> >> How is the door closed? It's trivial to make the function types
> >> compatible, and if you do, the code will work. Could you give an example
> >> of code using one of those idioms where it's excessively difficult to
> >> avoid the problem? Your current example does NOT qualify.
> >
> > Yes, you could make the individual printer functions have signature
> > void (*) (void*), but that makes those individual printer functions a
> > lot more opaque on their own, and it's very repetitive that you have
> > to repeat the casts over and over.
>
> That comment suggests a possible misunderstanding. If the function types
> were, in fact, compatible, there would be no need for a cast, the
> function would already have the correct type.
Suppose I have structures of various sizes, and functions to print them. For example,
struct an_int
{
int x;
};
struct two_ints
{
int x;
iny y;
};
void print_an_int( struct an_int *p )
{
printf( "%d\n", p->x );
}
void print_two_ints( struct two_ints *p )
{
printf( "%d %d\n", p->x, p->y );
}
If buf is a NULL-terminated array of struct an_int's, I'd like to be able to do
print_array( print_an_int, buf );
If buf is a NULL-terminated array of struct two_ints's, I'd like to be able to do
print_array( print_two_ints, buf );
Is there a way to do this without changing print_an_int or print_two_ints themselves?
Regards,
Sam
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-04-24 14:18 -0400 |
| Message-ID | <553A8909.2060901@verizon.net> |
| In reply to | #61025 |
On 04/24/2015 01:42 PM, samuelallenalexander@gmail.com wrote:
> On Friday, April 24, 2015 at 12:22:30 AM UTC+1, James Kuyper wrote:
>> On 04/23/2015 06:49 PM, Sam wrote:
>>> On Thursday, April 23, 2015 at 11:04:53 PM UTC+1, James Kuyper wrote:
>>>> On 04/23/2015 05:39 PM, Sam wrote:
>> ...
>>>>> Thanks for the very detailed answer with references and everything.
>>>>>
>>>>> It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C.
>>>>
>>>> How is the door closed? It's trivial to make the function types
>>>> compatible, and if you do, the code will work. Could you give an example
>>>> of code using one of those idioms where it's excessively difficult to
>>>> avoid the problem? Your current example does NOT qualify.
>>>
>>> Yes, you could make the individual printer functions have signature
>>> void (*) (void*), but that makes those individual printer functions a
>>> lot more opaque on their own, and it's very repetitive that you have
>>> to repeat the casts over and over.
>>
>> That comment suggests a possible misunderstanding. If the function types
>> were, in fact, compatible, there would be no need for a cast, the
>> function would already have the correct type.
>
> Suppose I have structures of various sizes, and functions to print them. For example,
>
> struct an_int
> {
> int x;
> };
>
> struct two_ints
> {
> int x;
> iny y;
> };
>
> void print_an_int( struct an_int *p )
> {
> printf( "%d\n", p->x );
> }
>
> void print_two_ints( struct two_ints *p )
> {
> printf( "%d %d\n", p->x, p->y );
> }
>
> If buf is a NULL-terminated array of struct an_int's, I'd like to be able to do
>
> print_array( print_an_int, buf );
>
> If buf is a NULL-terminated array of struct two_ints's, I'd like to be able to do
>
> print_array( print_two_ints, buf );
>
>
> Is there a way to do this without changing print_an_int or print_two_ints themselves?
No - my suggestion involved changing the corresponding functions:
void print_an_int(void *pv)
{
struct an_int *p = pv;
...
}
If there's a reason why you can't do that, you'll need to explain that
reason. If it's a valid reason, we might be able to suggest an
alternative approach that will work.
Note that in contexts like these, it's more typical for object-oriented
C code to access the appropriate function through a pointer stored with
the data it's designed to work on, so it's easier to make sure that it's
the correct function. Implementations of C++ based on C typically use a
vtable pointer for this purpose, though there are other ways to do it.
If you're familiar with the concept, I recommend considering using it in
this context. If you're unfamiliar with the concept, I can explain further.
[toc] | [prev] | [next] | [standalone]
| From | samuelallenalexander@gmail.com |
|---|---|
| Date | 2015-04-24 12:36 -0700 |
| Message-ID | <8e2b93dc-cbbe-415d-bdaf-2463bc2aa45d@googlegroups.com> |
| In reply to | #61029 |
On Friday, April 24, 2015 at 7:18:58 PM UTC+1, James Kuyper wrote:
> On 04/24/2015 01:42 PM, Sam wrote:
> > On Friday, April 24, 2015 at 12:22:30 AM UTC+1, James Kuyper wrote:
> >> On 04/23/2015 06:49 PM, Sam wrote:
> >>> On Thursday, April 23, 2015 at 11:04:53 PM UTC+1, James Kuyper wrote:
> >>>> On 04/23/2015 05:39 PM, Sam wrote:
> >> ...
> >>>>> Thanks for the very detailed answer with references and everything.
> >>>>>
> >>>>> It's really too bad the behavior is undefined since it would open the door for porting some genuinely useful OOP/FP idioms into C.
> >>>>
> >>>> How is the door closed? It's trivial to make the function types
> >>>> compatible, and if you do, the code will work. Could you give an example
> >>>> of code using one of those idioms where it's excessively difficult to
> >>>> avoid the problem? Your current example does NOT qualify.
> >>>
> >>> Yes, you could make the individual printer functions have signature
> >>> void (*) (void*), but that makes those individual printer functions a
> >>> lot more opaque on their own, and it's very repetitive that you have
> >>> to repeat the casts over and over.
> >>
> >> That comment suggests a possible misunderstanding. If the function types
> >> were, in fact, compatible, there would be no need for a cast, the
> >> function would already have the correct type.
> >
> > Suppose I have structures of various sizes, and functions to print them. For example,
> >
> > struct an_int
> > {
> > int x;
> > };
> >
> > struct two_ints
> > {
> > int x;
> > iny y;
> > };
> >
> > void print_an_int( struct an_int *p )
> > {
> > printf( "%d\n", p->x );
> > }
> >
> > void print_two_ints( struct two_ints *p )
> > {
> > printf( "%d %d\n", p->x, p->y );
> > }
> >
> > If buf is a NULL-terminated array of struct an_int's, I'd like to be able to do
> >
> > print_array( print_an_int, buf );
> >
> > If buf is a NULL-terminated array of struct two_ints's, I'd like to be able to do
> >
> > print_array( print_two_ints, buf );
> >
> >
> > Is there a way to do this without changing print_an_int or print_two_ints themselves?
>
> No - my suggestion involved changing the corresponding functions:
>
> void print_an_int(void *pv)
> {
> struct an_int *p = pv;
> ...
> }
>
> If there's a reason why you can't do that, you'll need to explain that
> reason. If it's a valid reason, we might be able to suggest an
> alternative approach that will work.
>
> Note that in contexts like these, it's more typical for object-oriented
> C code to access the appropriate function through a pointer stored with
> the data it's designed to work on, so it's easier to make sure that it's
> the correct function. Implementations of C++ based on C typically use a
> vtable pointer for this purpose, though there are other ways to do it.
> If you're familiar with the concept, I recommend considering using it in
> this context. If you're unfamiliar with the concept, I can explain further.
Thanks kindly. There's no compelling reason not to do what you suggested, I just wish it were unncessary because it makes the 'corresponding functions' more opaque, that's all. With a particular form of opacity that isn't easy to whisk away with the preprocessor. (It also causes unnecessary suppression of compiler warnings, e.g., if a user accidentally passes a struct one_int to print_to_ints, your suggestion will compile without complaint.)
Regards,
Sam
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-04-24 15:50 -0400 |
| Message-ID | <553A9E7E.5020207@verizon.net> |
| In reply to | #61033 |
On 04/24/2015 03:36 PM, samuelallenalexander@gmail.com wrote:
> On Friday, April 24, 2015 at 7:18:58 PM UTC+1, James Kuyper wrote:
...
>> No - my suggestion involved changing the corresponding functions:
>>
>> void print_an_int(void *pv)
>> {
>> struct an_int *p = pv;
>> ...
>> }
>>
>> If there's a reason why you can't do that, you'll need to explain that
>> reason. If it's a valid reason, we might be able to suggest an
>> alternative approach that will work.
>>
>> Note that in contexts like these, it's more typical for object-oriented
>> C code to access the appropriate function through a pointer stored with
>> the data it's designed to work on, so it's easier to make sure that it's
>> the correct function. Implementations of C++ based on C typically use a
>> vtable pointer for this purpose, though there are other ways to do it.
>> If you're familiar with the concept, I recommend considering using it in
>> this context. If you're unfamiliar with the concept, I can explain further.
>
> Thanks kindly. There's no compelling reason not to do what you suggested, I just wish it were unncessary because it makes the 'corresponding functions' more opaque, that's all. With a particular form of opacity that isn't easy to whisk away with the preprocessor. (It also causes unnecessary suppression of compiler warnings, e.g., if a user accidentally passes a struct one_int to print_to_ints, your suggestion will compile without complaint.)
That's a reason why accessing the function through a pointer stored with
the data can be a good idea when writing object-oriented code in C.
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2015-04-24 13:18 -0700 |
| Message-ID | <0714dea8-c7f4-42c7-b730-89d5ee7887ba@googlegroups.com> |
| In reply to | #61033 |
On Friday, April 24, 2015 at 8:36:58 PM UTC+1, samuelalle...@gmail.com wrote: > > Thanks kindly. There's no compelling reason not to do what you > suggested, I just wish it were unncessary because it makes the > 'corresponding functions' more opaque, that's all. With a particular > form of opacity that isn't easy to whisk away with the preprocessor. > (It also causes unnecessary suppression of compiler warnings, e.g., > if a user accidentally passes a struct one_int to print_to_ints, > your suggestion will compile without complaint.) > Language designers often put features in because they're relatively easy to program a compiler to recognise, rather then because they are a major source of errors. If you want write a C function that takes a function pointer, usually you should also take an opaque void * and pass it back, to provide context. Other languages have other ways of doing the same thing, but the C method isn't particularly error-prone. if you cast the void * to a structure of the wrong type, the program is likely to go horribly wrong very quickly, and that's not the sort of bug that usually slips past initial informal testing.
[toc] | [prev] | [next] | [standalone]
| From | Siri Cruz <chine.bleu@yahoo.com> |
|---|---|
| Date | 2015-04-23 16:08 -0700 |
| Message-ID | <chine.bleu-30B535.16080523042015@news.eternal-september.org> |
| In reply to | #60952 |
In article <55395FD4.90707@verizon.net>, James Kuyper <jameskuyper@verizon.net> wrote: > > Is it legal/defined-behavior if I cast print_with_brackets to void (*) > > (void*) and pass it as the first argument to print_array, e.g. as in the > > following? > "A pointer to a function of one type may be converted to a pointer to a > function of another type and back again; the result shall compare equal > to the original pointer. If a converted pointer is used to call a > As a practical matter, you can generally count on void* and char* to be > interchangeable - but I think you'd be doing better to make sure that Legal/defined-behaviour has to be in reference to what standard you're using. C? Posix? Microsoft? Windows 95? I write code that's intended for unices (MacOSX and Linux at the moment), and on unices all pointers, including function and data pointers, are identical. So you write to your intended customer base. -- :-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. 'I desire mercy, not sacrifice.' Icke's razor: Given two equally plausible explanations, choose the weirder. That's People's Commissioner Siri Cruz now. Punch!
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-04-23 18:36 -0700 |
| Message-ID | <874mo6i9yz.fsf@kst-u.example.com> |
| In reply to | #60964 |
Siri Cruz <chine.bleu@yahoo.com> writes:
> In article <55395FD4.90707@verizon.net>, James Kuyper <jameskuyper@verizon.net>
> wrote:
>> > Is it legal/defined-behavior if I cast print_with_brackets to void (*)
>> > (void*) and pass it as the first argument to print_array, e.g. as in the
>> > following?
>
>> "A pointer to a function of one type may be converted to a pointer to a
>> function of another type and back again; the result shall compare equal
>> to the original pointer. If a converted pointer is used to call a
>
>> As a practical matter, you can generally count on void* and char* to be
>> interchangeable - but I think you'd be doing better to make sure that
>
> Legal/defined-behaviour has to be in reference to what standard you're using. C?
> Posix? Microsoft? Windows 95? I write code that's intended for unices (MacOSX
> and Linux at the moment), and on unices all pointers, including function and
> data pointers, are identical.
I don't think POSIX guarantees that, though the definition of dlsym()
nearly nearly implies it.
> So you write to your intended customer base.
I prefer to write to the standard when it's practical. Sometimes
non-portable assumptions can make things easier, but in my experience
that happens less often than some might assume.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Siri Cruz <chine.bleu@yahoo.com> |
|---|---|
| Date | 2015-04-23 19:00 -0700 |
| Message-ID | <chine.bleu-96F416.19004323042015@news.eternal-september.org> |
| In reply to | #60968 |
> > So you write to your intended customer base. > > I prefer to write to the standard when it's practical. Sometimes > non-portable assumptions can make things easier, but in my experience > that happens less often than some might assume. Which makes sense if your customer base is all C implementations. (Oh, and 'customers' doesn't require a commercial transaction; it's everybody you want to use your code.) -- :-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. 'I desire mercy, not sacrifice.' Icke's razor: Given two equally plausible explanations, choose the weirder. That's People's Commissioner Siri Cruz now. Punch!
[toc] | [prev] | [next] | [standalone]
| From | raltbos@xs4all.nl (Richard Bos) |
|---|---|
| Date | 2015-04-25 10:48 +0000 |
| Message-ID | <553b6fe9.11178937@news.xs4all.nl> |
| In reply to | #60969 |
Siri Cruz <chine.bleu@yahoo.com> wrote: [ Please don't snip attributions of contributions you leave in. The following was written by Siri Cruz and Keith Thompson: ] > > > So you write to your intended customer base. > > > > I prefer to write to the standard when it's practical. Sometimes > > non-portable assumptions can make things easier, but in my experience > > that happens less often than some might assume. > > Which makes sense if your customer base is all C implementations. Yes... or if you don't know if your code might in future be useful on another platform. Or even on another, or a future, variant of the platform you're using now - do _you_ know all the differences between LSD, Missed'emV, and Lunix? Richard
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-04-25 04:05 -0700 |
| Message-ID | <kfnr3r8piwl.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #60964 |
Siri Cruz <chine.bleu@yahoo.com> writes: > In article <55395FD4.90707@verizon.net>, James Kuyper > <jameskuyper@verizon.net> wrote: > >>> Is it legal/defined-behavior if I cast print_with_brackets to void >>> (*) (void*) and pass it as the first argument to print_array, >>> e.g. as in the following? >> >> "A pointer to a function of one type may be converted to a pointer >> to a function of another type and back again; the result shall >> compare equal to the original pointer. If a converted pointer is >> used to call a >> >> As a practical matter, you can generally count on void* and char* >> to be interchangeable - but I think you'd be doing better to make >> sure that > > Legal/defined-behaviour has to be in reference to what standard > you're using. C? Posix? Microsoft? Windows 95? I write code > that's intended for unices (MacOSX and Linux at the moment), and > on unices all pointers, including function and data pointers, are > identical. Even if function pointers all have the same representation, that doesn't mean the calling sequences are all the same.
[toc] | [prev] | [next] | [standalone]
| From | Lőrinczy Zsigmond <nospam@for.me> |
|---|---|
| Date | 2015-04-24 06:51 +0200 |
| Message-ID | <mhci42$jmp$1@speranza.aioe.org> |
| In reply to | #60946 |
> Is it legal/defined-behavior if I cast print_with_brackets to void (*) (void*) and pass it as the first argument to print_array, e.g. as in the following?
Yes, but you really should use a typedef:
typedef void my_callback_fun (void *);
void print_array (my_callback_fun, void *array[]);
void print_with_brackets (char *array[]);
{
print_array ((my_callback_fun *)print_with_brackets, (void**)buf);
}
[toc] | [prev] | [next] | [standalone]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2015-04-25 10:57 -0400 |
| Message-ID | <OXN_w.390541$2j2.225130@fx23.iad> |
| In reply to | #60946 |
On 4/23/15 4:39 PM, samuelallenalexander@gmail.com wrote:
> Suppose I have a generic array-printing function like this,
>
> void print_array( void (*printer) (void *), void **array )
> {
> for ( ; *array; array++ )
> (*printer)(*array);
> }
>
> And suppose I have a "print-with-brackets" function like this,
>
> void print_with_brackets( char *x )
> {
> printf( "[%s]\n", x );
> }
>
> Is it legal/defined-behavior if I cast print_with_brackets to void (*) (void*) and pass it as the first argument to print_array, e.g. as in the following?
>
> void print_abc( void )
> {
> char **buf = malloc(sizeof(char*)*4);
> buf[0] = "a", buf[1] = "b", buf[2] = "c", buf[3] = NULL;
> print_array( (void (*) (void*))print_with_brackets, (void**)buf );
> }
>
> Regards,
> Sam
>
The solution is similar to what needs to be done with the callback for
qsort, either write print_with_brackets to take a void* and convert it
to the needed type inside it, or if it is generally useful as is
elsewhere, to create a wrapper that take a void * and use it to pass.
Something like:
void print_with_brackets_wrap( void *x) {
print_with_brackets( x);
}
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web