Мне нужно создать макрос для приведения структуры указателя типа (правильное слово?) из параметра функции. Это моя структура:
typedef struct _AppData {
GtkWidget* toplevel; // main window
GtkWidget* view; // column style view
GtkWidget* entry_outdir; // output dir
GtkWidget* entry_artist; // artist
GtkWidget* entry_title; // title
GtkWidget* entry_link; // link
GtkWidget* check_jman; // bool for jman
GtkWidget* menuitem_clear; // init states
GtkWidget* menuitem_action; // play/stop menu item
} AppData, *PAppData;
где-то в моем коде используется вот так:
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_select_folder), pad);
void cb_select_folder (GtkButton* sender, gpointer user_data)
{
PAppData pad = PAPPDATA(user_data); // typecast macro here
const gchar* dirname = gtk_entry_get_text (GTK_ENTRY(pad->entry_outdir));
// ..
}
Чтобы создать макрос, я пробую это:
#define PAPPDATA(_in_) (PAppData(_in_))
и я получаю сообщение об ошибке: ожидается «)» перед «user_data» Любая помощь?
Это ужасно. Не используйте typedef, чтобы скрыть природу указателя. На самом деле, подумайте о том, чтобы вообще избегать определений типов — они никогда не требуются и лишь изредка проясняют, а не скрывают. Что касается макросов, я не могу себе представить, почему кто-то захочет использовать макрос, чтобы скрыть простое приведение типов. Однако если вы настаиваете на этом, сначала напишите пример нужного вам кода без использования макроса, а затем используйте его как основу для написания макроса.
См. Хорошая ли идея использовать указатели typedef? — TL;DR, ответ обычно отрицательный, за исключением, возможно, указателей на функции (которые в противном случае могут быть довольно многословными).
@JohnBollinger «Это ужасно. Не используйте typedef, чтобы скрыть природу указателя». -- А как насчет GLIB typedef void* gpointer;, оправдано ли использование?
Нет, @yvs2014, это не так.
В общем, вероятно, лучше не использовать указатели typedef. Однако библиотека glib использует typedef (void*) gpointer; уже несколько десятилетий. И поскольку такие библиотеки, основанные на glib/gobject, как gtk, то, что вы используете, также используйте его. Иногда, я полагаю, просто плыву по течению.





В C вам не нужно приводить (void*) к его окончательному типу. При использовании glib gpointer является определением типа для void*. Следовательно, кастинг не обязателен. Вам нужно быть уверенным, что тип правильный, иначе у вас проблемы.
void cb_select_folder (GtkButton* sender, gpointer user_data)
{
PAppData pad = user_data; // No cast Necessary.
const gchar* dirname = gtk_entry_get_text (GTK_ENTRY(pad->entry_outdir));
// ..
}
GTK_ENTRY(pad->entry_outdir) на самом деле выполняет приведение, но это потому, что оно выполняется между двумя разными типами указателей, от GtkWidget* до GtkEntry*. В дополнение к приведению, он также выполняет некоторую проверку во время выполнения, является ли GtkWidget* на самом деле GtkEntry*, вы, вероятно, получите предупреждение во время выполнения при выполнении этого приведения, когда pad->entry_outdir окажется, что указывает на что-то другое, чем GtkEntry. Но, говоря языком GObject, GtkEntry — это GtkWidget, а GtkWidget — это GObject. PAppData — это простой указатель на структуру, а не GObject.
Спасибо за объяснение. Я программист-самоучка.
@Джоэл, и ты научил меня новому слову. Спасибо!
Приведение типов происходит следующим образом:
(PAppData) _in_
Теперь заключите их в круглые скобки:
((PAppData) (_in_))
И теперь макрос должен работать. Ваш исходный макрос использовал приведение C++.
Предполагая, что gpointer — это void *, приведение не требуется, так как в C существует неявное преобразование в void * и обратно. Я также не понимаю, зачем здесь нужен макрос. Это работает:
PAppData pad = (PAPPDATA)user_data; # No macro required
Обновлено: Согласно документации :
gpointer
typedef void* gpointer;Нетипизированный указатель.
gpointerвыглядит лучше и его проще использовать, чемvoid*.
Так что актерский состав можно исключить. И, как упомянул в комментариях Джонатан Леффер, см.: Хорошая ли идея использовать указатели typedef?.
Также обратите внимание, что в документации говорится:
Обычно в новом коде предпочтительнее использовать стандартный тип C
void *, ноgpointerможно использовать в контекстах, где имя типа должно состоять из одного слова, например, в имениGTypeG_TYPE_POINTERили при создании семейства имен функций для нескольких типов с помощью макросы.
Также возьмите за привычку окружать _in_ скобками. Макросам можно передать что угодно, а в C круглые скобки свободны.
Я бы добавил ссылку на документ glib с собственным примечанием об использовании gpointer: The standard C void * type should usually be preferred in new code, but gpointer can be used in contexts where a type name must be a single word, such as in the GType name of G_TYPE_POINTER or when generating a family of function names for multiple types using macros. взято из docs.gtk.org/glib/types.html#gpointer
@ yvs2014 Думаю, это был не первый ресурс, который я нашел. Большое спасибо. Ответ обновлен.
Выражение
PAppData(_in_)является явным преобразованием C++, а не C. Для C существует только один тип явного преобразования («приведение»), например(PAppData) _in_. Как вас должны были научить новички в материале C. Но ваш материал для начинающих также должен был научить вас тому, что в C нет необходимости приводить к или из указателейvoid *, они неявно преобразуются в любой другой простой тип указателя. Так что простойPAppData pad = user_data;подойдет.