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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: [шаблоны] Посоветуйте, можно ли улучшить код  (Прочитано 12249 раз)
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« : Сентябрь 03, 2016, 15:21 »

Всем доброго времени.

Суть: приложение имеет много классов-менеджеров, которые делают однообразную работу:
добавляют, удаляют, подсчитывают кол-во каких-то своих элементов и прочую работу.

Так вот, хотелось бы не плодить кучу реализаций этих менеджеров (не дублировать код
в каждом менеджере по добавлению, удалению элементов и пр.), а как-бы все отшаблонить.

Я не спец-по-шаблонам, получилось как-то так main.cpp:

Код
C++ (Qt)
#include <QCoreApplication>
#include <QList>
#include <QSharedPointer>
#include <QDebug>
 
template <class Manager, typename Element>
class ElementManager : public QObject
{
public:
   explicit ElementManager();
   virtual ~ElementManager();
 
   void addElement(const Element &element);
   void removeElement(const Element &element);
 
   static Manager *instance();
 
private:
   QList<Element> m_elements;
   static Manager *m_instance;
};
 
template <class Manager, typename Element>
Manager *ElementManager<Manager, Element>::m_instance = nullptr;
 
template <class Manager, typename Element>
ElementManager<Manager, Element>::ElementManager()
{
   m_instance = static_cast <Manager*> (this);
}
 
template <class Manager, typename Element>
ElementManager<Manager, Element>::~ElementManager()
{
   m_instance = nullptr;
}
 
template <class Manager, typename Element>
void ElementManager<Manager, Element>::addElement(const Element &element)
{
   m_elements.append(element);
}
 
template <class Manager, typename Element>
void ElementManager<Manager, Element>::removeElement(const Element &element)
{
   m_elements.removeOne(element);
}
 
template <class Manager, typename Element>
Manager *ElementManager<Manager, Element>::instance()
{
   return m_instance;
}
 
class Foo : public QObject
{
   Q_OBJECT
public:
   typedef QSharedPointer<Foo> Ptr;
   explicit Foo() = default;
};
 
class FooManager: public ElementManager<FooManager, Foo::Ptr>
{
   Q_OBJECT
public:
   explicit FooManager() = default;
 
   void do_something() {
       qDebug() << "FooManager: do something";
       emit fooSomething();
   }
 
signals:
   void fooSomething();
};
 
class Bar : public QObject
{
   Q_OBJECT
public:
   typedef QSharedPointer<Bar> Ptr;
   explicit Bar() = default;
};
 
class BarManager: public ElementManager<BarManager, Bar::Ptr>
{
   Q_OBJECT
public:
   explicit BarManager() = default;
 
   void do_something() {
       qDebug() << "BarManager: do something";
       emit barSomething();
   }
 
signals:
   void barSomething();
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   {
       new FooManager;
       auto p = FooManager::instance();
       p->do_something();
   }
 
   {
       new BarManager;
       auto p = BarManager::instance();
       p->do_something();
   }
 
   return a.exec();
}
 
#include "main.moc"
 

Это нормально, или можно как-то улучшить? Улыбающийся

UPD: Еще бы было бы хорошо, если бы можно было добавить сигналы типа elementAdded(Removed) в базовый шаблонный класс ElementManager, но, кажется это невозможно с шаблонами в Qt.
« Последнее редактирование: Сентябрь 03, 2016, 15:37 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Сентябрь 03, 2016, 16:28 »

Неясен ф-ционал самого менеджера. Ну добавили в него объект - и что? Если планируете что-то делать со всеми объектами в менеджере то он просто "контейнер". Логично чтобы клиенты запрашивали у менеджера объект, но никаких намеков на get() не видно.

Удаление по константной ссылке как-то конфузит - у клиента объект имеется, хз сколько этот объект будет жить, а равный ему объект почему-то удаляем из менеджера. Что таким образом хотим получить?

Зачем нужен первый аргумент темплейта (Manager) - не знаю, по-моему этот наворот ни к чему.

