Создайте макрос для структуры указателя

Мне нужно создать макрос для приведения структуры указателя типа (правильное слово?) из параметра функции. Это моя структура:

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» Любая помощь?

Выражение PAppData(_in_) является явным преобразованием C++, а не C. Для C существует только один тип явного преобразования («приведение»), например (PAppData) _in_. Как вас должны были научить новички в материале C. Но ваш материал для начинающих также должен был научить вас тому, что в C нет необходимости приводить к или из указателей void *, они неявно преобразуются в любой другой простой тип указателя. Так что простой PAppData pad = user_data; подойдет.

Some programmer dude 24.04.2024 15:57

Это ужасно. Не используйте typedef, чтобы скрыть природу указателя. На самом деле, подумайте о том, чтобы вообще избегать определений типов — они никогда не требуются и лишь изредка проясняют, а не скрывают. Что касается макросов, я не могу себе представить, почему кто-то захочет использовать макрос, чтобы скрыть простое приведение типов. Однако если вы настаиваете на этом, сначала напишите пример нужного вам кода без использования макроса, а затем используйте его как основу для написания макроса.

John Bollinger 24.04.2024 15:57

См. Хорошая ли идея использовать указатели typedef? — TL;DR, ответ обычно отрицательный, за исключением, возможно, указателей на функции (которые в противном случае могут быть довольно многословными).

Jonathan Leffler 24.04.2024 16:02

@JohnBollinger «Это ужасно. Не используйте typedef, чтобы скрыть природу указателя». -- А как насчет GLIB typedef void* gpointer;, оправдано ли использование?

yvs2014 24.04.2024 22:53

Нет, @yvs2014, это не так.

John Bollinger 25.04.2024 02:10

В общем, вероятно, лучше не использовать указатели typedef. Однако библиотека glib использует typedef (void*) gpointer; уже несколько десятилетий. И поскольку такие библиотеки, основанные на glib/gobject, как gtk, то, что вы используете, также используйте его. Иногда, я полагаю, просто плыву по течению.

hetepeperfan 25.04.2024 09:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В 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.

Спасибо за объяснение. Я программист-самоучка.

Joel 24.04.2024 18:48

@Джоэл, и ты научил меня новому слову. Спасибо!

Harith 25.04.2024 01:06

Приведение типов происходит следующим образом:

(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 круглые скобки свободны.

Mad Physicist 24.04.2024 16:05

Я бы добавил ссылку на документ 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 25.04.2024 00:02

@ yvs2014 Думаю, это был не первый ресурс, который я нашел. Большое спасибо. Ответ обновлен.

Harith 25.04.2024 00:44

Другие вопросы по теме