Russian Qt Forum
Ноябрь 05, 2024, 22:02 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Сделать из QObjectов синглтоны (по шаблону)  (Прочитано 8980 раз)
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« : Март 11, 2015, 19:10 »

Привет. Задача такая, есть объекты, унаследованные от QObject и еще нескольких классов (множественное наследование). Хочется сделать из этих объектов синглтоны. По сути каждый объект - это менеджер отвечающий за определенную часть работы программы. Менеджеров куча, к ним нужен доступ с разных модулей (библиотек). Раньше приходилось извращаться, делать отдельный объект, который хранил указатели на все остальные менеджеры и возвращал их по имени, а далее через dynamic_cast кастилось к нужным типам на месте. Т.к. данные менеджеры по сути в единичном экземпляре используются, есть резон их сделать синглтонами. Раньше простые синглтоны я использовал нормально, но вот с QObject ругается.

привожу код сингтона (нагуглил)
Код:
template <class T>
class CORESHARED_EXPORT CSingleton
{
public:
    static T& instance();
    void    free();
protected:
    virtual ~CSingleton();
    CSingleton();
private:
    static  T* _instance;
    static  int _refCount;
};

template <class T> T * CSingleton<T>::_instance = NULL;
template <class T> int CSingleton<T>::_refCount = 0;

template <class T>
CSingleton<T>::CSingleton()
{

}

template <class T>
CSingleton<T>::~CSingleton()
{
    _instance = NULL;
}

template <class T>
T &CSingleton<T>::instance()
{
    if(!_instance)
        _instance = new T;
    _refCount++;
    return  _instance;
}

template <class T>
void CSingleton<T>::free()
{
    if(--_refCount==0)
        delete this;
}

И вот есть такой класс
Код:
class CORESHARED_EXPORT CObjectStorageManager : //public  CSingleton<CObjectStorageManager>,
                                                        public  CManagerInterface,
                                                        public  CXMLDocumentItemInterface,
                                                        public  CMMNCadImportInterface
{
Q_OBJECT
public:
    explicit CObjectStorageManager(QObject *parent = 0);
    ~CRailObjectStorageManager();
};....

Если раскоментировать строку
Код:
//public  CSingleton<CObjectStorageManager>,
то получаем вот аткие ошибки

cobjectstoragemanager.cpp:26: ошибка: undefined reference to `CSingleton<CObjectStorageManager>::CSingleton()'
cobjectstoragemanager.cpp:26: ошибка: undefined reference to `CSingleton<CObjectStorageManager>::~CSingleton()'


Подскажите как грамотно сделать шаблон синглтона и применять его к QObject?
Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« Ответ #1 : Март 11, 2015, 23:48 »

в Qt 5 есть макрос: Q_GLOBAL_STATIC
в Qt 4 он тоже есть, но там недокументирован.
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #2 : Март 12, 2015, 10:28 »

Раньше приходилось извращаться, делать отдельный объект, который хранил указатели на все остальные менеджеры и возвращал их по имени, а далее через dynamic_cast кастилось к нужным типам на месте.
Как правило, с опытом, люди идут обратным путем: от плохих синглетонов к контекстам. Улыбающийся
Вы решили пойти наоборот?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #3 : Март 12, 2015, 13:54 »

Я конечно понимаю что пошёл в сторону говнокода, но пока не понимаю как сделать иначе. Если описать суть задачи то нужно следующее:

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

Я пока реализовал своим классом "глобальное пространство объектов", и к этому пространству имеют все менеджеры.
Исходники в аттаче.

Вот например описание класса одного из менеджеров (оставил только то что нужно)

Код:
class CORESHARED_EXPORT CObjectStorageManager : public  CManagerInterface,
                                                        public  CXMLDocumentItemInterface,
                                                        public  CMMNCadImportInterface
{
Q_OBJECT

CObjectFactoryLoader                       *   fmodelLoader; //указатель на другой менеджер, который будет использоваться этим

protected:

    void        enumerateEntities();
    void        detachEntities();

public:

    static  const   QString     SHARED_SPACE_NAME;
};

