Показаны сообщения с ярлыком GLib. Показать все сообщения
Показаны сообщения с ярлыком GLib. Показать все сообщения
В позапрошлом посте рассказывалось о создании экземпляров объектов GObject по имени класса, где, кроме всего прочего, было сказано:
Лично у меня данный пример как есть в Windows не заработал (собирал компилятором mingw). Не удавалось найти указатель на функцию cow_get_type. Опытным путем выяснилось, что работает такой механизм при соблюдении двух условий:
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type... и вызова этого метода.Все почти так и есть, но в коде GtkBuilder случайно был найден довольно изящный и портабельный механизм поиска foo_bar_get_type. Идея простая: нужно создать объект GModule не для динамически загружаемого модуля, а для самой программы. Для чего в метод g_module_open(const gchar *file_name, GModuleFlags flags) вместо имени файла нужно передать NULL:
file_name — the name of the file containing the module, or NULL to obtain a GModule representing the main program itself.Пример на Vala на основе идей из GtkBuilder (механизм получения типа по имени класса иллюстрирует функция type_from_name_lazy):
public abstract class Animal : Object { public abstract string to_string(); } public class Cow : Animal { public override string to_string() { return "I'm a cow!"; } } public class Dog : Animal { public override string to_string() { return "I'm a dog!"; } } int main() { Animal a; Type type; a = new Dog(); //< Явно создаем Dog, имя типа зарегистрировано в системе типов. print("1) %s\n", a.to_string()); if((type = Type.from_name("Dog")) != Type.INVALID) { a = Object.new(type) as Animal; print("2) %s\n", a.to_string()); } else print("2) Failed to get type!\n"); if((type = Type.from_name("Cow")) != Type.INVALID) { a = Object.new(type) as Animal; print("3) %s\n", a.to_string()); } else { print("3) Failed to get type! Trying type_from_name_lazy...\n"); if((type = type_from_name_lazy("Cow")) != Type.INVALID) { a = Object.new(type) as Animal; print("3) %s\n", a.to_string()); } else print("3) Failed to get type!\n"); } return 0; } [CCode (has_target = false)] delegate Type TypeGetFunc(); Type type_from_name_lazy(string name) { void *func = null; if(Module.open(null, 0).symbol(ascii_camel_to_snake(name) + "_get_type", out func)) return ((TypeGetFunc)func)(); else return Type.INVALID; } string ascii_camel_to_snake(string input) { var sb = new StringBuilder(); for(int i = 0; input[i] != '\0'; i++) { if( ( input[i].isupper() && i > 0 && !input[i - 1].isupper() ) || ( (i > 2 && input[i].isupper()) && input[i - 1].isupper() && input[i - 2].isupper() ) ) sb.append_c('_'); sb.append_c(input[i].tolower()); } return sb.str; }Вывод программы:
1) I'm a dog!И немного про Windows
2) I'm a dog!
3) Failed to get type! Trying type_from_name_lazy...
3) I'm a cow!
Лично у меня данный пример как есть в Windows не заработал (собирал компилятором mingw). Не удавалось найти указатель на функцию cow_get_type. Опытным путем выяснилось, что работает такой механизм при соблюдении двух условий:
- Объект должен быть реализован в отдельной dll;
- До попытки поиска указателя должна быть вызвана хотя бы одна функция из этой dll.
В предыдущем посте «Клонирование GObject» я рассказывал про идею некоего обобщенного кода, не зависимого от конкретной задачи,
который бы сохранял объекты Gobject в файлы (типы объектов и значения
свойств) и потом воссоздавал последовательность объектов по этим файлам. Предполагалось сохранять в текстовый файл имена классов и порядок объектов, а также имена свойств объектов и их значения.
Идея красивая, но здесь мы сталкиваемся с одним ограничением системы типов GObject (ограничением вполне естественным, т.к. написан GObjecct на Си и вся поддержка ООП реализована на уровне библиотеки): чтобы создать экземпляр класса по имени класса нужно, чтобы этот класс уже был зарегистрирован в системе типов GObject. Т.е. для типа FooBar должен быть заранее вызван метод foo_bar_get_type непосредственно или опосредованно (например, через метод создания вроде foo_bar_new).
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type (см. тему на stack overflow) и вызова этого метода.
UPDATE: Найден довольно изящный и портабельный способ поиска метода foo_bar_get_type, см. Создание объектов GObject по имени типа. Часть 2.
Пример на Vala ниже иллюстрирует описанную проблему. Мы можем создать объект типа Dog по имени, т.к. выше уже создан объект типа Dog и класс зарегистрирован в системе типов. Создание объекта типа Cow же завершается неудачей (см. вывод программы после исходного кода).
Идея красивая, но здесь мы сталкиваемся с одним ограничением системы типов GObject (ограничением вполне естественным, т.к. написан GObjecct на Си и вся поддержка ООП реализована на уровне библиотеки): чтобы создать экземпляр класса по имени класса нужно, чтобы этот класс уже был зарегистрирован в системе типов GObject. Т.е. для типа FooBar должен быть заранее вызван метод foo_bar_get_type непосредственно или опосредованно (например, через метод создания вроде foo_bar_new).
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type (см. тему на stack overflow) и вызова этого метода.
UPDATE: Найден довольно изящный и портабельный способ поиска метода foo_bar_get_type, см. Создание объектов GObject по имени типа. Часть 2.
Пример на Vala ниже иллюстрирует описанную проблему. Мы можем создать объект типа Dog по имени, т.к. выше уже создан объект типа Dog и класс зарегистрирован в системе типов. Создание объекта типа Cow же завершается неудачей (см. вывод программы после исходного кода).
public abstract class Animal : Object { public abstract string to_string(); } public class Cow : Animal { public override string to_string() { return "I'm a cow!"; } } public class Dog : Animal { public override string to_string() { return "I'm a dog!"; } } int main() { Animal a; Type type; a = new Dog(); //< Явно создаем Dog, здесь класс и регистрируется в системе типов. print("1) %s\n", a.to_string()); if((type = Type.from_name("Dog")) != 0) { a = Object.new(type) as Animal; print("2) %s\n", a.to_string()); } else print("2) Failed to get type!\n"); if((type = Type.from_name("Cow")) != 0) { a = Object.new(type) as Animal; print("3) %s\n", a.to_string()); } else print("3) Failed to get type!\n"); return 0; }Вывод программы:
1) I'm a dog!
2) I'm a dog!
3) Failed to get type!
В одной из программ на работе используется ряд самописных Gtk-виджетов для настройки фильтров изображения. Виджеты лежат в Box'е, добавляются, удаляются и настраиваются пользователем. У каждого типа виджетов свой набор настроек. Все настройки виджеты хранят в своих свойствах.
Чтобы сохранять после перезапуска программы список фильтров и их параметры, была идея сделать некий обобщенны код, не зависимый от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам.
В качестве "proof of concept" был написан код, клонирующий объект Gobject. Функция g_object_clone возвращает копию объекта, переданного ей в качестве параметра src. Код основан на примере со StackOverflow. Обращаю внимание, что полноценно копируются только простые свойства, для boxed-типов копируется указатель с инкрементированием счетчика ссылок.
Код gobject_clone.c:
Чтобы сохранять после перезапуска программы список фильтров и их параметры, была идея сделать некий обобщенны код, не зависимый от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам.
В качестве "proof of concept" был написан код, клонирующий объект Gobject. Функция g_object_clone возвращает копию объекта, переданного ей в качестве параметра src. Код основан на примере со StackOverflow. Обращаю внимание, что полноценно копируются только простые свойства, для boxed-типов копируется указатель с инкрементированием счетчика ссылок.
Код gobject_clone.c:
// gcc `pkg-config --libs --cflags glib-2.0 gobject-2.0` gobject_clone.c point.c -o gobject_clone #include <glib-object.h> #include "point.h" #include <stdio.h> #include <string.h> //< For strcmp.В качестве объектов в примере взят простенький класс Point с гитхаба за авторством Jacob Gelbman. Код point.h:// See http://stackoverflow.com/questions/3003655/is-there-a-good-way-to-copy-a-gtk-widget?answertab=votes#tab-top static GObject *g_object_clone(GObject *src) { GObject *dst; GParameter *params; GParamSpec **specs; guint n, n_specs, n_params; specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs); params = g_new0(GParameter, n_specs); n_params = 0; for (n = 0; n < n_specs; ++n) if((specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) { params[n_params].name = g_intern_string(specs[n]->name); g_value_init(¶ms[n_params].value, specs[n]->value_type); g_object_get_property(src, specs[n]->name, ¶ms[n_params].value); ++n_params; } dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params); g_free(specs); g_free(params); return dst; } // Print, clear, print again. static void psp (Point *p) { g_print("OBJECT %p\n", p); g_print(" Before clearing:\n"); point_print(p); g_signal_emit_by_name(p, "clear"); g_print(" After clearing:\n"); point_print(p); g_print(" ...\n"); } int main(int argc, char **argv) { g_type_init(); GArray *array = g_array_new(FALSE, FALSE, sizeof(gint)); gint val1 = 33, val2 = 44; g_array_append_val(array, val1); g_array_append_val(array, val2); g_array_append_val(array, val1); GObject *p1 = g_object_new(TYPE_POINT, "x", 5, "y", 10, "a", array, NULL); GObject *p2 = g_object_clone(p1); //< Note: garray uses ref/unref for copy/free. psp(POINT(p1)); g_print("Appending value '%d' to array.\n", val2); g_print(" ...\n"); g_array_append_val(array, val2); psp(POINT(p2)); g_clear_object(&p1); g_clear_object(&p2); return 0; }
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance #ifndef __POINT_H__ #define __POINT_H__ #include <glib.h> #include <glib-object.h>Код point.c:#define TYPE_POINT (point_get_type ()) #define POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_POINT, Point)) #define POINT_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_POINT, PointClass)) #define IS_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_POINT)) #define IS_POINT_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_POINT)) #define POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_POINT, PointClass)) typedef struct _PointClass PointClass; typedef struct _Point Point; struct _PointClass { GObjectClass parent; void (*clear) (Point *self); }; struct _Point { GObject parent; gint x; gint y; GArray *array; }; GType point_get_type(void); void point_print(Point *self); #endif /*__POINT_H__*/
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance #include <glib-object.h> #include "point.h"G_DEFINE_TYPE(Point, point, G_TYPE_OBJECT); enum { PROP_0, PROP_ARRAY, PROP_X, PROP_Y }; enum { CLEAR, LAST_SIGNAL }; static guint point_signals[LAST_SIGNAL] = {0}; void point_print (Point *self) { g_print(" x: %d, y: %d\n", self->x, self->y); g_print(" ar:"); if(self->array) { guint i; for(i = 0; i < self->array->len; i++) g_print(" %d", g_array_index(self->array, gint, i)); } else g_print("null"); g_print("\n"); } static void point_clear (Point *self) { g_object_set(self, "x", 0, "y", 0, "a", NULL, NULL); } static void point_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { Point *point = POINT(obj); switch (prop_id) { case PROP_ARRAY: g_value_set_boxed(value, point->array); break; case PROP_X: g_value_set_int(value, point->x); break; case PROP_Y: g_value_set_int(value, point->y); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; } } static void point_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { Point *point = POINT(obj); switch (prop_id) { case PROP_ARRAY: if(point->array) g_array_unref(point->array); point->array = g_value_dup_boxed(value); break; case PROP_X: point->x = g_value_get_int(value); break; case PROP_Y: point->y = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; } } static void point_finalize(GObject *point) { if(POINT(point)->array) g_array_unref(POINT(point)->array); G_OBJECT_CLASS(point_parent_class)->finalize(point); } static void point_class_init (PointClass *cls) { GObjectClass *g_object_class = G_OBJECT_CLASS(cls); GParamSpec *array_param; GParamSpec *x_param; GParamSpec *y_param; g_object_class->get_property = point_get_property; g_object_class->set_property = point_set_property; g_object_class->finalize = point_finalize; cls->clear = point_clear; array_param = g_param_spec_boxed( "a", "a", "some int array", G_TYPE_ARRAY, G_PARAM_READWRITE); x_param = g_param_spec_int( "x", "x", "x loc of point", INT_MIN, /* => */ INT_MAX, 0, G_PARAM_READWRITE); y_param = g_param_spec_int( "y", "y", "y loc of point", INT_MIN, /* => */ INT_MAX, 0, G_PARAM_READWRITE); g_object_class_install_property( g_object_class, PROP_ARRAY, array_param); g_object_class_install_property( g_object_class, PROP_X, x_param); g_object_class_install_property( g_object_class, PROP_Y, y_param); point_signals[CLEAR] = g_signal_new( "clear", /* signal_name */ TYPE_POINT, /* itype */ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, /* signal_flags */ G_STRUCT_OFFSET(PointClass, clear), /* class_offset */ NULL, /* accumulator */ NULL, /* accu_data */ g_cclosure_marshal_VOID__VOID, /* c_marshaller */ G_TYPE_NONE, /* return_type */ 0); /* n_params */ } static void point_init(Point *point) { point->array = NULL; }
"Хозяйке на заметку": прочитать данные (переменные, целые, с плавающей точкой и т.п.) из бинарного файла на Vala можно и нужно с помощью GVariant, например так:
{ uint8[] buf = new uint8[sizeof(uint32) + sizeof(uint32) + sizeof(double) + sizeof(double)]; { var data_stream = new DataInputStream(file.read()); data_stream.read(buf); } Variant variant = Variant.new_from_data<uint8>( new VariantType("(uudd)"), buf, true); uint32 int1 = variant.get_child_value(0).get_uint32(); uint32 int2 = variant.get_child_value(1).get_uint32(); double double1 = variant.get_child_value(2).get_double(); double double2 = variant.get_child_value(3).get_double(); stdout.printf("int 1 = %u\n", int1); stdout.printf("int 2 = %u\n", int2); stdout.printf("double 1 = %f\n", double1); stdout.printf("double 2 = %f\n", double2); }
Подписаться на:
Сообщения (Atom)