Re: Избавится от виртуальности
От: HiSH Россия http://m0riarty.ya.ru
Дата: 01.09.11 05:51
Оценка:
Здравствуйте, _nn_, Вы писали:

__>
__>class ScopeLocker
__>{
__>public:
__>    virtual void Lock() const = 0;
__>    virtual void Unlock() const = 0;
__>};

__>template<typename T>
__>class ConcreteScopeLocker : public ScopeLocker
__>{
__>public:
__>    ConcreteScopeLocker(T& lock) : m_lock(lock)
__>    {
__>        Lock();
__>    }
__>};
__>


При такой реализации БетонногоЛокера смысл виртуальности функций пропадает.
Re[2]: Избавится от виртуальности
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 01.09.11 06:29
Оценка:
Здравствуйте, HiSH, Вы писали:

HSH>При такой реализации БетонногоЛокера смысл виртуальности функций пропадает.


Смысла в виртуальности тут и нет, потому-то и возникло желание от нее избавиться.

Просто _nn_ не хочет указывать тип lockScope, и для этого, как workaround, использует базовый класс и виртуальные функции.
Re[2]: Избавится от виртуальности
От: _nn_ www.nemerleweb.com
Дата: 01.09.11 07:39
Оценка:
Здравствуйте, Andrew S, Вы писали:

__>>Однако Lock/Unlock пришлось сделать виртуальными.

__>>В C++11 можно было бы просто сделать
__>>
__>>auto const& lockScope = LockScope(cs);
__>>

__>>И тогда у lockScope был бы конкретный тип.

__>>А как можно это решить в рамках C++03 ?


AS>В поставленном ключе, на мой взгляд, задача легальным способом силами С++ 03 не решается. Все сводится к необходимости изобрести свой TYPEOF. Что обычно имеет тенденцию не работать на различных полуэкзотических компиляторах.

Придется ускорить переход на C++11

AS>Лучше сделать как в бусте. Тайпдефом — LockObject::ScopedLock.

AS>На первый взгляд это выглядит не сильно лучше ScopedLock<LockObject>, но на деле позволяет писать трейты, не задумываясь о пригодности ScopedLock для данного объекта.
Все равно тип писать нужно, в общем, нужно переходить на С++11.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Избавится от виртуальности
От: Sharpeye Россия  
Дата: 01.09.11 08:33
Оценка:
Здравствуйте, _nn_, Вы писали:

Тут товарищ Александреску рассказывает про ScopeGuard, можно из него (из ScopeGuard) сделать ScopeLock. Исходники тут.
Re: Избавится от виртуальности
От: Erop Россия  
Дата: 01.09.11 15:24
Оценка: 2 (2) +1
Здравствуйте, _nn_, Вы писали:

__>Однако Lock/Unlock пришлось сделать виртуальными.

__>В C++11 можно было бы просто сделать
__>
__>auto const& lockScope = LockScope(cs);
__>

__>И тогда у lockScope был бы конкретный тип.

__>А как можно это решить в рамках C++03 ?


__>P.S.

__>Желательно без BOOST_TYPEOF

Хорошо бы выяснить, точно ли это надо решать.
Дело в том, что например в таком вот коде:
void foo( MyLock& l )
{
    ConcreteScopeLocker<MyLock> locker( l );
    locker.Unlock();
    locker.Lock()
}
нет никаких виртуальных вызовов, так как тип объекта locker заведомо известен компилятору. По идее, и тип временного объекта, время жизни которого мы продлеваем ссылкой, тоже известен компилятору. Было бы странно, если бы компилятор это дело не оптимизировал и делал бы виртуальные вызовы. Хорошо бы проверить, делает он их или нет в каждом конкретном случае...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Избавится от виртуальности
От: Andrew S Россия http://alchemy-lab.com
Дата: 01.09.11 18:18
Оценка:
S>Тут товарищ Александреску рассказывает про ScopeGuard, можно из него (из ScopeGuard) сделать ScopeLock. Исходники тут.

Это совсем не про то.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: Избавится от виртуальности
От: Кодт Россия  
Дата: 02.09.11 06:53
Оценка: 1 (1)
Здравствуйте!

Можно вместо виртуальности использовать указатели на функции.
Плюс — избавляемся от лишних классов (можем хоть на лету конструировать свои скоупгарды на все случаи жизни).
Минусы — просадка в скорости, трудности оптимизатору, трудности отладчику (невнятное содержимое Watch).

typedef function<void()> voidfunc; // boost:: или std::tr1:: по вкусу

class ScopeLocker
{
  mutable voidfunc enter_, leave_;
public:
  ScopeLocker() {}
  ScopeLocker(voidfunc enter, voidfunc leave) { enter(); enter_ = enter; leave_ = leave; } // если в enter исключение, то до присваивания не дойдём
  ScopeLocker(const ScopeLocker& src) { swap(enter_, src.enter_); swap(leave_, src.leave_); } // эстафетное копирование, ибо!
  ~ScopeLocker() { if(leave_) leave_(); }

  void Lock()   { if(enter_) enter_(); }
  void Unlock() { if(leave_) leave_(); }
};

ScopeLocker concreteLock(ConcreteMutex& m) { return ScopeLocker(bind(&ConcreteMutex::Lock,&m), bind(&ConcreteMutex::Unlock,&m)); }