Если в качестве объектов планируются шаред пойнтеры, то лучше это в темплейте и объявить, т.е. "только шаред", смешивать толку не будет
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #2 : Сентябрь 03, 2016, 21:45 »

Цитировать
Неясен ф-ционал самого менеджера. Ну добавили в него объект - и что?
но никаких намеков на get() не видно.

Ну, это ж просто пример. естественно, есть там всякие get и прочее. не буду же я рисовать здесь всю подноготную.

Цитировать
Удаление по константной ссылке как-то конфузит - у клиента объект имеется, хз сколько этот объект будет жить

Это просто пример. На самом деле каждый элемент имеет уникальный идентификатор,
 и удаляться оно будет по идентификаторам а не по шаред пойнтерам.

Код
C++ (Qt)
void removeElement(const Id &id);
 

Цитировать
Зачем нужен первый аргумент темплейта (Manager) - не знаю, по-моему этот наворот ни к чему.

А без него работать не будет,например это не будет:

Код
C++ (Qt)
p->do_something();
 

Вот для этого я и задал вопрос. Может можно как-то это завернуть еще в template template... но я хз.
То до чего я додумался - это определить два параметра шаблона.

Цитировать
Если в качестве объектов планируются шаред пойнтеры, то лучше это в темплейте и объявить, т.е. "только шаред", смешивать толку не будет

Могут быть любые объекты.
« Последнее редактирование: Сентябрь 03, 2016, 21:49 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #3 : Сентябрь 03, 2016, 22:07 »

Вот, немного подрихтовал:

Код
C++ (Qt)
#include <QCoreApplication>
#include <QList>
#include <QSharedPointer>
#include <QDebug>
 
#include <algorithm>
 
class IManager : public QObject
{
   Q_OBJECT
signals:
   void elementAdded(const QString &elementId);
   void elementRemoved(const QString &elementId);
 
protected:
   void emitElementAdded(const QString &elementId) {
       emit elementAdded(elementId);
   }
   void emitElementRemoved(const QString &elementId) {
       emit elementRemoved(elementId);
   }
};
 
class IElement : public QObject
{
public:
   explicit IElement(const QString &elementId)
       : m_elementId(elementId)
   {
   }
 
   QString id() const {
       return m_elementId;
   }
 
private:
   QString m_elementId;
};
 
template <class Manager, typename Element>
class ElementManager : public IManager
{
public:
   explicit ElementManager();
   virtual ~ElementManager();
 
   Element element(const QString &elementId) const;
   void addElement(const Element &element);
   void removeElement(const QString &elementId);
 
   static Manager *instance();
 
private:
   QList<Element> m_elements;
   static Manager *m_instance;
};
 
template <class Manager, typename Element>
Manager *ElementManager<Manager, Element>::m_instance = nullptr;
 
template <class Manager, typename Element>
ElementManager<Manager, Element>::ElementManager()
{
   m_instance = static_cast <Manager*> (this);
}
 
template <class Manager, typename Element>
ElementManager<Manager, Element>::~ElementManager()
{
   m_instance = nullptr;
}
 
template <class Manager, typename Element>
Element ElementManager<Manager, Element>::element(const QString &elementId) const
{
   const auto end = m_elements.cend();
   const auto it = std::find_if(m_elements.cbegin(), end, [elementId](Element element) {
           return element->id() == elementId;
   });
 
   return (it == end) ? Element() : *it;
}
 
template <class Manager, typename Element>
void ElementManager<Manager, Element>::addElement(const Element &element)
{
   m_elements.append(element);
   emitElementAdded(element->id());
}
 
template <class Manager, typename Element>
void ElementManager<Manager, Element>::removeElement(const QString &elementId)
{
   auto e = element(elementId);
   if (e) {
       m_elements.removeOne(e);
       emitElementRemoved(e->id());
   }
}
 
template <class Manager, typename Element>
Manager *ElementManager<Manager, Element>::instance()
{
   return m_instance;
}
 
