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 typedef
ing 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.
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.
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.
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 );
}
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.
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.
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ć.
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);
}
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.
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