Причём можно под конкретную задачу облегчить устройство этих voidfunc — заменив на шаблонную свободную функцию и указатель
struct voidfunc
{
  void (*f_)(void*);
  void* p_;

  voidfunc() : f_(NULL) {}
  voidfunc(void(*f)(void*), void* p) : f_(f), p_(p) {}

  operator bool() const { return f_; }
  void operator()() const { if(f_) f_(p_); }
};

template<class M> void checked_lock(void* p) { ((M*)p)->Lock(); }
template<class M> void checked_unlock(void* p) { ((M*)p)->Unlock(); }

ScopeLocker concreteLock(ConcreteMutex& m)
{
  return ScopeLocker(
    voidfunc(checked_lock<ConcreteMutex>,&m),
    voidfunc(checked_unlock<ConcreteMutex>,&m)
  );
}



Но вообще, мне кажется плохой затеей вытаскивать пару методов Lock/Unlock наружу. Это шанс для несбалансированного вызова.

Я больше скажу: рекурсивные мьютексы — рассадник зла. Даже в сочетании с условными переменными, делающими unlock на время ожидания. Особенно с условными переменными.
Потому что
— мы делаем unlock и думаем, что больше нет повода для взаимоблокировки (а где-то выше по стеку есть действующий lock)
— мы делаем lock и думаем, что вплоть до следующего unlock никто не вмешается (а где-то в недрах есть unlock-wait-lock)

А избавившись в ScopeLocker от необходимости вытаскивать методы, — возвращаемся к тому самому ad-hoc-auto, которое уже сделано.
Перекуём баги на фичи!
Re[2]: Избавится от виртуальности
От: uzhas Ниоткуда  
Дата: 02.09.11 07:07
Оценка:
Здравствуйте, Кодт, Вы писали:

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

Привет!
К> ScopeLocker(const ScopeLocker& src) { swap(enter_, src.enter_); swap(leave_, src.leave_); } // эстафетное копирование, ибо!
ой боюсь, боюсь!

К>Но вообще, мне кажется плохой затеей вытаскивать пару методов Lock/Unlock наружу. Это шанс для несбалансированного вызова.


К>Я больше скажу: рекурсивные мьютексы — рассадник зла. Даже в сочетании с условными переменными, делающими unlock на время ожидания. Особенно с условными переменными.

К>Потому что
К>- мы делаем unlock и думаем, что больше нет повода для взаимоблокировки (а где-то выше по стеку есть действующий lock)
К>- мы делаем lock и думаем, что вплоть до следующего unlock никто не вмешается (а где-то в недрах есть unlock-wait-lock)
эти "потому что" валидны для случаев, когда руками вызывают Lock\Unlock
если воспитать себя и использовать только AutoLock и только на стеке (см. мой первый ответ), то все гораздо радужнее будет
рекурсивность мьютексов усложняет анализ работы программы, однако вносит дополнительные бенефиты. так что тут палка о двух концах
лично мне рекурсивность еще не портила жизнь, а только упрощала (потому что проги пишу прямолинейные и тормозные )
Re[3]: Избавится от виртуальности
От: Кодт Россия  
Дата: 02.09.11 07:52
Оценка:
Здравствуйте, uzhas, Вы писали:

К>> ScopeLocker(const ScopeLocker& src) { swap(enter_, src.enter_); swap(leave_, src.leave_); } // эстафетное копирование, ибо!

U>ой боюсь, боюсь!

Нечего тут бояться!
Дело в том, что, когда мы ад-хок-автопеременные вводим,
Some x = Some(a,r,g,s);
Some const& y = Some(a,r,g,s);
Some const& z = Derived(a,r,g,s);
Some const& t = evaluated(a,r,g,s);

то, формально, это инициализация копированием
Some x( Some(a,r,g,s) );
Some y( Some(a,r,g,s) );
Derived shadow1( Derived(a,r,g,s) ); Some const& z = shadow1;
Evaluated shadow2(Evaluated(evaluated(a,r,g,s))); Some const& t = shadow2;

Компилятор имеет право сократить лишние конструкторы копирования, но может это и не сделать — особенно, в последнем случае.
Поэтому у нас выбор
— или передавать эстафету владения (права на unlock)
— или делать рекурсивный lock
чтобы количество unlock'ов в деструкторах всех промежуточных объектов совпадало с количеством lock'ов.

Эстафету, опять-таки, можно делать явно (как у меня), или неявно (используя трюки с shared_ptr).


U>эти "потому что" валидны для случаев, когда руками вызывают Lock\Unlock

U>если воспитать себя и использовать только AutoLock и только на стеке (см. мой первый ответ), то все гораздо радужнее будет
U>рекурсивность мьютексов усложняет анализ работы программы, однако вносит дополнительные бенефиты. так что тут палка о двух концах
U>лично мне рекурсивность еще не портила жизнь, а только упрощала (потому что проги пишу прямолинейные и тормозные )

Пока критические секции кода — действительно секции и действительно критические (т.е. маленькие и атомарные), то да.
(Тем более нет нужды вытаскивать наружу методы lock/unlock у scope guard'а!)
А если мы пишем монитор (на паре из мьютекса и условной переменной), в котором встречаются {lock — {unlock-wait-lock} — unlock}, то рекурсия противопоказана.
Перекуём баги на фичи!
Re[4]: Избавится от виртуальности
От: stapter  
Дата: 02.09.11 12:46
Оценка: 1 (1)
С возвращением!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.