Код:

const   QString CObjectStorageManager::SHARED_SPACE_NAME = "storage_manager"; //это ключ, по которому будем искать указатель на этот менеджер

CObjectStorageManager::CObjectStorageManager(QObject *parent) :
    CManagerInterface(parent),
    CXMLDocumentItemInterface(),
    CMMNCadImportInterface(),
    fmodelLoader(NULL)
{

}

void CObjectStorageManager::enumerateEntities() //находим нужные нам менеджеры
{
    fmodelLoader = dynamic_cast<CObjectFactoryLoader*>(getSharedInstance(CObjectFactoryLoader::SHARED_SPACE_NAME));
}

void CObjectStorageManager::detachEntities()
{
    fmodelLoader = NULL;
}

Вот в программе происходит регистрация объектов (увы пока создаются не в либах, а в самой программе,да да говнокодом пахнет)
Код:
//конструктор главного окна
fsharedSpace->registerObject(CObjectStorageManager::SHARED_SPACE_NAME,fstorageManager);
fsharedSpace->registerObject(CObjectFactoryLoader::SHARED_SPACE_NAME,fmodelLoader);

Короче, как сделать что бы со всех частей программы (в либах, плагинах, самом экзешнике) был доступ ко всем нужным мне объектам, не тягая за собой все указатели на них.
При условии что они впринципе в еденичных экзеплярах.
Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« Ответ #4 : Март 12, 2015, 14:02 »

Я у себя сделал так:
Объект ядра умеет только искать модули, загружать/выгружать, запускать/останавливать.
Ядро при запуске модуля, предоставляет модулям свой интерфейс, через который модуль может запросить интерфейс другого модуля по его имени.


Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #5 : Март 12, 2015, 14:49 »

смотрю на шаблоны Mediator и Multiton - вот мне нужно нечто среднее..
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Март 12, 2015, 15:03 »

A если незатейливо
Код
C++ (Qt)
struct CBaseManager;
typedef CBaseManager * (*TCreateFunc)( void );
 
struct CBaseManager {
 static  bool AddManager( const QString & name,  TCreateFunc func );
 static  CBaseManager * GetManagerByName( const QString & name );
 
 virtual QString GetName( void ) const = 0;
 void Release( void );
 
private:
 ~CBaseManager( void );
 QAtomicInt mNumRef;
 
 typedef QPair <CBaseManager *, TCreateFunc> TPair;
 static QMap <QString, TPair> mMap;
};
 
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #7 : Март 12, 2015, 15:37 »

в целом это повторяет мой код. забыл уточнить, в этом "реестре" могут храниться не только менеджеры, но и объекты наследованные от других классов.. вот собственно это все и усложняет немного, поэтому приходится прибегать к dynamic_cast
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Март 12, 2015, 17:43 »

Если обязуетесь наследоваться от QObject и регистрироваться (Q_DECLARE_METATYPE). то можно избежать забот с "Create""
Код
C++ (Qt)
#include <QtWidgets>
 
QHash <int, QPointer<QObject> > theHash;
 
template <class T>
QSharedPointer<T> AcquireResource( void )
{
T * ptr = 0;
int id = qMetaTypeId<T> ();
QObject * obj = theHash.value(id);
if (obj)
ptr = dynamic_cast<T *> (obj);
else {
ptr = static_cast <T *> (QMetaType::create(id));
theHash[id] = ptr;
}
return QSharedPointer<T> (ptr);
}
 
//-------------------- test ------------------------
 
struct CTest : public QObject {
CTest( void ) : mA(5)
{
qDebug() << "construct";
}
 
CTest( const CTest & src )
{
mA = src.mA;
qDebug() << "construct copy";
}
 
  ~CTest( void )
{
qDebug() << "destroy";
}
 
int mA;
};
 
Q_DECLARE_METATYPE(CTest)
 
int main(int, char **)
{
for (int i = 0; i < 2; ++i) {
QSharedPointer<CTest> p = AcquireResource<CTest>();
qDebug() << p->mA;
}
 
return 0;
}
 

