views:

789

answers:

8

Reading a question in stackoverflow, I wondered whether it's possible to declare a function that takes a pointer to itself. I.e. to make such declaration of foo, for which the following would be correct:

foo(foo);

The simpliest idea is casting to another function pointer (can't cast to void*, since it may be smaller), so the function declaration looks like this:

void foo(void (*)());

While that's OK in C (and will work with casting in C++), I wonder, whether it can be done without such hard "reinterpret" casting or losing type information.

In other words, I want the following declaration:

void foo( void (*)( void (*)( void (*) ( ... ))));

but, of course, unlimited declarations are impossible. Naive typedefing doesn't help either.

C++ templates are welcome, even if they make the calling code (foo(foo)) look a bit less succinct, but still finite.

C-style answers that show, how one can drop type information without casting, or other tricks of that sort are, of course, interesting, but won't be accepted.

+5  A: 

Apparently not - see this thread. The type required here would always be infinite.

Dario
I still don't see a relation between "infinite type" and "possible". If I thought there is one, I wouldn't ask the question, would I?
Pavel Shved
All statically-typed languages I know require their types to be finite. If a type is infinite, the type-system isn't able to express this one. But go ahead, invent an infinite type system.
Dario
Infinite types are not possible to declare...? You said it yourself, "unlimited declarations are impossible".
GMan
@GMan: unlimited *declarations* are impossible, but does it *really* forbid unlimited *types*? Something like `typedef foo(foo*);` is an example of (banned) finite declaration that declares unlimited type.
Pavel Shved
@Dario - "All statically-typed languages I know require their types to be finite." Ever heard of Haskell?
Chris Lutz
@Chris: Haskell doesn't allow infinite types, just infinite data! Or maybe you're referring to higher-rank polymorphism, but these types aren't infinite too.
Dario
C# is a statically typed language, and: `delegate void F(F f);` is a perfectly legal declaration.
Pavel Minaev
@Pavel: Then give an example value for `F` that is not null.
Dario
A: 

This is a perfectly valid use of void *.

typedef void T(void *);

void f(T *probably_me)
{
  (*probably_me)(f);
}
DigitalRoss
Ah, a good example of what I called "C-style trick". ;-)
Pavel Shved
actually, that's not valid C99 as function pointers can't be cast to `void *`
Christoph
Most implementations allow it, after all, every C compiler is backwards compatible to the days when `char *` was not only a system-wide alias, but it was the standard-blessed alias. But technically, @Christoph is right, and the conforming way to write this program is to declare a pointer to another function type and cast it. Still, my example passed gcc -Wall because it is a non-conforming but existing convention...
DigitalRoss
+1  A: 

Generally I agree with Dario - making this on type level seems impossible.

But you can use classes ("strategy pattern"):

class A {
  void evil(A a) {       // a pointer to A is ok too
  }
};

You can even add operator():

  void operator()(A a) { return evil(a); }

Generally such things are better done in FP languages. Haskell version is simply data Evil = Evil (Evil -> Integer). This uses a wrapper (Evil) that corresponds to using a class.

From the questioner: what this answer lacked was the ability to pass several different functions as an argument of one of them. This can be solved either by making evil() virtual or by explicitly storing in the object a function pointer that implements it (which is basically the same).

With this clarification, the answer is good enough to be accepted.

sdcvvc
Beat me to it...
DigitalRoss
Nice idea. But I'd also like to pass more parameters along with function pointer. I see a way of doing it with use of factory pattern to clone "functions" and passing the parameters as constructor arguments. But this seems too tricky...
Pavel Shved
Where exactly are you passing those parameters? My naive understanding would be void operator()(A a, int x, int y) - you can regard A as the type of functions taking A and two ints and returning void. Haskell's version would be data Evil = Evil (Evil -> Integer -> Integer -> T) where T is the return type. Did I understand you? Factory pattern is currying in FP - it's much clearer in ML/Haskell, but I don't understand why it is needed here. If possible please extend the question with some example usage of those additional parameters (not neccesarily a real-world one).
sdcvvc
+1  A: 

If a function could take itself as an argument, then it could perform anonymous recursion by calling itself. But recursion is impossible in simply-typed lambda calculus (which is basically what you have here, with function types). You need to implement a fixed-point combinator using recursive functions or recursive types in order to perform anonymous recursion.

newacct
Can you please give me an idea where to find additional information? Roughly speaking I'm looking for information that would enable me to understand:1) What is simply-typed lamda calculus2) That this is isomorphic to it3) That it excludes recursionThanks!
Ranieri
Thanks for a bunch of useful keywords! I bet, my next question will be "With what books can I study theory of FP?"
Pavel Shved
What does simply-typed lambda calculus have to do with C/C++?
Pavel Minaev
+2  A: 

A related problem is returning a function pointer of same type. It comes up when implementing state machines, so it got its own entry in the C FAQ.

The same workarounds can be applied to your problem.

Christoph
+1  A: 

I don't believe you can have a function that can take itself as an argument in C++ without some kind of trickery, such as putting your function in a class, and having the function take that class as an argument.

Another way that will be possible once the new C++ standard hits is to use lambdas, and have the lambda capture itself. I think it would go something like this:

auto recursive_lambda = [&recursive_lambda] { recursive_lambda(); };

Be warned that such a statement is totally untested in any compiler that supports lambdas. Your mileage may vary.

ZachS
+2  A: 

YES

It's a variant of "Can you write a function that returns a pointer to itself?", except that in your case, the function type recursively appears as an argumkent, not as the return type. Herb Sutters answer is reusable, though: wrap the pointer in a forward-declared proxy class.

MSalters
+5  A: 

Another dirty trick.

void Foo( ... )
{
}

int main()
{
 Foo( Foo );
}

Above program will compile without any error. But it is not recursive. Following modified function is recursive version with a limiter.

#define RECURSIVE_DEPTH (5)

typedef void ( *FooType )( int, ... );

void Foo( int Depth, ... )
{
 void ( *This )( int, ... );

 va_list Arguments;

 va_start( Arguments, Depth );

 if( Depth )
 {
  This = va_arg( Arguments, FooType );

  This( Depth - 1, This );
 }

 va_end ( Arguments );  
}

int main()
{
 Foo( RECURSIVE_DEPTH, Foo );
}
Vadakkumpadath
The above programs invoke undefined behavior with void main(). main() should always be declared as returning an int in C and C++.
David Thornley
@David - thanks for the tip.
Vadakkumpadath
What's against receiving the function pointer using normal `va_arg` / `va_start` calls, without that cursed offset calculations? They seem to be unnecessary and make the whole code non-portable. Would have upvoted if those offset calculations weren't there :)
Johannes Schaub - litb
@litb - Thank you. Actually I don't know how to use `va_start` without `previous variable` argument. Is it possible to use UNIX version in windows?I really respect your point of non-portability and removing my non-portable code..;)
Vadakkumpadath
There's no way to use `va_start` without a fixed argument, by design.
Pavel Minaev
@Pavel - Thanks for the info
Vadakkumpadath