Russian Qt Forum

Qt => Кладовая готовых решений => Тема начата: SASA от Август 24, 2010, 09:11



Название: Класс автоматического взведения и сброса флага
Отправлено: SASA от Август 24, 2010, 09:11
Код:
#ifndef FLAGASSISTANT_H
#define FLAGASSISTANT_H

/// Дефайн для быстрого взведения флага спомощью класса CFlagAssistant.
#define COCKFLAG(flagName) CFlagAssistant flagAssistant(&flagName);

/*! \brief Класс автоматического взведения и сброса флага.
* \author SASA.
* \date 2010/08/18
*/
class CFlagAssistant
{
public:
/*! \brief Конструктор.
* \param[in] _flag (\c bool *) - Указатель на флаг, за которым следим.
*/
CFlagAssistant(bool * _flag)
{
m_flag = _flag;
if(m_flag) *m_flag = true;
}
~CFlagAssistant()
{
if(m_flag) *m_flag = false;
}
protected:
bool * m_flag; ///< Флаг за которым следим.
};

#endif // FLAGASSISTANT_H

Использование.

Код:
class MyClass
{
public:
MyClass()
{
m_flag = false;
}
/*! Пока исполняется этот метод, флаг должен быть взведённым.
* После выхода из метода, флаг должен сброситься.
*/
void metod()
{
COCKFLAG(m_flag);
много кода c несколькими ретурнами;
}
protected:
m_flag;
};

Самое главное, что изменив код метода (добавив ещё один ретурн) нам не надо писать строку m_flag = false; А про неё так легко забыть :) А есть ещё исключения, по которым функция может прерваться где угодно. Класс позволяет легко сохранить условие, описанное в комментарии.

З.Ы. О валидности указателя надо заботиться самим. Буду рад услышать предложения о повышении безопастности


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: spectre71 от Сентябрь 04, 2010, 23:32
А если флаг был == true, что получится при выходе :)

И вообще интереснее установка флага в конкретное значение (true/false)


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: SASA от Сентябрь 05, 2010, 13:24
А если флаг был == true, что получится при выходе :)

И вообще интереснее установка флага в конкретное значение (true/false)
Что Вы имеете в виду.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: SABROG от Сентябрь 05, 2010, 18:52
Что Вы имеете в виду.

Вероятно то, что у тебя нигде флаг не устанавливается в 0. Вроде как стандарт C++ инициализирует нулями только POD типы и не факт, что класс является POD типом.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: pastor от Сентябрь 06, 2010, 00:04
... не факт, что класс является POD типом.

Данный класс (CFlagAssistant) точно не является POD типом.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Denjs от Сентябрь 06, 2010, 00:25
гм... можно "человеческими" словами описать за какой надобностью данный класс нужен?
ну т.е. бизнес-профит? бизнес-задача или системная задача которую решает данный класс? а то как-то я затрудныюсь понять зачем мне например данный класс может быть полезен? за каком флагом мы следим? зачем его быстро возводим? и т.п.... ? ???

UPD: через некоторое время разбора кода понял, что класс предназначен для "автоматического" сброса|устновки значения переменной по выходу из процедуры или участка кода в "{}"  - что бы не восстанавливать старое значение руками.
да?

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

Заранее спасибо.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: SASA от Сентябрь 06, 2010, 17:39
да?
Да.
По-моему я всё опсиал. Класс простенький - описание простенькое.

Логика работы аналогична QMutexLocker.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Igors от Сентябрь 07, 2010, 12:19
У меня есть такой же класс, называется CAutoFlag, (правда указатель но ноль я не проверяю). Штучка малюсенькая но удобная. Без нее получается коряво, напр.

Код
C++ (Qt)
void MyClass::CalcSomething( void )
{
mPortalActive = true;
if (!Check1()) {
 mPortalActive = false;
 return;
}
...
if (!Check2()) {
 mPortalActive = false;
 return;
}
...
 mPortalActive = false;
}
 
