4

I was trying to declare a function that takes a function of the same type as parameter.

void rec(void(*f)(void(*)(void(*)(...))))
{
    f(f);
}

I ended up making a recursive attempt.

You can always cast from a void*.

void rec(void* f)
{
    ((void(*)())f)(f);
}

But it's not type safe

I attempted to do this with a typedef:

typedef void(*RecFunc)(RecFunc);

But doesn't compile.

Is it possible to do it?

Sam
  • 1,542
  • 2
  • 13
  • 27
tuket
  • 3,232
  • 1
  • 26
  • 41
  • `typedef void(*RecFunc)(RecFunc);` is compiling just fine https://ideone.com/sUtDSL – Eugene Sh. Jun 14 '18 at 16:27
  • Look at what you're setting as the argument to your function in your typedef. – Christian Gibbons Jun 14 '18 at 16:28
  • @EugeneSh. Not if you actually try and use the typedef... RecFunc p = nullptr; – rmawatson Jun 14 '18 at 16:31
  • Related to [recursive std::function](https://stackoverflow.com/questions/23737449/recursive-typedef-function-definition-stdfunction-returning-its-own-type) and [recursive lambda](https://stackoverflow.com/questions/15345529/typedef-for-recursive-lambda) – Jarod42 Jun 14 '18 at 16:32
  • @rmawatson I just did. See it again https://ideone.com/sUtDSL – Eugene Sh. Jun 14 '18 at 16:32
  • 1
    I think the answer might be different for C and C++ – Eugene Sh. Jun 14 '18 at 16:34
  • @EugeneSh. The error from clang-C++ is *"error: unknown type name 'RecFunc'"* and the error from clang-C is *"error: a parameter list without types is only allowed in a function definition"* So one explanation is that you're compiling with a C compiler that defaults the parameter type to `int`. – user3386109 Jun 14 '18 at 16:38
  • It does, and then it doesn't: https://ideone.com/0Cq6mC :) turns out it is OK in C, but not in C++. – thang Jun 14 '18 at 16:39
  • 1
    @user3386109 Yeah, I guess it could happen.. So it is UB as interpreting `int` as a function pointer. – Eugene Sh. Jun 14 '18 at 16:40
  • you get a warning in c. That is interesting though. In C, it is OK because it's a loophole. You can define function arguments without type. int foo(bar) is ok. – thang Jun 14 '18 at 16:42
  • Wow, that's interesting! I was compiling with gcc C++14. I guess it's a case where C++ is not backwards compatible? – tuket Jun 15 '18 at 08:37

2 Answers2

5

You can't do what you are trying to do. As you noticed, you ended up trying to make a recursive typedef. That is not supported by the language.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • He's really asking about a recursive function declaration (typedef is just the mechanism) - also not allowed AFAIK, not that I've ever tried. –  Jun 14 '18 at 16:29
  • @EugeneSh., Try using `typedef void (*RefFunc)(RecFun f);`. – R Sahu Jun 14 '18 at 16:35
  • @EugeneSh., I didn't follow the *`h` is passed* bit. – R Sahu Jun 14 '18 at 16:48
  • @EugeneSh., gcc is doing something strange there. With `-Wall`, it prints a waring *parameter names (without types) in function declaration* and proceeds to build the program. However, if I change the `typedef` to `typedef void(*RecFunc)(int RecFunc);` it fails to build. Somebody with deep knowledge of `gcc` might be able to explain its behavior. – R Sahu Jun 14 '18 at 16:55
  • @EugeneSh. Looks like that doesn't compile in C++...wonder why. – Kyle Strand Jun 14 '18 at 17:25
  • @RSahu, if it is just an identifier without type, it is the deprecated K&R form of function declaration, I think. – Jens Gustedt Jun 14 '18 at 17:32
  • @JensGustedt, Wasn't `int` assumed as the default type if it wasn't specified? – R Sahu Jun 14 '18 at 17:33
  • @RSahu, 30 years ago, perhaps. – Jens Gustedt Jun 14 '18 at 18:58
  • @JensGustedt, what can be implied type these days? – R Sahu Jun 15 '18 at 15:16
5

You can't do this directly in a conformant manner, but you can if you put the function pointer in a wrapper struct:

struct F;
typedef void(*RecFunc)(struct F *);

struct F {
    RecFunc f;
};

We first forward declare the struct so the typedef can use it. Then we define the struct to contain the function pointer. In this case the resursive function type is defined to take a pointer to struct F, but it will still work if it takes an instance of struct F.

Then you can use it like this:

void f(struct F *sf)
{
    static int x=5;

    if (x) {
        printf("x=%d\n",x);
        x--;
        sf->f(&(struct F){f});
    }
}

int main()
{
    f(&(struct F){f});
    return 0;
}

Output:

x=5
x=4
x=3
x=2
x=1
dbush
  • 205,898
  • 23
  • 218
  • 273