Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #60946 > unrolled thread

Casting function pointers to different signatures

Started bysamuelallenalexander@gmail.com
First post2015-04-23 13:39 -0700
Last post2015-04-25 10:57 -0400
Articles 19 — 10 participants

Back to article view | Back to comp.lang.c


Contents

  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

#60946 — Casting function pointers to different signatures

Fromsamuelallenalexander@gmail.com
Date2015-04-23 13:39 -0700
SubjectCasting 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]


#60950

FromBarry Schwarz <schwarzb@dqel.com>
Date2015-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]


#60952

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-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]


#60954

Fromsamuelallenalexander@gmail.com
Date2015-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]


#60961

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-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]


#60962

Fromsamuelallenalexander@gmail.com
Date2015-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]


#60966

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-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]


#61025

Fromsamuelallenalexander@gmail.com
Date2015-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]


#61029

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-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]


#61033

Fromsamuelallenalexander@gmail.com
Date2015-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]


#61035

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-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]


#61037

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2015-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]


#60964

FromSiri Cruz <chine.bleu@yahoo.com>
Date2015-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]


#60968

FromKeith Thompson <kst-u@mib.org>
Date2015-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]


#60969

FromSiri Cruz <chine.bleu@yahoo.com>
Date2015-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]


#61069

Fromraltbos@xs4all.nl (Richard Bos)
Date2015-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]


#61071

FromTim Rentsch <txr@alumni.caltech.edu>
Date2015-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]


#60971

FromLőrinczy Zsigmond <nospam@for.me>
Date2015-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]


#61086

FromRichard Damon <Richard@Damon-Family.org>
Date2015-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