function returning function pointer (recursive type)

M

Mark Piffer

Hi group,
only today I stumbled over the famous (?) inability of C to define
recursive types for functions.
The task was to define a daisy chain of functions which facilitate
insertion and deletion by
passing and returning function pointers of their very own type. Each
function should hold
a static variable to its' successor. After having unwound my brain
from the infinite recursion
that happened in the first typedef (*)()(*foo)((*)(...etc. I rummaged
the comp.lang.c archives
and found the explanation with a workaround based on structs. I
remember however that
function pointers can be cast into one another and back without
invoking undefined behaviour.
Is this solution any worse than the struct thing? (code not yet
checked for logical errors, but
it compiled ok on Comeau):


typedef int (*mock_func_ptr)();
typedef mock_func_ptr (*daisychain_func_ptr)();

daisychain_func_ptr
me(daisychain_func_ptr new_func)
{
daisychain_func_ptr next;
int call_me_again;

/* do something useful here...*/

if (call_me_again) {
if (!next) {
next = (daisychain_func_ptr)new_func(0);
}
else {
next = (daisychain_func_ptr)next(new_func);
}
return (daisychain_func_ptr)me;
}
else {
if (next) {
daisychain_func_ptr t = next;
next = 0;
return (daisychain_func_ptr)t(new_func);
}
else if (new_func) {
return (daisychain_func_ptr)new_func(0);
}
else {
return 0;
}
}
}
 
M

Mark Piffer

Hi group,
only today I stumbled over the famous (?) inability of C to define
recursive types for functions.
The task was to define a daisy chain of functions which facilitate
insertion and deletion by
passing and returning function pointers of their very own type. Each
function should hold
a static variable to its' successor. After having unwound my brain
from the infinite recursion
that happened in the first typedef (*)()(*foo)((*)(...etc. I rummaged
the comp.lang.c archives
and found the explanation with a workaround based on structs. I
remember however that
function pointers can be cast into one another and back without
invoking undefined behaviour.
Is this solution any worse than the struct thing? (code not yet
checked for logical errors, but
it compiled ok on Comeau):

typedef int (*mock_func_ptr)();
typedef mock_func_ptr (*daisychain_func_ptr)();

daisychain_func_ptr
me(daisychain_func_ptr new_func)
{
   daisychain_func_ptr next;
   int call_me_again;

  /* do something useful here...*/

   if (call_me_again) {
      if (!next) {
         next = (daisychain_func_ptr)new_func(0);
      }
      else {
         next = (daisychain_func_ptr)next(new_func);
      }
      return (daisychain_func_ptr)me;
   }
   else {
      if (next) {
         daisychain_func_ptr t = next;
         next = 0;
         return (daisychain_func_ptr)t(new_func);
      }
      else if (new_func) {
         return (daisychain_func_ptr)new_func(0);
      }
      else {
         return 0;
      }
   }

}

After having some spare minutes to think about it while driving home,
I think I can answer myself: the above IS undefined behaviour because
the function pointers are deref'd with the wrong type. I should have
noticed when I had to cast the "me" in the return statement.
 
L

luserXtrog

After having some spare minutes to think about it while driving home,
I think I can answer myself: the above IS undefined behaviour because
the function pointers are deref'd with the wrong type. I should have
noticed when I had to cast the "me" in the return statement.

I think you can avoid the undefined behavior by using void pointers
as intermediaries. I'm not sure what this is supposed to "do", but
I think the following is equivalent to your example.

HTH!

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

void * me (void *vfunc) {
static void *(*next)(void *) = NULL;
static bool again = false;
void *(*func)(void *) = vfunc;

if (again) {
return next = next?next(func):func(NULL);
} else {
if (next) {
void *(*t)(void *) = next;
next = NULL;
return t(func);
} else if (func) {
return func(NULL);
} else return NULL;
}
}

int main() {
printf("%x\n", me(NULL));
printf("%x\n", me(me));
}
 
N

Nate Eldredge

luserXtrog said:
I think you can avoid the undefined behavior by using void pointers
as intermediaries. I'm not sure what this is supposed to "do", but
I think the following is equivalent to your example.

HTH!

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

void * me (void *vfunc) {
static void *(*next)(void *) = NULL;
static bool again = false;
void *(*func)(void *) = vfunc;

if (again) {
return next = next?next(func):func(NULL);
} else {
if (next) {
void *(*t)(void *) = next;
next = NULL;
return t(func);
} else if (func) {
return func(NULL);
} else return NULL;
}
}

That won't generally work. void * can't be relied on to be able to
store a function pointer.
 
L

luserXtrog

That won't generally work.  void * can't be relied on to be able to
store a function pointer.

The standard appears to disagree.

J.5.7 Function pointer casts
A pointer to an object or to void may be cast to a pointer to a
function, allowing data to
be invoked as a function (6.5.4).
A pointer to a function may be cast to a pointer to an object or to
void, allowing a
function to be inspected or modiï¬ed (for example, by a debugger)
(6.5.4).
 
F

Flash Gordon

luserXtrog said:
The standard appears to disagree.

J.5.7 Function pointer casts
A pointer to an object or to void may be cast to a pointer to a
function, allowing data to
be invoked as a function (6.5.4).
A pointer to a function may be cast to a pointer to an object or to
void, allowing a
function to be inspected or modiï¬ed (for example, by a debugger)
(6.5.4).

Now read the heading of Appendix J. It is "Common Extensions". So the
standard is telling you that this is a common extension and *not*
something specifically allowed.
 
N

Nate Eldredge

luserXtrog said:
The standard appears to disagree.

J.5.7 Function pointer casts
A pointer to an object or to void may be cast to a pointer to a
function, allowing data to
be invoked as a function (6.5.4).
A pointer to a function may be cast to a pointer to an object or to
void, allowing a
function to be inspected or modiï¬ed (for example, by a debugger)
(6.5.4).

Annex J.5 is entitled "Common extensions". These are features that some
implementations provide, but they are not required to do so.

More to the point here is 6.3.2.3 (1). "A pointer to void may be
converted to or from a pointer to any incomplete or object type."
Function types are not included, and AFAIK there is nowhere else in the
body of the Standard that allows converting a function pointer to and
from a void pointer.
 
K

Keith Thompson

luserXtrog said:
The standard appears to disagree.

J.5.7 Function pointer casts
A pointer to an object or to void may be cast to a pointer to a
function, allowing data to
be invoked as a function (6.5.4).
A pointer to a function may be cast to a pointer to an object or to
void, allowing a
function to be inspected or modiï¬ed (for example, by a debugger)
(6.5.4).

Section J.5 is titled "Common extensions.

I'll just assume that "The standard appears to disagree." wasn't meant
to imply that it actually does disagree.
 
L

luserXtrog

Section J.5 is titled "Common extensions.

I'll just assume that "The standard appears to disagree." wasn't meant
to imply that it actually does disagree.

Yes. It was the first thing I found so I rushed off to retort.
The statement was intentionally vague to reflect my lack of
certainty. Correction appreciated.
 

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

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top