class Foo : public IElement
{
   Q_OBJECT
public:
   typedef QSharedPointer<Foo> Ptr;
   explicit Foo(const QString &id)
       : IElement(id)
   {
   }
};
 
class FooManager: public ElementManager<FooManager, Foo::Ptr>
{
   Q_OBJECT
public:
   explicit FooManager() = default;
 
   void do_something() {
       qDebug() << "FooManager: do something";
       emit fooSomething();
   }
 
signals:
   void fooSomething();
};
 
class Bar : public IElement
{
   Q_OBJECT
public:
   typedef QSharedPointer<Bar> Ptr;
   explicit Bar()
       : IElement(QLatin1String("bar.id"))
   {
   }
};
 
class BarManager: public ElementManager<BarManager, Bar::Ptr>
{
   Q_OBJECT
public:
   explicit BarManager() = default;
 
   void do_something() {
       qDebug() << "BarManager: do something";
       emit barSomething();
   }
 
signals:
   void barSomething();
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   {
       new FooManager;
       auto p = FooManager::instance();
 
       Foo::Ptr foo(new Foo(QLatin1String("foo1.id")));
       p->addElement(foo);
       p->removeElement(QLatin1String("foo1.id"));
       p->do_something();
   }
 
   {
       new BarManager;
       auto p = BarManager::instance();
       p->do_something();
   }
 
   return a.exec();
}
 
#include "main.moc"
 
« Последнее редактирование: Сентябрь 03, 2016, 22:09 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Сентябрь 04, 2016, 10:10 »

