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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: C++ lazy initialization  (Прочитано 8615 раз)
ecspertiza
Супер
******
Offline Offline

Сообщений: 1053


С уважением, мастер конфетного цеха!


Просмотр профиля
« : Ноябрь 28, 2017, 19:56 »

Собственно есть несколько вариантов решения, хочется сделать красиво :-)

Вариант 1, первое что нашел на эту тему, не радует то, что по сути это синглтон. Удалить никак
Код:
class TestClass
{
public:
    static TestClass &get() {
        static TestClass instance;
        return instance;
    }
};

Вариант 2, изобретал на коленке
Код:
template <class T>
class Lazy
{
    public:
        T* operator ->() {
            return GetObject();
        }

    private:
        T *GetObject() {
            if (_instance.isNull()) {
                _instance = QSharedPointer<T>::create();
            }

            return _instance.data();
        }

        QSharedPointer<T> _instance;
};

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

Есть мысли как использовать вариативный шаблон отложено в этом случае? Или может быть можно еще как то решить задачу?
Записан
Swa
Самовар
**
Offline Offline

Сообщений: 170


Просмотр профиля
« Ответ #1 : Ноябрь 28, 2017, 22:39 »

Либо создавать объект на куче либо откладывать инициализацию не всего объекта, а только его больших полей.
Записан
ksk-
Самовар
**
Offline Offline

Сообщений: 178



Просмотр профиля
« Ответ #2 : Ноябрь 29, 2017, 05:28 »

Как вариант, можно сохранить параметры конструктора объекта в кортеж, а в методе GetObject() создавать объект из этого кортежа. Но и тут есть минусы. Например, если один и тот же класс имеет несколько конструкторов, то и типы Lazy для них будут различны.
« Последнее редактирование: Ноябрь 29, 2017, 05:30 от ksk- » Записан
ksk-
Самовар
**
Offline Offline

Сообщений: 178



Просмотр профиля
« Ответ #3 : Ноябрь 29, 2017, 05:32 »

Можно также вместо параметров передавать лямбду, которая будет создавать экземпляр объекта.
Записан
ecspertiza
Супер
******
Offline Offline

Сообщений: 1053


С уважением, мастер конфетного цеха!


Просмотр профиля
« Ответ #4 : Ноябрь 29, 2017, 10:07 »

Либо создавать объект на куче либо откладывать инициализацию не всего объекта, а только его больших полей.

Не очень понятно, чем инициализация на куче будет отличаться от шаред поинтера? Кроме того, что мы сами контролируем создание\удаление объекта

Как вариант, можно сохранить параметры конструктора объекта в кортеж, а в методе GetObject() создавать объект из этого кортежа. Но и тут есть минусы. Например, если один и тот же класс имеет несколько конструкторов, то и типы Lazy для них будут различны.

Была такая мысль, но хочется использовать шаблон

Код:
template<typename... Values>

Может быть можно его сохранить в классе ?
Записан
ksk-
Самовар
**
Offline Offline

Сообщений: 178



Просмотр профиля
« Ответ #5 : Ноябрь 29, 2017, 10:45 »

Псевдокод.
Код
C++ (Qt)
template<typename T, typename... Ts>
class Lazy
{
public:
   template<typename... Us>
   explicit Lazy(Us &&... args)
       : args_(std::make_tuple(std::forward<Us>(args)...)) {
   }
 
   T *GetObject() {
       if (!ptr_) {
           ptr_.reset(createObject<T>(args_));
       }
 
       return ptr_.get();
   }
 
private:
   std::tuple<Ts...> args_;
   std::unique_ptr<T> ptr_;
};
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #6 : Ноябрь 29, 2017, 11:08 »

У меня пока получилось так:
Код
C++ (Qt)
#include <functional>
#include <memory>
 
using std::shared_ptr;
using std::make_shared;
using std::function;
using std::bind;
 
template <typename T, typename ...Param>
class Lazy
{
using pointer = shared_ptr<T>;
 
public:
explicit Lazy( Param... p ) :
m_builder( bind( &Lazy::build, this, p... ) )
{
}
 
pointer operator ->()
{
return ptr();
}
 
private:
pointer ptr()
{
if( !m_obj )
m_obj = m_builder();
 
return m_obj;
}
 
pointer build( Param... p )
{
return make_shared<T>( p... );
}
 
function<pointer ()> m_builder;
shared_ptr<T> m_obj;
};
 

К сожалению, сразу не получилось избавиться от перечисления всех типов параметров при описании указателя. Грустный
Код
C++ (Qt)
Lazy<Foo,int> p1( 10 );
cout << p1->a() << endl;
 
Lazy<Foo,int,int> p2( 10, 20 );
cout << p2->a() << endl;
 
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #7 : Ноябрь 29, 2017, 14:21 »

В продолжение к посту Old
Код
C++ (Qt)
template <typename T, typename ...Param>
Lazy<T, Param...> makeLazy(Param... p) {
   return Lazy<T, Param...>(p...);
}
 
