Практика использования std::unique_ptr std::call_once std::once_flag
От: xobotik Россия  
Дата: 19.11.15 15:15
Оценка:
Здравствуйте!
Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:
template<typename T>
class ResourceCreator {
private:
    std::unique_ptr<T> _resource;
    std::once_flag _initFlag;
public:
    void Init() {
        std::call_once(_initFlag, [&]() { _resource.reset(new T()); });
    }
    std::unique_ptr<T> &getResource() {
        return _resource;
    }
    ~ResourceCreator() {
        _resource.release();
    }
};

template<typename T>
class ResourceCreatorArgs {
private:
    std::unique_ptr<T> _resource;
    std::once_flag _initFlag;
public:
    template<typename... Args>
    void Init(Args... constructorArgs) {
        std::call_once(_initFlag, [&]() { _resource.reset(new T(std::forward<Args>(constructorArgs)...)); });
    }
    std::unique_ptr<T> &getResource() {
        return _resource;
    }
    ~ResourceCreatorArgs() {
        _resource.release();
    }
};

template<typename T> 
class ResourceCreatorAndOnceRun {
private:
    ResourceCreator<T> _creator;
    std::once_flag _onceRun;
public:
    void Init(std::function<void()> func) {
        _creator.Init();
        std::call_once(_onceRun, func);
    }
    std::unique_ptr<T> &getResource() {
        return _creator.getResource(); 
    }
};

template<typename T>
class ResourceCreatorArgsAndOnceRun {
private:
    ResourceCreatorArgs<T> _creator;
    std::once_flag _onceRun;
public:
    void Init(std::function<void()> func, Args... constructorArgs) {
        _creator.Init(constructorArgs...);
        std::call_once(_onceRun, func);
    }
    std::unique_ptr<T> &getResource() {
        return _creator.getResource(); 
    }
};


Сценарий использования этих велосипедов:
namespace {
    ResourceCreatorArgsAndOnceRun<Loger> _loger;
}

inline void initLogging() {
    std::string fileName = getLogFileName(); // имя файла содержит время старта программы
    _loger.InitAndRun([&]() { createLogFile(fileName); }, fileName);
}

#define TO_LOG(text) \
    _loger.getResource().get()->save(LogEntry(__FILE__, __PRETTY_FUNCTION__, __LINE__, (text)));


P.S. Если код не очень, скажите пожалуйста, где косяки =)
Заранее спасибо!
С уважением!
std::call_once std::once_flag std::unique_ptr
Re: Практика использования std::unique_ptr std::call_once std::once_flag
От: watchmaker  
Дата: 19.11.15 15:43
Оценка: 2 (1)
Здравствуйте, xobotik, Вы писали:

X> ~ResourceCreator() {

X> _resource.release();
X> }
А зачем искусственно создавать утечку?

X> std::unique_ptr<T> &getResource() {

X> return _resource;
X> }
А зачем возвращать ссылку на unique_ptr? Чтобы кто-нибудь другой также имел возможность вызвать .release() и создать утечку? :)
Re: Практика использования std::unique_ptr std::call_once std::once_flag
От: Vamp Россия  
Дата: 19.11.15 15:55
Оценка:
X>Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:
Зачем все это? Выглядит ужасно. Я уже не говорю о возвращении неконстантной ссылки на член — можешь просто его сделать public. Будет то же самое, но проще.
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Практика использования std::unique_ptr std::call_once std::once_flag
От: xobotik Россия  
Дата: 19.11.15 16:16
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, xobotik, Вы писали:


X>> ~ResourceCreator() {

X>> _resource.release();
X>> }
W>А зачем искусственно создавать утечку?

Верно! Просмотрел этот момент!

X>> std::unique_ptr<T> &getResource() {

X>> return _resource;
X>> }
W>А зачем возвращать ссылку на unique_ptr? Чтобы кто-нибудь другой также имел возможность вызвать .release() и создать утечку?

Верно не зачем. Кого-то другого нет, сам себе злобный буратино =)
Исправил на:
T &getResource() const {
    return (*_resource.get());
}
С уважением!
Re[2]: Практика использования std::unique_ptr std::call_once std::once_flag
От: xobotik Россия  
Дата: 19.11.15 16:19
Оценка:
Здравствуйте, Vamp, Вы писали:

X>>Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:

V>Зачем все это? Выглядит ужасно. Я уже не говорю о возвращении неконстантной ссылки на член — можешь просто его сделать public. Будет то же самое, но проще.

Нужно создать экземпляр класса один раз, вызвать функцию один раз.
Посмотрите Сценарий использования, подробно расписал все там.
С уважением!
Re: Практика использования std::unique_ptr std::call_once std::once_flag
От: uzhas Ниоткуда  
Дата: 20.11.15 17:05
Оценка: 2 (1)
Здравствуйте, xobotik, Вы писали:

X>Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:

не плохо

