Russian Qt Forum
Апреля 03, 2025, 23:50 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: C++ lazy initialization  (Прочитано 8877 раз)
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.1 секунд. Запросов: 20.