Подтвердить что ты не робот

Могу ли я объявить функцию, которая может принимать указатель на себя в качестве аргумента?

Чтение вопроса в stackoverflow, я задавался вопросом, можно ли объявить функцию, которая берет указатель на себя. То есть сделать такое объявление foo, для которого было бы правильным:

foo(foo);

Простейшая идея заключается в том, чтобы использовать другой указатель на функцию (нельзя использовать для void*, поскольку она может быть меньше), поэтому объявление функции выглядит следующим образом:

void foo(void (*)());

Пока это ОК в C (и будет работать с кастингом на С++), мне интересно, можно ли это сделать без такой сложной "переинтерпретации" кастинга или потери информации о типе.

Другими словами, я хочу следующее выражение:

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

но, конечно, неограниченные декларации невозможны. Наивное typedef ing тоже не помогает.

Шаблоны С++ приветствуются, даже если они делают код вызова (foo(foo)) выглядят немного менее краткими, но все же конечными.

Ответы на C-стиль, которые показывают, как можно удалить информацию о типе без кастинга или другие трюки такого рода, конечно, интересны, но не будут приняты.

4b9b3361

Ответ 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

Если функция может принимать себя как аргумент, то она может выполнять анонимную рекурсию, вызывая себя. Но рекурсия невозможна в просто типизированном лямбда-исчислении (это в основном то, что вы здесь, с типами функций). Для выполнения анонимной рекурсии вам необходимо реализовать комбинатор с фиксированной запятой, используя рекурсивные функции или рекурсивные типы.