С классом приятнее
Код
C++ (Qt)
void MyClass::CalcSomething( void )
{
CAutoFlag theFlag(&mPortalActive);
if (!Check1())
 return;
...
if (!Check2())
 return;
...
}
 


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: navrocky от Сентябрь 08, 2010, 19:13
Обощим идею ;) Пример использования в каментах ниже

Код
C++ (Qt)
#pragma once
 
/*! Use this scoped class to temporarely change some value and restore old value
   when scope out.
 
\code
bool block;
{
    scoped_value_change<bool> s(block, true, block);
    ...
}
...
std::string status;
{
    scoped_value_change<std::string> s(flag, "entered", "leaved");
    ...
}
 
\endcode
*/

template <typename T>
class scoped_value_change
{
public:
   scoped_value_change(T& value, const T& in, const T& out)
   : value_(value), out_(out)
   {
       value_ = in;
   }
 
   ~scoped_value_change()
   {
       value_ = out_;
   }
private:
   T& value_;
   T out_;
};
 


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: spectre71 от Сентябрь 09, 2010, 10:14
А если флаг был == true, что получится при выходе :)

И вообще интереснее установка флага в конкретное значение (true/false)
Что Вы имеете в виду.

bool MyFlg1 = true;
bool MyFlg2 = false;

{
  COCKFLAG(MyFlg1);
  COCKFLAG(MyFlg2);

//MyFlg1 == true;
//MyFlg2 == true;
...
...

}

//MyFlg1 == false;
//MyFlg2 == false;


//MyFlg1 должен быть равен true !!!



Название: Re: Класс автоматического взведения и сброса флага
Отправлено: navrocky от Сентябрь 09, 2010, 10:55
//MyFlg1 должен быть равен true !!!

В моем варианте это решено:
Код:
bool my_flag = true;
{
    scoped_value_change<bool> sv(my_flag, false, my_flag);
    // my_flag = false
}
// my_flag = true


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: SASA от Сентябрь 09, 2010, 14:04
Обощим идею ;)

Мне нравится. Но я бы добавил макрос типа COCKFLAG.
Почему он нужен? Потому что две следущие строки очень похожи, а поведение прямо противоволожное.

Код:
scoped_value_change<bool> s(block, true, block);

Код:
scoped_value_change<bool>(block, true, block);

И по смыслу более логично. Я хочу написать строчку со смыслом "Взведи флаг, который автоматом сброситься". А не "Заведи переменную, которая мне потом не нужна, которая взведет флаг и потом его сбросит".




Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Akon от Сентябрь 09, 2010, 15:31
Обощим идею ;) Пример использования в каментах ниже

Код
C++ (Qt)
#pragma once
 
/*! Use this scoped class to temporarely change some value and restore old value
   when scope out.
 
\code
bool block;
{
    scoped_value_change<bool> s(block, true, block);
    ...
}
...
std::string status;
{
    scoped_value_change<std::string> s(flag, "entered", "leaved");
    ...
}
 
\endcode
*/

template <typename T>
class scoped_value_change
{
public:
   scoped_value_change(T& value, const T& in, const T& out)
   : value_(value), out_(out)
   {
       value_ = in;
   }
 
   ~scoped_value_change()
   {
       value_ = out_;
   }
private:
   T& value_;
   T out_;
};
 

Более-менее окончательный вариант.
Я бы добавил еще простой конструктор (частный случай), оставляющий исходное значение:
Код:
scoped_value_change(T& value, const T& out)
: value_(value), out_(out)
{
}

Ну и оптимизация. Что лучше: 1) const T& out или 2) T out - зависит от Т. boost::call_traits<T>::param_type - будет передавать либо по ссылке, либо по значению (истинное обобщение  :)).   


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: navrocky от Сентябрь 09, 2010, 15:33
Цитировать
Мне нравится. Но я бы добавил макрос типа COCKFLAG.
Согласен, на базе этого можно наплодить дефайнов, упрощающих часто используемые вызовы.

