Czy mogę zadeklarować funkcję, która może przyjmować wskaźnik do siebie jako argument?

Czytając pytanie w stackoverflow, zastanawiałem się, czy jest możliwe zadeklarowanie funkcji, która pobiera wskaźnik do siebie. Tj. złożyć taką deklarację foo, dla której poprawne byłoby:

foo(foo);

Najprostszym pomysłem jest rzutowanie na inny wskaźnik funkcji (nie można rzucać na void*, ponieważ może być mniejszy), więc deklaracja funkcji wygląda tak:

void foo(void (*)());

Chociaż w C jest OK (i będzie działać z castingiem w C++), to zastanawiam się, czy da się to zrobić bez tak twardej "reinterpretacji" odlewania lub utraty informacji typu.

Innymi słowy, chcę następującą deklarację:

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

Ale, oczywiście, nieograniczone deklaracje są niemożliwe. Naiwny typedefing też nie pomaga.

Szablony C++ są mile widziane, nawet jeśli sprawiają, że kod wywołujący (foo(foo)) wygląda nieco mniej zwięźle, ale wciąż jest skończony.

Odpowiedzi w stylu C, które pokazują, jak można upuścić informacje o typie bez rzucania, lub inne tego typu sztuczki są, oczywiście, interesujące, ale nie będą akceptowane.

Author: P Shved, 2009-10-03

8 answers

Ogólnie zgadzam się z Dario - zrobienie tego na poziomie typu wydaje się niemożliwe.

Ale możesz użyć klas ("wzór strategii"):

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

Możesz nawet dodać operator ():

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

Ogólnie rzecz biorąc, takie rzeczy są lepiej robione w językach FP. Wersja Haskell jest po prostu:

data Evil = Evil (Evil -> Integer)

Używa wrappera (Evil), który odpowiada używaniu klasy.

od pytającego : tej odpowiedzi brakowało możliwości przejścia kilku różnych funkcji jako argument jednego z nich. Można to rozwiązać poprzez uczynienie evil() wirtualnym lub poprzez jawne Przechowywanie w obiekcie wskaźnika funkcji, który go implementuje (który jest w zasadzie taki sam).

Z tym wyjaśnieniem, odpowiedź jest wystarczająco dobra, aby zostać zaakceptowanym.

 2
Author: sdcvvc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-27 15:50:35

Najwyraźniej nie-zobacz Ten wątek . Typ wymagany tutaj zawsze byłby nieskończony.

 5
Author: Dario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48

Kolejna brudna sztuczka.

void Foo( ... )
{
}

int main()
{
 Foo( Foo );
}

Powyĺźszy program bÄ ™ dzie kompilowany bez bĹ ' Ä ™ du. Ale nie jest rekurencyjny. Po zmodyfikowanej funkcji jest wersja rekurencyjna z ogranicznikiem.

#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 );
}
 5
Author: Vadakkumpadath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-30 01:23:20

Pokrewnym problemem jest zwrócenie wskaźnika funkcji tego samego typu. Pojawia się przy implementacji maszyn państwowych, więc ma swój własny wpis W C FAQ.

Te same obejścia można zastosować do problemu.

 2
Author: Christoph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-03 20:05:50

Tak

Jest to wariant " Czy możesz napisać funkcję, która zwraca wskaźnik do siebie?", z tą różnicą, że w Twoim przypadku typ funkcji pojawia się rekurencyjnie jako argument, a nie jako typ zwracany. Odpowiedź Herb Sutters jest wielokrotnego użytku, choć: zawiń wskaźnik w Forward-deklarowanej klasy proxy.

 2
Author: MSalters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 10:26:43

Nie wierzę, że możesz mieć funkcję, która może przyjmować samą siebie jako argument w C++ bez jakichś sztuczek, takich jak umieszczenie swojej funkcji w klasie, a funkcja przyjmuje tę klasę jako argument.

Innym sposobem, który będzie możliwy po trafieniu nowego standardu C++, jest użycie lambda i posiadanie samego przechwytywania lambda. Myślę, że pójdzie coś takiego:

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

Należy pamiętać, że takie stwierdzenie jest całkowicie niesprawdzone w każdym kompilatorze, który obsługuje lambda. Przebieg może się różnić.

 1
Author: ZachS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-03 20:22:20

Jest to całkowicie poprawne użycie void *.

typedef void T(void *);

void f(T *probably_me)
{
  (*probably_me)(f);
}
 0
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-03 19:43:39

Jeśli funkcja może przyjąć samą siebie jako argument, to może wykonać anonimową rekurencję przez wywołanie samej siebie. Ale rekurencja jest niemożliwa w rachunku lambda (który jest w zasadzie tym, co masz tutaj, z typami funkcji). Musisz zaimplementować kombinator punktów stałych używając funkcji rekurencyjnych lub typów rekurencyjnych w celu wykonania rekurencji anonimowej.

 0
Author: newacct,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-03 19:49:12