LINUX.ORG.RU

Зачем нужны GTK_DIALOG(obj), GTK_WINDOW(obj) и т.п.?

 ,


0

1

Требовалось сделать аналог виндовой функции MessageBox исключительно для вывода сообщений об ошибках.
Решил динамически грузить GTK-ашную либу и минимальным количеством её функций выводить требуемое.
Вот что получилось:

#include <stdlib.h>
#include <stdio.h>
#ifdef __unix__
	#include <dlfcn.h>
#endif /* __unix__ */

void error_msg_box(const char *msg);

int main() {
	error_msg_box("Test ok.");
	return EXIT_SUCCESS;
}

void error_msg_box(const char *msg) {
	if (msg == NULL)
		msg = "Unknown error.";

#ifdef _WIN32
	int __stdcall MessageBoxA
		(void *wnd, const char *text, const char *caption, unsigned int type);
	#define MB_ICONERROR 0x10
	MessageBoxA(NULL, msg, "Error", MB_ICONERROR);
#else

#ifdef __unix__
	void *lib_gtk = dlopen("libgtk-x11-2.0.so", RTLD_LAZY);
	if (lib_gtk == NULL)
		goto fb;

	typedef void (*GTK_INIT)(int *argc, char ***argv);
	typedef void *(*GTK_MESSAGE_DIALOG_NEW)(void *parent, int flags, int type, int buttons, const char *format, ...);
	typedef void (*GTK_WINDOW_SET_TITLE)(void *window, const char *title);
	typedef void (*GTK_WINDOW_SET_POSITION)(void *window, int position);
	typedef void (*GTK_WINDOW_SET_KEEP_ABOVE)(void *window, int setting);
	typedef int (*GTK_DIALOG_RUN)(void *dialog);
	typedef void (*GTK_WIDGET_DESTROY)(void *widget);

	GTK_INIT gtk_init;
	GTK_MESSAGE_DIALOG_NEW gtk_message_dialog_new;
	GTK_WINDOW_SET_TITLE gtk_window_set_title;
	GTK_WINDOW_SET_POSITION gtk_window_set_position;
	GTK_WINDOW_SET_KEEP_ABOVE gtk_window_set_keep_above;
	GTK_DIALOG_RUN gtk_dialog_run;
	GTK_WIDGET_DESTROY gtk_widget_destroy;

	#define GTK_MESSAGE_ERROR 3
	#define GTK_BUTTONS_CLOSE 2
	#define GTK_WIN_POS_CENTER 1

	if (
		!(gtk_init = dlsym(lib_gtk, "gtk_init")) ||
		!(gtk_message_dialog_new = dlsym(lib_gtk, "gtk_message_dialog_new")) ||
		!(gtk_window_set_title = dlsym(lib_gtk, "gtk_window_set_title")) ||
		!(gtk_window_set_position = dlsym(lib_gtk, "gtk_window_set_position")) ||
		!(gtk_window_set_keep_above = dlsym(lib_gtk, "gtk_window_set_keep_above")) ||
		!(gtk_dialog_run = dlsym(lib_gtk, "gtk_dialog_run")) ||
		!(gtk_widget_destroy = dlsym(lib_gtk, "gtk_widget_destroy"))
	)
		goto fb;

	gtk_init(NULL, NULL);
	void *dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", msg);
	gtk_window_set_title(dialog, "Error");
	gtk_window_set_position(dialog, GTK_WIN_POS_CENTER);
	gtk_window_set_keep_above(dialog, 1);
	gtk_dialog_run(dialog);
	gtk_widget_destroy(dialog);

	if (dlclose(lib_gtk))
		fputs("Warning: Failed to close GTK library.\n", stderr);

	return;
#endif /* __unix__ */

fb: fprintf(stderr, "Error: %s\n", msg);

#endif /* _WIN32 */
}
Компилируется так: gcc main.c -ldl -o main
(Под винду так: i686-w64-mingw32-gcc main.c -o main.exe)

Внимание вопрос: зачем в GTK нужны макросы GTK_DIALOG(obj), GTK_WINDOW(obj) и т.п. если всё вроде как работает и без них?

В документации в эти макросы предлагают оборачивать объекты (виджеты) при передаче в функции, например так:

gtk_dialog_run(GTK_DIALOG(dialog));

Для явного приведения типов, иначе не скомпилишь правильно — с ключами -Wall -Wextra -Werror (без этих ключей вообще нельзя ничего более-менее серьезного компилять!)

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

Для явного приведения типов, …

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

LinuxUser ★★ ()
Ответ на: комментарий от LinuxUser

Там еще и проверка данных в макросе может быть.

прошу привести конкретный пример при каких обстоятельствах это произойдёт.

Без понятия, я этой гадостью (GTK) уже давно не пользовался. Вообще предпочитаю не связываться с GUI, но если надо, делаю веб-морду.

Eddy_Em ☆☆☆☆☆ ()

А что поделать? Писать ООП на языке, который для этого не предназначен - ССЗБ. Скажем так, это такая реализация полиморфизма на С с помощью макросов.

hippi90 ★★★★★ ()
Ответ на: комментарий от LinuxUser

Может ли возникнуть ситуация когда приведённый выше код не сработает, например, ссегфолтится?

Если ты приводишь тип к суперклассу, то нет, т.к. физически адрес не меняется. Например, GtkButton в GtkWidget.

Если ты приводишь к интерфейсу, то адрес меняется. Выкинув кастующий макрос, получишь сегфолт или еще что-нибуль интересное. Например, GtkEntry в GtkEditable.

Макросы нужны для двух вещей:

Во-первых, для проверок, что ты правильно работаешь с типами данных.

Во-вторых, чтобы унифицировать работу с классами и интерфейсами. Ставя везде касты, ты можешь не думать о том, что есть класс, а что интерфейс.

Deleted ()

в сорцы заглядывал? там всё английским языком написано.

в дебаг версии будет вызвана проверка на допустимость каста. в релиз это простой каст.

/* The debugging versions of the casting macros make sure the cast is "ok"
 *  before proceeding, but they are definately slower than their less
 *  careful counterparts as they involve no less than 3 function calls.
 */
#ifdef NDEBUG

#define GTK_CHECK_CAST(obj,cast_type,cast)         ((cast*) obj)

#else /* NDEBUG */

#define GTK_CHECK_CAST(obj,cast_type,cast) \
  ((cast*) gtk_object_check_cast ((GtkObject*) obj, cast_type))

#endif /* NDEBUG */


/* Macro for casting a pointer to a GtkObject pointer.
 */
#define GTK_OBJECT(obj)                   GTK_CHECK_CAST (obj, gtk_object_get_type (), GtkObject)


#endif

vtVitus ★★★★★ ()
Ответ на: комментарий от vtVitus

Всё понял, всем спасибо!

Если кто надумает использовать тот код что я написал, то в нём не хватает освобождения lib_gtk в случае если не получилось найти указатель на одну из функций.

58 и 59 строчки

	)
		goto fb;
нужно заменить на
	) {
		if (dlclose(lib_gtk))
			fputs("Warning: Failed to close GTK library.\n", stderr);
		goto fb;
	}

LinuxUser ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.