Можете ли вы привести указатель на функцию одного типа к функции другого типа, которая принимает дополнительные аргументы? - PullRequest
6 голосов
/ 07 января 2010

Можете ли вы привести указатель на функцию такого типа:

void (*one)(int a)

к одному из этого типа:

void (*two)(int a, int b)

и затем безопасно вызвать указанную функцию с дополнительными аргументами, которые она была приведена для принятия? Я думал, что это незаконно, что оба типа функций должны быть совместимы. (Имеется в виду тот же прототип - то же возвращаемое значение, тот же список параметров.) Но это именно то, что, кажется, делает этот бит кода GTK + (взято из здесь ):

g_signal_connect_swapped(G_OBJECT(button), "clicked",
                         G_CALLBACK(gtk_widget_destroy), G_OBJECT(window));

Если вы посмотрите на сигнал «нажал» (или просто посмотрите на другие примеры его использования из первой ссылки), вы увидите, что его обработчики должны быть объявлены следующим образом:

void user_function(GtkButton *button, gpointer user_data);

Когда вы регистрируете обработчик с помощью g_signal_connect_swapped (), указатель виджета и аргументы указателя данных меняются местами по порядку, поэтому вместо этого объявление должно выглядеть следующим образом:

void user_function(gpointer user_data, GtkButton *button);

Вот проблема. Функция gtk_widget_destroy (), зарегистрированная как обратный вызов, прототипируется следующим образом:

void gtk_widget_destroy(GtkWidget *widget);

чтобы принять только один аргумент. Предположительно, поскольку указатель данных (GtkWindow) и указатель на виджет сигнализации (GtkButton) меняются местами, единственным полученным им аргументом будет указатель окна, а указатель кнопки, который будет передан после, будет молча игнорироваться , Некоторые Googling приводят похожие примеры, даже регистрацию таких функций, как gtk_main_quit (), которые вообще не принимают аргументов.

Правильно ли я считаю, что это нарушение стандартов? Нашли ли разработчики GTK + легальную магию, чтобы все это работало?

Ответы [ 3 ]

2 голосов
/ 08 января 2010

Соглашение о вызовах в C делает ответственность вызывающей стороны за очистку аргументов в стеке. Так что, если вызывающая сторона предоставляет слишком много аргументов, это не проблема. Дополнительные аргументы просто игнорируются.

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

1 голос
/ 24 января 2010

Теперь, как это круто, я также в настоящее время прорабатываю учебник GTK и наткнулся на точно такую ​​же проблему.

Я попробовал несколько примеров, а затем задал вопрос Что произойдет, если я приведу указатель на функцию, изменив число параметров , с упрощенным примером.

Ответ на ваш вопрос (адаптировано из превосходных ответов на мой вопрос выше и ответов на вопрос Указатель функции приведен к другой сигнатуре ):

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

Так что настоящий вопрос в том, почему разработчики GLib сделали это так. Но это другой вопрос, я думаю ...

1 голос
/ 14 января 2010

На мой взгляд, стандарты С89 в этом отношении довольно запутанные. Насколько я знаю, они не запрещают приведение из / в функцию без указания параметров, поэтому:

typedef void (*one)(int first);
typedef void (*two)(int first, int second);
typedef void (*empty)();

one src = something;
two dst;

/* Disallowed by C89 standards */
dst = (two) src;

/* Not disallowed by C89 standards */
dst = (two) ((empty) src);

В конце компиляторы должны иметь возможность приводить значения от one до two, поэтому я не вижу причины запретить прямое приведение.

В любом случае, обработка сигналов в GTK + использует некое темное волшебство за сценой для управления обратными вызовами с различными шаблонами аргументов, но это другой вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...