Код
C++ (Qt)
   auto p1 = makeLazy<Foo>( 10 );
   cout << p1->a() << p1->b() << endl;
 
   auto p2 = makeLazy<Foo>( 10, 20 );
   cout << p2->a() << p2->b() << endl;
 
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #8 : Ноябрь 29, 2017, 14:27 »

К сожалению, сразу не получилось избавиться от перечисления всех типов параметров при описании указателя. Грустный
Мне казалось, что c++17 научили распознавать параметры шаблонного класса... Но почему-то не сработало...
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #9 : Ноябрь 29, 2017, 17:05 »

Вариант с лямбдой выглядит аккуратней: Улыбающийся
Код
C++ (Qt)
#include <functional>
#include <memory>
 
using std::shared_ptr;
using std::make_shared;
using std::function;
using std::bind;
 
template <typename T, typename ...Param>
class Lazy
{
using pointer = shared_ptr<T>;
 
public:
explicit Lazy( Param... p ) :
m_builder( [=](){ return make_shared<T>( p... ); } )
{
}
 
pointer operator ->()
{
return ptr();
}
 
private:
pointer ptr()
{
if( !m_obj )
m_obj = m_builder();
 
return m_obj;
}
 
function<pointer ()> m_builder;
shared_ptr<T> m_obj;
};
 
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #10 : Ноябрь 29, 2017, 17:31 »

Это всё хорошо, только где и как предлагается использовать классы/объекты типа Lazy? кроме как
... либо откладывать инициализацию не всего объекта, а только его больших полей.
Записан

Пока сам не сделаешь...
ecspertiza
Супер
******
Offline Offline

Сообщений: 1053


С уважением, мастер конфетного цеха!


Просмотр профиля
« Ответ #11 : Ноябрь 29, 2017, 18:44 »

Код:
template<class T>
class Lazy
{
public:
template<typename... Args>
Lazy(Args ... arguments)
: _instance(nullptr)
, _factory([=]()
{
return new T(arguments...);
})
{
}

~Lazy()
{
if (_instance != nullptr)
{
delete _instance;
}
}

T* operator ->()
{
return GetObject();
}

private:
T* GetObject()
{
if (_instance == Q_NULLPTR)
{
_instance = _factory();
}
return _instance;
}

private:
T* _instance;
std::function<T*()> _factory;
};

как то так получилось :-)
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #12 : Ноябрь 29, 2017, 18:51 »

Такая проверка избыточна:
Код
C++ (Qt)
~Lazy()
{
if (_instance != nullptr)
{
delete _instance;
}
}
 

delete nullptr - разрешенная операция. Улыбающийся
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #13 : Ноябрь 30, 2017, 09:38 »

Вариант с лямбдой выглядит аккуратней: Улыбающийся

В продолжение к посту Old
Код
C++ (Qt)
template <typename T, typename ...Param>
Lazy<T, Param...> makeLazy(Param... p) {
   return Lazy<T, Param...>(p...);
}
 
Код
C++ (Qt)
   auto p1 = makeLazy<Foo>( 10 );
   cout << p1->a() << p1->b() << endl;
 
   auto p2 = makeLazy<Foo>( 10, 20 );
   cout << p2->a() << p2->b() << endl;
 

Можно и еще усовершенствовать)

Код
C++ (Qt)
#include <functional>
#include <memory>
 
using std::shared_ptr;
using std::make_shared;
using std::function;
using std::bind;
 
template <typename T>
class Lazy
{
using pointer = shared_ptr<T>;
 
public:
typename < typename ... Param>
explicit Lazy( Param ... p ) :
m_builder( [=](){ return make_shared<T>( p... ); } )
{
}
 
pointer operator ->()
{
return ptr();
}
 
private:
pointer ptr()
{
if( !m_obj )
m_obj = m_builder();
 
return m_obj;
}
 
function<pointer ()> m_builder;
shared_ptr<T> m_obj;
public:
template < typename ... Param>
static Lazy<T> make ( Param ... p)
{
return Lazy<T>( p... );
}
};
 

тогда по коду можно будет использовать код вида

Код
C++ (Qt)
   using LazyFoo = Lazy< Foo >;
 
   LazyFoo p1 = LazyFoo::make( 10 );
   LazyFoo p2 = LazyFoo::make( 10, 20 );
   LazyFoo p3 = p1;
 

и оператор = специальным способом реализовать.


Хотелось бы узнать какая конкретно задача решается? Такая реализация Lazy имеет свои накладные расходы - копирует и запоминает параметры, необходимые для инициализации.
« Последнее редактирование: Ноябрь 30, 2017, 11:09 от ssoft » Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #14 : Ноябрь 30, 2017, 09:56 »

Код
C++ (Qt)
T* GetObject()
{
if (_instance == Q_NULLPTR)
{
_instance = _factory();
}
return _instance;
}
 
Ещё 5 копеек  Веселый
Код
C++ (Qt)
T* GetObject()
{
if (!_instance)
{
_instance = _factory();
}
return _instance;
}
 
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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