Родительские связи между объектами [C++]

Тема в разделе "LANGS.C", создана пользователем Guest, 1 сен 2011.

  1. Guest

    Guest Guest

    Публикаций:
    0
    Пытаюсь решить некоторую фундаментальную проблему (проблема существует в локальном контексте особых проектов), а именно связывание по родительской линии различных объектов.

    Все мы помним интересные, а кто-то считает это извратом, исследования и разработки товарища Александреску, многие уже оценили стратегическое проектирование с использованием шаблонов и примеры использующие такой подход - Loki::SmartPtr. Все это навеяло на некоторые дурные идеи.

    Итак, есть базовый класс строящийся из политик Policy, этот базовый класс наследуется всеми классами более высокого уровня, тем самым каждый объект имеет доступ к базовым возможностям заложенным в этот фундаментальный класс. Это дает возможность обращаться к базовым фичам любого класса, отлично подходит для реализации сервисного функционала.

    Одна из политик - Family, подразумевает связыванием объектов по родительской линии. Например у нас имеется объект A, объект B1 и объект B2. Объект A является родительским, объекты B1, B2 дочерними. Все объекты унаследованы от фундаментального класса.

    Задачи политики Family - обеспечить доступ к фундаментальному классу объектов B1, B2 через объект A.
    Псевдокод:

    Код (Text):
    1. APtr pa = new A();
    2. if (pa != nullptr)
    3. {
    4.     for (child_t &i: pa->base.family.childs) // рассматриваем фундамент объектов B1,B2 через итератор как дочерние элементы, про существование B1 и B2 мы можем ничего не знать
    5.     {
    6.          ....
    7.     }
    8. }
    Задача вполне решаемая, если связывать объекты вручную, но этот вариант не подходит по концепции, которая выглядит следующим образом:
    * Фундаментальный класс рассматривается как примитив - легкий и быстрый
    * Фундаментальный класс должен быть самостоятельным
    * Классы более высокого уровня унаследовавшие фундаментальный класс не должны изменяться во внутренней логике
    * Все вышеописанное позволяет создать дополнительный уровень контроля и принятия решений в работе логики классов

    Единственный, пришедший в голову, вариант для связи выглядит следующим образом:
    Конструктору дочерних классов передается this родительского (тоже не самое лучшее решение, т.к. RAII подход может обрушить конструктор после добавления нового класса в список родителя. Либо аналогичное решение в виде вызова функции setParent(this) у дочерних элементов сразу после конструирования.

    Все это требует модификации кода уже существующих классов. Одно дело в хидере добавить наследование, другое дело лезть в код.

    Все это напоминает собой .NET, там это используется для GC.

    Вопрос такой: как произвести автоматическое легкое связывание (без модификации кода связываемых классов) по схеме родители<->дети.

    Допускаются любые идеи, хаки, фичи компиляторов, главное чтобы оно как-то заработало. Также вспомнился Phoenix Project, но увы под VS2010 оно не работает и проект вообще давно не развивается, но может есть какие другие, о которых я не знаю?
     
  2. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Ничего не понял. this дочерних объектов неявно приводится к родительскому (кроме случаев ромбовидного наследования). Зачем его где-то хранить? Или речь не про наследование типов, а про агрегирование ссылок на другие объекты?
     
  3. Guest

    Guest Guest

    Публикаций:
    0
    Да, речь про ссылки на объекты. Про классы/типы речь не идет.
     
  4. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    im1111

    Ну тогда все зависит от того, кто контролирует время жизни дочерних объектов, и как этот контроль связан с временем жизни родителя. В зависимости от ситуации это может быть контейнер shared_ptr, или контейнер простых поинтеров. Или что-то еще.
     
  5. Guest

    Guest Guest

    Публикаций:
    0
    Ммм, забыл ньюанс - фундаментальный класс создает "контекст" (обычный класс), который построен через SmartPtr со счетчиком ссылок, в этом контексте есть ссылка на сам класс. Манипуляции происходят с этим контекстом, т.е. если объект разрушился, то невалидной будет ссылка в контексте на этот объект (если контекст еще используется и не разрушен).
    Получается что мы связываем между собой контексты, через которые мы можем получить доступ к объекту, которому принадлежит контекст (проксирование).
     
  6. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    im1111

    Ну опять же, тогда все зависит от того, как соотносятся времена жизни "фундаментального класса" и "контекста". Можно хранить shared_ptr на фундаментальный класс внутри контекста - тогда первый будет жить как минимум до тех пор, пока жив второй.
     
  7. Guest

    Guest Guest

    Публикаций:
    0
    ага, со временем жизни уже решено, это я упростил все, чтобы не запутывать сложностями реализации, вопрос собственно в автоматическом связывании
     
  8. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    im1111

    Ну например так: контекст создается методом фундаментального класса. Контекст через конструктор принимает shared_ptr. Фундаментальный класс отнаследован от enable_shared_from_this чтобы передавать shared_ptr на себя внутри метода и не напрягать юзера:

    Код (Text):
    1. class fundamental : public std::enable_shared_from_this<fundamental>
    2. {
    3. public:
    4.  
    5.     std::shared_ptr<context> make_context(...)
    6.     {
    7.         return std::make_shared<context>(shared_from_this());
    8.     }
    9. };
     
  9. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Если дочерний объект нельзя модифицировать, то как он будет использовать родительский функционал? А если его будут использовать извне, то заводите контейнер для связей и все дела.

    З.Ы. И прошу, не пишите "хидер".
     
  10. Guest

    Guest Guest

    Публикаций:
    0
    Дочерний объект модифицировать можно, имеется в виду модификация его описания в ".h" файле, т.е.:

    Код (Text):
    1. было:
    2.  
    3. class A
    4. {
    5. ....
    6. }
    7.  
    8. стало:
    9.  
    10. class A: public base
    11. {
    12. ...
    13. }
    Это допустимо, т.к. можно модифицировать все .h, а этот base подменить заглушкой если отпадет необходимость использования в будущем.

    Пример:

    Код (Text):
    1. class B: public base
    2. {
    3. ....
    4. }
    5.  
    6. class A: public base
    7. {
    8. ....
    9. private:
    10.     SmartPtr<B> m_lpB;
    11. }
    Нужно уведомить m_lpB о том что его родитель class A. Я описал выше, что в голову приходит только:
    Код (Text):
    1. m_lpB = SmartPtr(new B(this));
    в коде класса A и это не лучшее решение, т.к. все конструкторы классов придется модифицировать, что не есть хорошо. Ну еще дикий метод - препроцессор который все сделает сам перед компиляцией, однако это целый парсер C++ кода придется писать, готовых решений я не видел.
     
  11. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    im1111

    Ну это в любом случае придется делать руками, т.к. агрегация автоматически не детектится.
     
  12. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Если можно менять заголовочный файл и при этом нельзя трогать .cpp, то можно сделать следующий хак.

    Base.h
    Код (Text):
    1. #pragma once
    2.  
    3. template<typename T>
    4. class Base
    5. {
    6. public:
    7.     Base()
    8.     {
    9.         T *p = static_cast<T*>(this);
    10.         p->m_lpB = SmartPtr(new B(this));
    11.     }
    12. };
    Derived.h
    Код (Text):
    1. #pragma once
    2. #include "Base.h"
    3.  
    4. class Derived : Base<Derived>
    5. {
    6. friend Base<Derived>;
    7. private:
    8.     SmartPtr<B> m_lpB;
    9. };
     
  13. Guest

    Guest Guest

    Публикаций:
    0
    Спасибо всем за помощь, натолкнули на новые варианты решения вопроса, продолжаю рисерч.