GObject и наследование

Я делал серьезное программное обеспечение на Glib. И я понял, что есть некоторые темы, которые я не совсем понимаю. IRC тоже не помог...

Когда мы делаем наследование, у нас может быть два класса. Первый A наследуется от GObject напрямую, B наследуется от A. Потом я пришел к следующему:

https://developer.gnome.org/gobject/stable/chapter-gobject.html

static void
viewer_file_constructed (GObject *obj)
{
  /* update the object state depending on constructor properties */

  /* Always chain up to the parent constructed function to complete object
   * initialisation. */
  G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed = viewer_file_constructed;
}

Но когда у вас есть такая договоренность. Дочерний класс делает следующее: object_class->constructed = viewer_file_constructed; в B переопределяет фактически единственный адрес памяти для сконструированного. Таким образом, это означает, что G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj); вызовет B->constructed рекурсивно. И это не то, что мы хотим.

Может быть, я не понимаю, но я предполагаю, что структура памяти в B такова:

struct _B
{
  A parent_instance;

  /* instance members */
};

Внутреннее представление должно быть примерно таким:

[   Gobject struct memory ]
[                         ]
[   Gobject variables     ]
[   A struct memory       ]
[   A variables           ] 
[                         ]
[   B struct memory       ]
[   B variables           ] 

Таким образом, память GObject при приведении в B является общей для классов B и A. И все адреса одинаковые на одном и том же я...

  • G_OBJECT_CLASS (B)->построен
  • G_OBJECT_CLASS (A)->построен

Это правильно? Итак, если я хочу перезаписать построенный... Должен ли я сохранить указатель, который был раньше, а затем перезаписать его в init моим? Так что я могу вызвать оригинал после того, как я сделаю свою обработку?

То же самое относится и к свойствам. Поскольку A определяет свои свойства с помощью перечисления, оно может варьироваться от 0 до N.

Поэтому я предполагаю, что свойства B должны начинаться с N, а не с 0. В противном случае свойства A обрабатываются B и, возможно, с другими структурами данных и именами.

Проверьте это: https://developer.gnome.org/gobject/stable/gobject-properties.html

enum
{
  PROP_FILENAME = 1,
  PROP_ZOOM_LEVEL,
  N_PROPERTIES
};

Если оба clases определяют свойство с индексом 1. Там будет проблема, потому что glib не знает, кто должен обрабатывать. Я предполагаю, что дочерний класс B справится с этим, но неправильно, потому что, возможно, B ожидает, скажем, PROP_DIRECTORY, но поскольку индекс тот же. Сможет ли glib отправить в нужный экземпляр?

Я могу только сказать, что это сработает, если в регистре glib будет добавлено некоторое смещение в зависимости от уровня иерархии. Может кто-нибудь объяснить, как это на самом деле работает? Я не могу найти ни одного документа с требуемыми техническими подробностями.


person Gonzalo Aguilar Delgado    schedule 17.01.2018    source источник
comment
Возможно, единственный способ получить ответ - посмотреть на источник...   -  person ptomato    schedule 18.01.2018
comment
GLib существует уже более 20 лет. Его официальная документация, как и она, стабильна и относительно медленно меняется. Вы должны предполагать, что такие официальные документы знают, о чем они говорят, вместо того, чтобы думать, что вы первый человек, который внезапно обнаружил в нем такие фундаментальные, полностью ломающие ошибки ... В таких случаях вы должны предположить, что вы неправильно поняли и вам нужно читать больше. , не то, чтобы документация была неправильной. Это не.   -  person underscore_d    schedule 23.10.2018


Ответы (1)


Внутреннее представление должно быть примерно таким:

Не совсем. Между структурами GObject и GObjectClass есть разница. Существует один экземпляр структуры GObject для каждого экземпляра объекта, но только один экземпляр GObjectClass для всего класса.

Если у вас есть класс FooBar, производный от GObject, структура FooBarClass будет выглядеть примерно так:

typedef struct
{
  GObjectClass parent_class;

  /* Virtual methods for FooBar instances: */
  void (*vfunc) (FooBar *self);
} FooBarClass;

В куче будет экземпляр FooBarClass. Поскольку он содержит всю структуру GObjectClass в качестве члена parent_class, это означает, что он имеет свои собственные указатели виртуальных методов finalize, dispose, get_property и т. д.

Отдельно в куче есть экземпляр GObjectClass для типа GObject. Он содержит еще один набор указателей виртуальных методов finalize, dispose и т. д.

Поскольку FooBar происходит от GObject, foo_bar_parent_class будет указывать на экземпляр GObjectClass. Это то, что позволяет сцепиться.

Поэтому, если вы хотите реализовать виртуальный метод constructed и связать его (вы должны связать constructed), просто сделайте так, как пример кода в документации, на которую вы ссылаетесь, делает. Это верно.


То же самое относится и к свойствам. Поскольку A определяет свои свойства с помощью перечисления, оно может варьироваться от 0 до N.

Неправильно. Когда свойство регистрируется в классе с использованием g_object_class_install_properties(), индексы свойств связываются с экземпляром GObjectClass внутри структуры класса для этого класса. Они не связаны с автономной структурой GObjectClass для типа GObject. Это тот же принцип, что и выше.

Другими словами, нет глобального реестра индексов свойств: все делается для каждого класса. Так что можно (и нужно) начинать индексацию свойств с 1 для каждого из ваших классов. Они не будут конфликтовать.

Обратите внимание, что, как указано в документации для g_object_class_install_properties() индекс свойства 0 является особым и не должен использоваться. Вы должны начать индексацию вашего имущества с 1.


Как говорит ptomato, это выходит за рамки уровня детализации, подходящего для документации. Вы должны прочитать исходный код.

person Philip Withnall    schedule 18.01.2018