X>P.S. Если код не очень, скажите пожалуйста, где косяки =)

  1. _loger — опечатка в имени переменной
  2. почему эта переменная в анонимном неймспейсе? вы хотите на каждую TU по своему логгеру? если же логгер один, то нужно иметь в виду порядок инициализации глобальных переменных, кто-то в другом классе может захотеть в конструкторе пологировать, а логгер еще не создан
  3. пробрасывать аргументы надо так:
    template<typename... Args>
    void Init(Args&&... constructorArgs) {
     std::call_once(_initFlag, [&]() { _resource.reset(new T(std::forward<Args>(constructorArgs)...)); });
    }
Re[3]: Практика использования std::unique_ptr std::call_once std::once_flag
От: antropolog  
Дата: 22.11.15 12:42
Оценка: :)
Здравствуйте, xobotik, Вы писали:

X>Нужно создать экземпляр класса один раз, вызвать функцию один раз.


Создать экземпляр класса:
auto obj = new Class(params);


вызвать функцию один раз:
obj->method(args);


X>Посмотрите Сценарий использования, подробно расписал все там.

пардон, но у вас там ООП головного мозга. Зачем делать через ж-у то, что можно сделать прямо и очевидным образом?
Re[4]: Практика использования std::unique_ptr std::call_once std::once_flag
От: uzhas Ниоткуда  
Дата: 22.11.15 19:31
Оценка:
Здравствуйте, antropolog, Вы писали:

A>можно сделать прямо и очевидным образом?


— Видишь суслика синглтон?
— Нет!
— И я не вижу. А он есть!
Re[4]: Практика использования std::unique_ptr std::call_once std::once_flag
От: xobotik Россия  
Дата: 23.11.15 12:11
Оценка:
Здравствуйте, antropolog, Вы писали:

A>Здравствуйте, xobotik, Вы писали:


X>>Нужно создать экземпляр класса один раз, вызвать функцию один раз.


A>Создать экземпляр класса:

A>
A>auto obj = new Class(params);
A>


A>вызвать функцию один раз:

A>
obj->>method(args);
A>


X>>Посмотрите Сценарий использования, подробно расписал все там.

A>пардон, но у вас там ООП головного мозга. Зачем делать через ж-у то, что можно сделать прямо и очевидным образом?

КО? И каждый раз при записи в лог создавать экземпляр класса и вызывать функцию?
Вроде написал сценарий использования или напрасно ? ) Имя файла лога, должно содержать временную метку начала работы программы.
С уважением!
Re[2]: Практика использования std::unique_ptr std::call_once
От: xobotik Россия  
Дата: 23.11.15 12:14
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, xobotik, Вы писали:


X>>Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:

U>не плохо

X>>P.S. Если код не очень, скажите пожалуйста, где косяки =)

U>

    U>
  1. _loger — опечатка в имени переменной
    U>
  2. почему эта переменная в анонимном неймспейсе? вы хотите на каждую TU по своему логгеру? если же логгер один, то нужно иметь в виду порядок инициализации глобальных переменных, кто-то в другом классе может захотеть в конструкторе пологировать, а логгер еще не создан
    U>
  3. пробрасывать аргументы надо так:
    U>
    U>template<typename... Args>
    U>void Init(Args&&... constructorArgs) {
    U> std::call_once(_initFlag, [&]() { _resource.reset(new T(std::forward<Args>(constructorArgs)...)); });
    U>}
    U>

    U>

Что такое TU (включение заголовка)? Логгер один. Как тогда вы считаете нужно реализовать?

Ок, спасибо =)
С уважением!
Отредактировано 23.11.2015 12:14 xobotik . Предыдущая версия .
Re: Практика использования std::unique_ptr std::call_once std::once_flag
От: sokel Россия  
Дата: 23.11.15 15:40
Оценка: 3 (1)
Здравствуйте, xobotik, Вы писали:

X>Здравствуйте!

X>Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:

Это синглтон, передача аргументов здесь не нужна, т.к. судя по использованию getLogFileName, createLogFile глобальные функции — их можно и в конструкторе вызвать.
call_once тут намекает на многопоточную инициализацию. Если компилятор поддерживает magic static, всё до безобразия просто:
template<typename T>
class singleton {
public:
    static T& instance() {
        static T obj;
        return obj;
    }
};
...
Logger& logger = singleton<Logger>::instance();


Если же magic static нет, можно поизвращаться с DCLP:
template<typename T>
class singleton {
    struct spin_lock_quard {
        spin_lock_quard(std::atomic_flag& l) : mtx(l) {
            while(mtx.test_and_set(std::memory_order_acquire));
        }
        ~spin_lock_quard() {
            mtx.clear(std::memory_order_release);
        }
        std::atomic_flag& mtx;
    };
public:
    static T& instance() {
        (void)releaser;
        T* tmp = obj.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);
        if(tmp == nullptr) {
            spin_lock_quard lock(mtx);
            tmp = obj.load(std::memory_order_relaxed);
            if(tmp == nullptr) {
                tmp = new T;
                std::atomic_thread_fence(std::memory_order_release);
                obj.store(tmp, std::memory_order_relaxed);
            }
        }
        return *tmp;
    }