Вот для этого я и задал вопрос. Может можно как-то это завернуть еще в template template... но я хз.
То до чего я додумался - это определить два параметра шаблона.
Ага, теперь понял. Насколько я знаю (любителем template никогда не был), то что Вы сделали вполне нормально и называется traits. Можно и без него
Код
C++ (Qt)
class FooManager: public ElementManager<Foo::Ptr>
{
..
 FooManager * instance( void ) { return static_cast<FooManager *> (m_instance); }
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #5 : Сентябрь 04, 2016, 10:58 »

Да, можно и без него, но тогда придётся в каждом экземпляре менеджера писать реализацию получения инстанса и прочие лишние телодвижения.

Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Сентябрь 04, 2016, 14:27 »

Да, можно и без него, но тогда придётся в каждом экземпляре менеджера писать реализацию получения инстанса и прочие лишние телодвижения.
Если появятся "прочие", пока их не видно
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #7 : Сентябрь 04, 2016, 21:03 »

Да, с дополнительным параметром "Manager" появляются проблемы при получении инстанса, если сам объект менеджера был создан в одном модуле (шаред библиотеке), а статический метод instance() вызван из другого модуля (другой шаред библиотеки). В этом случае почему-то возвращается нулевой инстанс... o_O (хотя сам объект создается раньше чем дегается его метод инстанс).

Так что я пока подзабил и сделал как раньше с одним шаблонным параметром "Element".  Улыбающийся

« Последнее редактирование: Сентябрь 04, 2016, 21:05 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Сентябрь 05, 2016, 08:36 »

(хотя сам объект создается раньше чем дегается его метод инстанс).
Присвоение m_instance происходит в конструкторе базового класса, т.е. когда сам Manager еще не сконструирован. Может и будет работать, но как-то это нездорово/чревато
Записан
panAlexey
Гипер активный житель
*****
Offline Offline

Сообщений: 864

Акцио ЗАРПЛАТА!!!!! :(


Просмотр профиля
« Ответ #9 : Сентябрь 07, 2016, 23:51 »

Да, можно и без него, но тогда придётся в каждом экземпляре менеджера писать реализацию получения инстанса и прочие лишние телодвижения.
фабрика?
хотя голова уже не варит.
Записан

Win Xp SP-2, Qt4.3.4/MinGW. http://trdm.1gb.ru/
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #10 : Сентябрь 12, 2016, 14:17 »

kuzulis, быть может в модуле сделать функции, которые будут возвращать указатель на статику?
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #11 : Сентябрь 12, 2016, 15:57 »

kuzulis, взгляните на это:
Код
C++ (Qt)
#include <QObject>
#include <QVector>
#include <QtDebug>
#include <memory>
 
class IManager{
public:
   virtual void doSomething() = 0;
   virtual ~IManager(){
   }
};
 
class AbstractManager: public QObject, public IManager{
   Q_OBJECT
 
signals:
   void elementAdded(const QString &elementId);
   void elementRemoved(const QString &elementId);
};
 
 
template<typename T>
struct Element{
   QString id;
   T value;
};
 
 
template<class T>
class ElementManager: public AbstractManager{
   using ElementType = Element<T>;
   using Container = QVector<ElementType>;
public:
   ElementManager(){
       if (instance_){
           qCritical() << "Overwritting ElementManager global instance";
       }
 
       instance_ = this;
   }
 
   virtual ~ElementManager() override {
       instance_ = nullptr;
   }
 
   void addElement(const ElementType &element){
       elements_ << element;
       emit elementAdded(element.id);
   }
 
   void removeElement(const QString &id){
       typename Container::iterator it = find(id);
       elements_.erase(it);
       emit elementRemoved(id);
   }
 
   ElementType element(const QString &id) const{
       return *find(id);
   }
 
   static IManager *instance(){
       return instance_;
   }
 
private:
   typename Container::iterator find(const QString &id){
       for (typename Container::iterator it = elements_.begin(), end = elements_.end(); it != end; ++it){
           if (it->id == id){
               return it;
           }
       }
       return  elements_.end();
   }
 
private:
   static IManager *instance_;
   Container elements_;
};
 
template<typename T>
IManager *ElementManager<T>::instance_ = nullptr;
 
template<typename T>
IManager *instance(){
   return ElementManager<T>::instance();
}
 
class IntManager: public ElementManager<int>{
public:
   IntManager(){
   }
   virtual ~IntManager() override{
   }
 
   virtual void doSomething() override{
       qDebug() << "IntManager doing something";
   }
};
 
class StringManager: public ElementManager<QString>{
public:
   StringManager(){
   }
   virtual ~StringManager() override{
   }
 
   virtual void doSomething() override{
       qDebug() << "StringManager doing something";
   }
};
 
 
int main()
{
   using IntElement = Element<int>;
   using StringElement = Element<QString>;
 
   IntElement intElement;
   intElement.id = "int.id";
   intElement.value = 7;
 
   IntManager intManager;
   QObject::connect(&intManager, &IntManager::elementAdded, [=](const QString &id){qDebug() << "Element with id" << id << "added to int manager";});
   QObject::connect(&intManager, &IntManager::elementRemoved, [=](const QString &id){qDebug() << "Element with id" << id << "removed from int manager";});
   intManager.addElement(intElement);
 
   StringElement stringElement;
   stringElement.id = "string.id";
   stringElement.value = "Qt";
 
   StringManager stringManager;
   stringManager.addElement(stringElement);
 
   std::array<IManager *, 2> intances{{
       instance<int>(),
       instance<QString>()
   }};
 
   for (IManager *instance: intances){
       instance->doSomething();
   }
 
 
   return 0;
}
 
#include "main.moc"
 

Я пытался оформить instance_ как std::unique_ptr, но тогда происходил вылет, видать, нельзя делать unique статичным.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #12 : Сентябрь 12, 2016, 19:30 »

Кстати, а разве оно должно компилиться? Ведь с "emit elementRemoved(id);" в шаблонном классе оно не должно компилиться. Не?
Записан

ArchLinux x86_64 / Win10 64 bit
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #13 : Сентябрь 12, 2016, 19:36 »

Я копипастил из QtCreator, код работает.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #14 : Сентябрь 12, 2016, 19:54 »

Код
C++ (Qt)
ElementType element(const QString &id) const{
       return *find(id);
   }
Тут проверку забыл написать
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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