Edit: это неверно, будет работать только для одного клиента, когда он избавится от своего QSharedPointer - удалится и объект
« Последнее редактирование: Март 13, 2015, 05:11 от Igors » Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« Ответ #9 : Март 12, 2015, 17:54 »

странно выглядят эти два условия:
наследоваться от QObject и регистрироваться (Q_DECLARE_METATYPE).
при том, что QObject не должен иметь конструктора копирования и оператора =, а классы регистрируемые Q_DECLARE_METATYPE должны их иметь.
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #10 : Март 12, 2015, 19:04 »

забыл уточнить, в этом "реестре" могут храниться не только менеджеры, но и объекты наследованные от других классов..
наследников CSharedObjectInterface?

вот собственно это все и усложняет немного, поэтому приходится прибегать к dynamic_cast
Скройте это в методе получения указателя на объект.
Добавьте метод, который будет по типу и имени возвращать объекты:
Код
C++ (Qt)
Object *o1 = manager.instance<Object>(); // Вернет указатель на первый попавшийся объект класса Object или его наследников.
Object *o2 = manager.instance<Object>( "Obj2" ); // Тоже, только с именем Obj2
Storage *store = manager.instance<Storage>();
 
« Последнее редактирование: Март 12, 2015, 19:08 от Old » Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #11 : Март 12, 2015, 19:25 »

QObject (точнее мета-объектный компилятор) и шаблонный класс - несовместимы.

Подробнее о ситуации и способах обхода: Academic Solutions to Academic Problems
Записан

Юра.
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Март 13, 2015, 05:19 »

странно выглядят эти два условия:
наследоваться от QObject и регистрироваться (Q_DECLARE_METATYPE).
при том, что QObject не должен иметь конструктора копирования и оператора =, а классы регистрируемые Q_DECLARE_METATYPE должны их иметь.
Да, для каких-то классов конструктор копирования может быть private. И вообще чувствуется что metaType здесь избыточен, требуются только new и delete
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Март 13, 2015, 06:11 »

Вроде все получается. Есть одно "не очень академическое" приведение, ну ничего, знатоки подправят  Улыбающийся
Код
C++ (Qt)
#include <QtWidgets>
 
QHash <const char *, QWeakPointer <int> > theHash;
 
template <class T>
QSharedPointer<T> AcquireResource( void )
{
const char * key = typeid(T).name();
 
typedef QWeakPointer <T> TWeakPtr;
typedef QSharedPointer <T> TSharedPtr;
 
TWeakPtr & weakPtr = (TWeakPtr &) theHash[key];
TSharedPtr sharedPtr = weakPtr.toStrongRef();
if (sharedPtr.isNull()) {
sharedPtr = TSharedPtr(new T);
weakPtr = sharedPtr;
}
return sharedPtr;
}
 
//-------------------- test ------------------------
 
struct CTest {
CTest( void ) : mA(5) { qDebug() << "construct CTest"; }
~CTest( void ) { qDebug() << "destroy CTest"; }
 
int mA;
};
 
struct CTest2 : public QObject {
CTest2( void ) : mB(6) { qDebug() << "construct CTest2"; }
~CTest2( void ) { qDebug() << "destroy CTest2"; }
 
int mB;
};
 
int main(int, char **)
{
if (1) {
QSharedPointer<CTest2> test = AcquireResource<CTest2>();
test->mB = 7;
qDebug() << "test" << test.data() << test->mB;
 
for (int i = 0; i < 2; ++i) {
QSharedPointer<CTest> p = AcquireResource<CTest>();
qDebug() << "p" << p.data() << p->mA;
 
QSharedPointer<CTest2> p2 = AcquireResource<CTest2>();
qDebug() << "p2" << p2.data() << p2->mB;
}
}
 
return 0;
}
 
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #14 : Март 13, 2015, 06:28 »

Не видно, кто в хеш значения кладет.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.249 секунд. Запросов: 22.