private:
    static void release() {
        spin_lock_quard lock(mtx);
        delete obj.load();
        obj.store(nullptr);
    }
    static std::atomic<T*> obj;
    static std::atomic_flag mtx;
    static struct release_guard {
        ~release_guard() { singleton::release(); }
    } releaser;
};
template<typename T> std::atomic<T*> singleton<T>::obj = ATOMIC_VAR_INIT(0);
template<typename T> std::atomic_flag singleton<T>::mtx = ATOMIC_FLAG_INIT;
template<typename T> typename singleton<T>::release_guard singleton<T>::releaser;


Ну и в обеих случаях дрянь при использовании singleton в глобальных деструкторах (читай — в других singleton объектах). Тут уже либо феникс либо вообще без release.
Правильней будет создать и инициализировать логгер явно и передать его по ссылке в конструкторы всем кто будет его использовать, явно обеспечив ему большее время жизни (внешний скоп по отношению к использующим).
Re[2]: Практика использования std::unique_ptr std::call_once std::once_flag
От: andyp  
Дата: 23.11.15 16:30
Оценка:
Здравствуйте, sokel, Вы писали:

offtopic:
А компиляторы, поддерживающие call_once, но не поддерживающие magic static в природе бывают? Вроде и то и то — фишки С++ 11.
Re[3]: Практика использования std::unique_ptr std::call_once std::once_flag
От: sokel Россия  
Дата: 23.11.15 23:44
Оценка: 4 (1)
Здравствуйте, andyp, Вы писали:

A>offtopic:

A>А компиляторы, поддерживающие call_once, но не поддерживающие magic static в природе бывают?
Visual Studio например, call_once начиная 2012 а magic statics только в 2015.

A>Вроде и то и то — фишки С++ 11.

Ну тот же pthread_once задолго до magic statics появился, call_once просто его адаптация под стандарт.
Re[2]: Практика использования std::unique_ptr std::call_once std::once_flag
От: xobotik Россия  
Дата: 24.11.15 10:27
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, xobotik, Вы писали:


X>>Здравствуйте!

X>>Подскажите пожалуйста, плохо ли использовать (и почему плохо) std::unique_ptr , std::call_once и std::once_flag в следующем контексте:

S>Это синглтон, передача аргументов здесь не нужна, т.к. судя по использованию getLogFileName, createLogFile глобальные функции — их можно и в конструкторе вызвать.

S>call_once тут намекает на многопоточную инициализацию. Если компилятор поддерживает magic static, всё до безобразия просто:

Ну собственно std::call_once и использовал, чтобы была гарантия одной инициализации, даже если кто-то вызовет в разных потоках дважды инициализацию. Разве magic static дает нам такую возможность?
С уважением!
Re[5]: Практика использования std::unique_ptr std::call_once std::once_flag
От: antropolog  
Дата: 02.12.15 07:19
Оценка:
Здравствуйте, xobotik, Вы писали:

X>КО? И каждый раз при записи в лог создавать экземпляр класса и вызывать функцию?

X>Вроде написал сценарий использования или напрасно ?

зачем все эти извращения с недосинглтонами, гонками и прочей ахинеей, когда можно сделать просто:

//loggger.h
//Интерфейс лога, подключается там, где нужно писать в лог
log(severety, message);


//logger_factory.h
//Интерфейс фабрики логгера, подключается только в одном файле - в main.cpp
initLog(filename)
deinitLog();


//logger.cpp
//Имплементация логгера
#include "loggger.h"
#include "logger_factory.h"


class Log {
  open(filename);
  close();
  writeToLog(severety, message);
};

Log global_log;

initLog(filename) {
  global_log.init(filename);
}

deinitLog(
  global_log.close();
}

log(severity, message) {
  global_log.writeToLog(severity, message);
}


и наконец main

#include "logger_factory.h"
main() {
  initLog( get_new_log_name() );
  ....
  deinitLog();
}


кому нужен лог — подключает logger.h и пишет в лог. Логгер инициализируется один раз явно в main. Код понятен даже ребёнку. Работает стабильно. Ошибиться очень тяжело. Повторяю вопрос — "Зачем делать через ж-у то, что можно сделать прямо и очевидным образом?"
Re[6]: Практика использования std::unique_ptr std::call_once
От: xobotik Россия  
Дата: 03.12.15 08:47
Оценка: +1
Здравствуйте, antropolog, Вы писали:

A>кому нужен лог — подключает logger.h и пишет в лог. Логгер инициализируется один раз явно в main. Код понятен даже ребёнку. Работает стабильно. Ошибиться очень тяжело. Повторяю вопрос — "Зачем делать через ж-у то, что можно сделать прямо и очевидным образом?"


Сделать можно, только использовать его могут на стороне через ж-у и не очевидным способом. Поэтому хотелось сделать гарантированную разовую инициализацию.
С уважением!
Отредактировано 10.12.2015 1:05 kaa.python . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.