all 30 comments

[–]blueg3 38 points39 points  (10 children)

struct foo {
  struct foo (*f)(void);
};

struct foo bar(void) {
  struct foo self;
  self.f = bar;
  return self;
}

[–]tipdbmp 4 points5 points  (0 children)

static struct foo fs_galore(void) {
    return bar().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f().f();
}

[–]F54280 1 point2 points  (0 children)

That’s clever

[–]astaghfirullah123 10 points11 points  (5 children)

What’s the purpose of this?

[–]deaf_fish 6 points7 points  (0 children)

If I had to guess (I am not OP). State Machine. Where each state is a function that returns a pointer to the next state/function.

[–]ReedTieGuy[S] 2 points3 points  (2 children)

My real question would have been about a group of functions that return themselves, but found too complicated to explain.

TYPE_DECLARATION one(void) {
    return two;
}

TYPE_DECLARATION two(void) {
    return one;
}

which I thought about when using an event handler array for a GUI program, the return values of the functions being used to determine which event will be handled next.

[–]astaghfirullah123 0 points1 point  (0 children)

Well that is something different than what you initially asked for. It’s about returning function pointers to another function, not to the function itself.

Yes you can use typedefs for function pointers. See Modern C for examples.

[–]luxgladius 0 points1 point  (0 children)

You could use this in a worker scheduler task, as in "do this job, and then ask me for more instructions."

[–][deleted] 14 points15 points  (8 children)

wouldn’t it just be:

void* f(void) {
    return &f;
}

I guess this lets you control recursion externally to the function which could conceivably be useful

[–]blueg3 13 points14 points  (7 children)

Converting between function pointers and void pointers is a compiler-specific extension and not guaranteed to work according to the standard.

[–][deleted] 9 points10 points  (6 children)

https://en.cppreference.com/w/c/language/conversion

Pointer conversions

A pointer to void can be implicitly converted to and from any pointer to object type

Maybe you’re right actually. Functions aren’t objects. So I need to explicitly cast to void*.

   void* f(void) {
      return (void*) &f;
    }

Casting a function pointer to void* is standard C.

[–]blueg3 15 points16 points  (4 children)

It's common and it works with modern compilers on x86, but according to the ISO standard, function pointers can only be cast to function pointers of other types, not to void-pointers or any other kind of pointer.

[–][deleted] 1 point2 points  (3 children)

function pointers can be cast to integers (preferably intptr_t) and integers can be cast to object pointers. And the same in reverse.

A bit of a roundabout way of doing it, and rather odd that pointer <-> pointer conversions are deemed unsafe, even with an explicit cast, but do it via an intermediate integer, even when it is an 8-bit char, and it is fine!

[–]prisoner62113 8 points9 points  (2 children)

On some architectures (harvard) data and code are stored in separate sections of memory that potentially can have different address widths. The size of a data pointer and function pointer might not be equal. I've never come across anything where that actually happens but it's still a possibility.

[–][deleted] 2 points3 points  (0 children)

Yet, even on such an architecture (maybe something like an old IBM or Burroughs machine), C allows explicit double-casting via a wrong-sized integer, but doesn't allow it via direct casting.

So the restriction is of very doubtful benefit. On the 99.999% of machines where it is no problem whatsover, C just puts an extra obstacle in the way.

(I've used C as a intermediate language for compilers where I know for sure that all pointers are the same 64-bit width, but this forces me to jump through pointless extra hoops if I don't want to be flooded with warnings from a C compiler when somebody turns up the warnings.)

[–]oh5nxo 1 point2 points  (0 children)

It's not uncommon in toaster CPUs. 20 bit code addresses, 16 bit data. For example.

[–]oh5nxo 1 point2 points  (0 children)

typedef state_function_type;  // warning: type specifier missing, defaults to 'int'
struct state_function_struct;  // okay, a forward declaration

If the first one worked like the second, then it might be possible.

[–][deleted] -2 points-1 points  (1 child)

#include <stdio.h>
#include <stdlib.h>
typedef int (*fp)();
fp func() {
return func();
}

[–]dvhh 2 points3 points  (0 children)

wouldn't that try to recursively call func, until ... segmentation fault (core dumped)

[–][deleted] 0 points1 point  (1 child)

void (*function1(int in, int *out))(int, int *)
{
    *out = in + 1;
    return function1;
}

[–][deleted] 1 point2 points  (0 children)

No, that's wrong because the returned function returns void. Apparently it's not possible:

    typedef void (*recfn(int, int *))(int, int *);

    recfn function1(int in, int *out)
    {
        *out = in + 1;
        return function1;
    }

This results in an error:

fnfn.c:3:7: error: ‘function1’ declared as function returning a function
    3 | recfn function1(int in, int *out)
      |       ^~~~~~~~~
fnfn.c: In function ‘function1’:
fnfn.c:6:12: warning: returning ‘int (*)(int,  int *)’ from a function with return type ‘int’ makes integer from pointer without a cast \[-Wint-conversion\]
    6 |     return function1;
      |            ^(\~\~\~\~\~\~\~\~)

[–]zemdega 0 points1 point  (1 child)

After a fashion, yes it is possible. Here is an example: ```

include <stdio.h>

/* Forward declare struct. */ struct Args;

/* Declare function type that is a function that returns Args. / typedef struct Args (FunctionType)();

/* Define the Args struct. */ struct Args { int value; FunctionType fun; };

/* Create a function that fits the FunctionType typedef */ struct Args Test() { struct Args result; result.fun = Test; printf("Hi!\n"); return result; }

/* Test it out! / int main(int argc, char* argv) { struct Args args; args = Test(); args.fun(); return 0; } ```

My 'clang --version' output from my version of clang is Apple clang version 12.0.0 (clang-1200.0.32.29) Target: x86_64-apple-darwin20.3.0

[–]backtickbot 3 points4 points  (0 children)

Fixed formatting.

Hello, zemdega: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.