Могу ли я объявить функцию, которая может принимать указатель на себя в качестве аргумента?
Чтение вопроса в stackoverflow, я задавался вопросом, можно ли объявить функцию, которая берет указатель на себя. То есть сделать такое объявление foo
, для которого было бы правильным:
foo(foo);
Простейшая идея заключается в том, чтобы использовать другой указатель на функцию (нельзя использовать для void*
, поскольку она может быть меньше), поэтому объявление функции выглядит следующим образом:
void foo(void (*)());
Пока это ОК в C (и будет работать с кастингом на С++), мне интересно, можно ли это сделать без такой сложной "переинтерпретации" кастинга или потери информации о типе.
Другими словами, я хочу следующее выражение:
void foo( void (*)( void (*)( void (*) ( ... ))));
но, конечно, неограниченные декларации невозможны. Наивное typedef
ing тоже не помогает.
Шаблоны С++ приветствуются, даже если они делают код вызова (foo(foo)
) выглядят немного менее краткими, но все же конечными.
Ответы на C-стиль, которые показывают, как можно удалить информацию о типе без кастинга или другие трюки такого рода, конечно, интересны, но не будут приняты.
Ответы
Ответ 1
В общем, я согласен с Дарио - сделать это на уровне типа кажется невозможным.
Но вы можете использовать классы ( "шаблон стратегии" ):
class A {
void evil(A a) { // a pointer to A is ok too
}
};
Можно добавить оператор():
void operator()(A a) { return evil(a); }
Как правило, такие вещи лучше делать на языках FP. Версия Haskell проста:
data Evil = Evil (Evil -> Integer)
В этом случае используется оболочка (Evil
), которая соответствует использованию класса.
Из вопросителя: чего не хватало этому ответу, была способность передавать несколько разных функций в качестве аргумента одного из них. Это можно решить либо путем создания evil()
virtual, либо путем явного хранения в объекте указателя функции, который его реализует (что в основном одинаково).
При этом разъяснении ответ достаточно хорош, чтобы его можно было принять.
Ответ 2
По-видимому, нет - см. эту тему. Тип, требуемый здесь, всегда будет бесконечным.
Ответ 3
Еще один грязный трюк.
void Foo( ... )
{
}
int main()
{
Foo( Foo );
}
Выше программа будет компилироваться без каких-либо ошибок. Но это не рекурсивно. Следующая модифицированная функция - это рекурсивная версия с ограничителем.
#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 );
}
Ответ 4
Связанная проблема возвращает указатель на функцию того же типа. Он появляется при реализации государственных машин, поэтому он получил свою собственную запись в часто задаваемых вопросах C.
К вашей проблеме могут применяться те же обходные пути.
Ответ 5
Да
Это вариант "Можете ли вы написать функцию, которая возвращает указатель на себя?" , за исключением того, что в вашем случае функция тип рекурсивно появляется как argumkent, а не как возвращаемый тип. Однако ответ Herb Sutters можно использовать повторно: оберните указатель в прокси-классе с просроченным объявлением.
Ответ 6
Я не верю, что у вас может быть функция, которая может взять себя в качестве аргумента в С++ без какой-либо обманки, например, поместить вашу функцию в класс, а функция возьмет этот класс в качестве аргумента.
Другой способ, который будет возможен после того, как новые стандартные хиты С++ будут использовать lambdas, и сам захват лямбда. Я думаю, что это будет выглядеть примерно так:
auto recursive_lambda = [&recursive_lambda] { recursive_lambda(); };
Будьте предупреждены, что такой оператор полностью не протестирован в любом компиляторе, который поддерживает lambdas. Ваш пробег может отличаться.
Ответ 7
Это вполне допустимое использование void *
.
typedef void T(void *);
void f(T *probably_me)
{
(*probably_me)(f);
}
Ответ 8
Если функция может принимать себя как аргумент, то она может выполнять анонимную рекурсию, вызывая себя. Но рекурсия невозможна в просто типизированном лямбда-исчислении (это в основном то, что вы здесь, с типами функций). Для выполнения анонимной рекурсии вам необходимо реализовать комбинатор с фиксированной запятой, используя рекурсивные функции или рекурсивные типы.