Цитировать
Я бы добавил еще простой конструктор (частный случай), оставляющий исходное значение:
Да!

Цитировать
boost::call_traits
Таки тоже да!

Результат:
Код
C++ (Qt)
#pragma once
 
#include <boost/call_traits.hpp>
 
/*! Use this scoped class to temporarely change some value and restore old value
   when scope out.
 
\code
bool flag;
{
    scoped_value_change<bool> s(flag, true, flag);
    ...
    // or
    scoped_value_change<bool> s(flag, true);
    ...
 
    // or even
    SCOPE_COCK_FLAG(flag);
    ...
}
\endcode
*/

 
/*! Temporarely set a flag to true and return old flag value when the scope is out.*/
#define SCOPE_COCK_FLAG(flag) scoped_value_change<bool> __sv_##flag(flag, true)
 
/*! Temporarely set a flag to false and return old flag value when the scope is out.*/
#define SCOPE_RESET_FLAG(flag) scoped_value_change<bool> __sv_##flag(flag, false)
 
template <typename T>
class scoped_value_change
{
public:
   typedef typename boost::call_traits<T>::reference reference;
   typedef typename boost::call_traits<T>::param_type param;
 
   scoped_value_change(reference value, param in)
   : value_(value), out_(value)
   {
       value_ = in;
   }
 
   scoped_value_change(reference value, param in, param out)
   : value_(value), out_(out)
   {
       value_ = in;
   }
 
   ~scoped_value_change()
   {
       value_ = out_;
   }
private:
   reference value_;
   T out_;
};
 


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Авварон от Сентябрь 09, 2010, 16:04
не ребят, у вас явно уже ум за разум заходит... больше буста


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: navrocky от Сентябрь 09, 2010, 16:12
кыш на С программировать  ;)


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Igors от Сентябрь 10, 2010, 12:13
Конечно template привлекателен своей универсальностью/общностью. Но в то же время он затрудняет понимание, при использовании приходится тратить усилия и время чтобы вспомнить. Я лично ни разу не сталкивался с необходимость авто-сброса никакой др. переменной кроме булевской. Как и с ситуацией "флаг уже установлен". Делать общее решение "на всякий случай" мне кажется неэффективным, оно должно созреть. вызываться практикой


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: ритт от Сентябрь 14, 2010, 20:11
на данную тему - вот у нас имеется реальный пример автовзвода/сброса флага:
Код:
class AutoTransaction
{
public:
    AutoTransaction(QSqlDatabase db) : m_success(true), m_db(db)
    {
        m_db.transaction();
    }

    ~AutoTransaction()
    {
        if (m_success)
            m_db.commit();
        else
            m_db.rollback();
    }

    void setSuccess(bool success)
    {
        m_success = success;
    }

private:
    bool m_success;
    QSqlDatabase m_db;
};

#define EXEC(db, method)   \
    AutoTransaction atr(db); \
    try {                            \
        method;                   \
    } catch(...) {                \
        atr.setSuccess(false); \
        throw;                      \
    }

конечно, эти несколько строк можно было бы и накопипастить везде, где требуется, но так гораздо удобнее, а из-за инлайнов разницы в производительности 0...


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Barmaglodd от Сентябрь 15, 2010, 09:49
Зачем try-catch?
Код:
class AutoTransaction
{
public:
    AutoTransaction(QSqlDatabase db) : m_success(false), m_db(db)
    {
        m_db.transaction();
    }

    ~AutoTransaction()
    {
        if (m_success)
            m_db.commit();
        else
            m_db.rollback();
    }

    void dismiss()    {
        m_success = true;
    }

private:
    bool m_success;
    QSqlDatabase m_db;
};

#define EXEC(db, method)   \
    AutoTransaction atr(db); \
    method;                   \
    atr.dismiss();


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Akon от Сентябрь 15, 2010, 10:09
Присоединяюсь. atr уже не будет доступен при перебросе исключения.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: BRE от Сентябрь 15, 2010, 10:18
atr уже не будет доступен при перебросе исключения.
Почему?


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Akon от Сентябрь 15, 2010, 11:42
Сорри, не совсем понял логику работы.
Тогда лучше вариант Barmaglodd - нет лишнего переброса исключения. А еще лучше scoped-вариант - чтоб после EXEC все реально закончилось.

Код:
#define EXEC(db, method)   \
{ \
    AutoTransaction atr(db); \
    method;                   \
    atr.dismiss();
}


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: BRE от Сентябрь 15, 2010, 11:49
Я думаю это специфика проекта, что бы не отпускать исключения вверх.



Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Akon от Сентябрь 15, 2010, 12:03
Во всех представленных здесь вариантах исключения прорываются наверх. Так в общем то и задумано, я полагаю; главное, что деструктор AutoTransaction всегда будет вызван. Короче RAII.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: navrocky от Сентябрь 15, 2010, 12:06
Интересно, а почему метод по смыслу commit назван dismiss? %)


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Akon от Сентябрь 15, 2010, 12:22
Спроси у Barmaglodd. В своем посту я скопипастил.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: SASA от Сентябрь 15, 2010, 17:00
Тепрь я понимаю, как тележка превратилась в БилАз. ;D


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: ритт от Сентябрь 15, 2010, 21:05
Зачем try-catch?

если method; выбросит исключение, автоматом откатим транзакцию.
это специфический для приложения макрос, т.к. все ошибки работы с бд, сетью и т.д. выбрасываются в виде исключений...

а вот
Код:
    AutoTransaction atr(db); \
    method;                   \
    atr.dismiss();
это вообще какой-то бред. зачем откатывать, если код отработал успешно?


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: navrocky от Сентябрь 15, 2010, 21:56
Бармаглот вообще выдал более красивое решение, только с названием метода ступил...

В деструкторе стекового объекта производится автоматический откат транзакции, если не был вызван метод commit. При исключении commit не будет вызван. Вот и всё, никаких try/catch/rethrow.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Barmaglodd от Сентябрь 16, 2010, 11:02
это вообще какой-то бред. зачем откатывать, если код отработал успешно?
А внимательно код почитать?

dismiss, т.к. то, что здесь написано, частный случай ScopeGuard (http://www.drdobbs.com/184403758). Ну и потом, никто же не запрещает изменить название метода :)


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: Akon от Сентябрь 16, 2010, 14:07
Функционально эквивалентный вариант, лишенный недостатков, указанных в разделе Solution 1: Brute Force ScopeGuard (http://www.drdobbs.com/184403758):
Цитировать
The code works, but at the cost of increased size and clumsiness. The two-liner just became a ten-liner. This technique isn't appealing; imagine littering all of your code with such try-catch statements.

Moreover, this technique doesn't scale well. Imagine you have a third operation to do. In that case, things suddenly become much clumsier. You can choose between equally awkward solutions: nested try statements or a more complicated control flow featuring additional flags. These solutions raise code bloating issues, efficiency issues, and, most important, severe understandability and maintenance issues.

Код:
class Method
{
public:
virtual void operator()() const = 0;
};

class MyMethod : public Method
{
public:
MyMethod(/*params*/);
virtual void operator()() const { /*does real actions*/ }
private:
/*fields for params*/
}

void exec(QSqlDatabase& db, const Method& method)
{
db.transaction();
try {
  method();  // throws at error
  sqlDatabase->commit();
}
catch (...) {
  sqlDatabase->rollback();
  throw;
}
}

// Использование:
...
QSqlDataBase db(...);
MyMethod method(/*params*/);
exec(db, method);
...

Указанный вариант соотносится со ScopeGuard-вариантом примерно также, как std::for_each c непосредственным циклом. Вообщем, вопрос предпочтений.


Название: Re: Класс автоматического взведения и сброса флага
Отправлено: SABROG от Декабрь 10, 2010, 15:20
А тролли внутри Qt используют класс QBoolBlocker из qobject_p.h. Жаль не сделали его публичным.