C++ и RAII
От: Erop Россия  
Дата: 12.10.12 15:46
Оценка: 1 (1) -3
Из внезапно возникшего тут обсуждения, что есть истинный Бог христианскийRAII я понял, что многие коллеги считают, что RAII в С++ является просто какой-то серебрянной пулей, и что всеми ресурсами нужно управлять так и только так.

Конечно я не против, а наоборот, всеми руками за RAII, и в таких штуках, как локеры или, например, умные указатели, парадигма RAII работает очень неплохо, но и на Солнце есть пятна. В целях всеобщего просвещения и духовного развития, предлагаю обсудить проблемы, которые есть в RAII в языке С++

С уважением, коллега Erop.

18.10.12 19:22: Перенесено модератором из 'C/C++' — Кодт
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 12.10.12 15:55
Оценка: :)
E>предлагаю обсудить проблемы, которые есть в RAII в языке С++

Собственно, давайте рассмотрим такую штуку, как документ, попробуем спроектировать в рамках RAII класс для работы с документами.

Конструктор от имени файла будет у нас документ создавать.
Конструктор по умолчанию, будет создавать пустой документ
Деструктор будет записывать изменения, если они были.

Вот тут нас и ждёт засада. Предположим, что в деструкторе что-то пошло не так, файл оказался недоступен по записи, или его вообще ещё не назначили этому документу, или мы вообще решили перед записью уточнять надо ли записать изменения.
Вот тут-то у нас и возникнет проблема. Во-первых, нам придётся всё это спросить не выходя из деструктора, то есть, засунуть в данные кусок интерфейса, либо нагородить каких-то колбэков.
Ну это ещё пол-беды. Главная родовая травма деструкторов С++, это то, что их нельзя тормознуть.
То есть, если пользователь в алерт-боксе, нажмёт не "Сохранить" или "не сохранять", а "отменить", то мы вообще не сможем никак этого поддержать в нашей прекрасной RAII-архитектуре
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: Abyx Россия  
Дата: 12.10.12 16:02
Оценка: +6 :)
Здравствуйте, Erop, Вы писали:

E>Собственно, давайте рассмотрим такую штуку, как документ, попробуем спроектировать в рамках RAII класс для работы с документами.


E>Деструктор будет записывать изменения, если они были.

зачем? это не ответственность деструктора.

RAII — это про управление владением объектами, а не про хитрую логику в деструкторе.

E>Ну это ещё пол-беды. Главная родовая травма деструкторов С++, это то, что их нельзя тормознуть.

E>То есть, если пользователь в алерт-боксе, нажмёт не "Сохранить" или "не сохранять", а "отменить", то мы вообще не сможем никак этого поддержать в нашей прекрасной RAII-архитектуре
struct foo ( ~foo() { ::MessageBoxA(0, "!?", 0, 0); } };
у меня все работает
In Zen We Trust
Re: C++ и RAII
От: Abyx Россия  
Дата: 12.10.12 16:04
Оценка: 1 (1) +1 :)
Здравствуйте, Erop, Вы писали:

E>Из внезапно возникшего тут обсуждения, что есть истинный Бог христианскийRAII я понял, что многие коллеги считают, что RAII в С++ является просто какой-то серебрянной пулей, и что всеми ресурсами нужно управлять так и только так.


E>Конечно я не против, а наоборот, всеми руками за RAII, и в таких штуках, как локеры или, например, умные указатели, парадигма RAII работает очень неплохо, но и на Солнце есть пятна. В целях всеобщего просвещения и духовного развития, предлагаю обсудить проблемы, которые есть в RAII в языке С++


у RAII проблем нет. проблемы есть у тех кто не понимает что такое RAII и как его использовать.
In Zen We Trust
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: pzhy  
Дата: 12.10.12 16:52
Оценка:
Здравствуйте, Erop, Вы писали:

E>>предлагаю обсудить проблемы, которые есть в RAII в языке С++


E>Собственно, давайте рассмотрим такую штуку, как документ, попробуем спроектировать в рамках RAII класс для работы с документами.


E>Конструктор от имени файла будет у нас документ создавать.

E>Конструктор по умолчанию, будет создавать пустой документ
E>Деструктор будет записывать изменения, если они были.

E>Вот тут нас и ждёт засада. Предположим, что в деструкторе что-то пошло не так, файл оказался недоступен по записи, или его вообще ещё не назначили этому документу, или мы вообще решили перед записью уточнять надо ли записать изменения.

E>Вот тут-то у нас и возникнет проблема. Во-первых, нам придётся всё это спросить не выходя из деструктора, то есть, засунуть в данные кусок интерфейса, либо нагородить каких-то колбэков.
E>Ну это ещё пол-беды. Главная родовая травма деструкторов С++, это то, что их нельзя тормознуть.
E>То есть, если пользователь в алерт-боксе, нажмёт не "Сохранить" или "не сохранять", а "отменить", то мы вообще не сможем никак этого поддержать в нашей прекрасной RAII-архитектуре

Вроде умный ты мужик. А такие хм, глупости пишешь. RAII — для освобождения ресурсов-примитивов. То что ты написал к этому отношения не имеет. Можно конечно в деструкторе дернуть save() — если документ изменен, но это плохой тон, это дело выщестоящего леера. В любой MVC архитектуре для этого есть механизмы помимо деструктов.

В анологии — завещание надо писать заранее, а в деструкторе просто денатурировать отжившие белки
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: Abyx Россия  
Дата: 12.10.12 17:05
Оценка: 20 (2)
Здравствуйте, Erop, Вы писали:

добавлю что-то более конструктивное.
посмотрите на std::thread — это RAII обертка вокруг нативного потока.
при этом в деструкторе нет вызова join для предотвращения утечки этого потока.
там простой и легковесный код
~thread()
{
   if (this->joinable()) // если поток еще выполняется
      std::terminate(); // то это баг.

   free_native_thread_handle(this->handle);
}
In Zen We Trust
Re: Erop скучает
От: B0FEE664  
Дата: 12.10.12 17:11
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Из внезапно возникшего тут обсуждения, что есть истинный Бог христианскийRAII я понял, что многие коллеги считают, что RAII в С++ является просто какой-то серебрянной пулей, и что всеми ресурсами нужно управлять так и только так.


Чтобы хорошо горело надо масла подливать. Небольшой кусочек кода и Хо Ли Вар пройдет тропой Хо Ши Мина. Вот, тебе Erop серебренная пуля. Позволяет безопасно доступиться к данным из множества потоков:
Eсть класс

class Data
{
  public:
    std::queue<std::string> m_data;
    std::string pop()
    {
      const std::string str = m_data.front();
      m_data.pop();
      return str;
    }   
};

Его надо быстро превратить в thread-safe.
В первой нитке пишем
    ThreadSharedPtr<Data> oSharedData(new Data);//common data will
    ... = new boost::thread(thread2, oSharedData);
    ....
    oSharedData->m_data.push("handshake");


Во второй нитке пишем:
void thread2(ThreadSharedPtr<Data> oSharedData)
{
    ....
    std::string str = oSharedData->pop();
    ....
}


и ThreadSharedPtr обеспечит потокобезопасность. Реализация такая:

#include <boost/utility.hpp>
#include <boost/thread/mutex.hpp>

//The class used for lock/unlock m_mutex
//It is a helper. It should be privated class and friend of SharedData,
template <class T>
class SharedDataLocker
{
  public:
    // copy constructor
    SharedDataLocker(const SharedDataLocker& obj)
        : m_mutex       (obj.m_mutex),
          m_ptr         (obj.m_ptr),
          m_nLockCounter(obj.m_nLockCounter)
    {
        assert(0 < m_nLockCounter);
        ++m_nLockCounter;
    }

    SharedDataLocker(boost::mutex& mutex, T* ptr, int& nLockCounter)
        : m_mutex(mutex), m_ptr(ptr), m_nLockCounter(nLockCounter)
    {
        m_mutex.lock();
        assert(0 == nLockCounter);
        ++nLockCounter;//after lock !
    }

    ~SharedDataLocker()
    {
        assert(0 < m_nLockCounter);
        --m_nLockCounter;
        if ( 0 == m_nLockCounter )
        {
          m_mutex.unlock();
        }
    }
  private:
    // forbid use of operator "="
    const SharedDataLocker& operator=( const SharedDataLocker& );
  public:
    T* operator -> ()
    {
        assert(NULL != m_ptr);
        return m_ptr;
    }
  private:
    boost::mutex& m_mutex;
    T*            m_ptr;
    int&          m_nLockCounter;
};


template <class T>
class SharedData : public boost::noncopyable
{
    public:
        SharedData(T* p)
          : m_ptr(p), m_nLockCounter(0)
        {
        }

        ~SharedData()
        {
            delete m_ptr;
        }
    public:
        SharedDataLocker<T> operator -> ()
        {
            return SharedDataLocker<T>(m_mutex, m_ptr, m_nLockCounter);
        }
        T& Unprotected()
        {
            assert(NULL != m_ptr);
            return *m_ptr;
        }
    private:
        boost::mutex m_mutex;
        T*           m_ptr;
        int          m_nLockCounter;
};



template <class T>
class ThreadSharedPtr
{
  public:
    ThreadSharedPtr(T* p)
        : m_SharedMutexAndData(new SharedData<T>(p))
    {
        assert(NULL != p);
    }

    ThreadSharedPtr(const ThreadSharedPtr& obj)
        : m_SharedMutexAndData(obj.m_SharedMutexAndData)
    {
    }

    ~ThreadSharedPtr()
    {
    }
  public:
    ThreadSharedPtr& operator = (const ThreadSharedPtr& obj)
    {
        m_SharedMutexAndData = obj.m_SharedMutexAndData;
        return *this;
    }
  public:
    SharedData<T>& operator -> () const
    {
        assert(NULL != m_SharedMutexAndData.get());
        return *m_SharedMutexAndData;
    }

    T& Unprotected()
    {
        assert(NULL != m_SharedMutexAndData.get());
        return m_SharedMutexAndData->Unprotected();
    }
  private:
    boost::shared_ptr< SharedData<T> > m_SharedMutexAndData;
};


Тут RAII на RAII сидит и RAII управляет.
И каждый день — без права на ошибку...
Re[3]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 12.10.12 17:45
Оценка:
Здравствуйте, pzhy, Вы писали:


P>Вроде умный ты мужик. А такие хм, глупости пишешь. RAII — для освобождения ресурсов-примитивов. То что ты написал к этому отношения не имеет. Можно конечно в деструкторе дернуть save() — если документ изменен, но это плохой тон, это дело выщестоящего леера. В любой MVC архитектуре для этого есть механизмы помимо деструктов.


А чем save так уж сильно отличается от flush?..

Кроме того, файл, это примитивный ресурс? Подключение к базе? Интернет-соединение? Транзакция?

На самом деле, возлагать это на другой слой MVC именно потому и приходится, что RAII подразумевает нетривиальные действия в деструкторе, а эти действия в деструкторе С++ объекта, как раз и ограниченны...

P>В анологии — завещание надо писать заранее, а в деструкторе просто денатурировать отжившие белки


И брачный контракт до развода. Но, если не спровоцировать обсуждение небольшой провокацией, то малр кто об этом задумается
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 12.10.12 17:49
Оценка: -1
Здравствуйте, Abyx, Вы писали:

A>при этом в деструкторе нет вызова join для предотвращения утечки этого потока.

A>там простой и легковесный код
A>
A>~thread()
A>{
A>   if (this->joinable()) // если поток еще выполняется
A>      std::terminate(); // то это баг.

A>   free_native_thread_handle(this->handle);
A>}
A>

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

Так что спасибо за пример...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Erop скучает
От: Erop Россия  
Дата: 12.10.12 17:56
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>


BFE>//The class used for lock/unlock m_mutex
BFE>//It is a helper. It should be privated class and friend of SharedData,
BFE>template <class T>
BFE>class SharedDataLocker

BFE>


BFE>Тут RAII на RAII сидит и RAII управляет.


1) я не спорю с пользой RAII, я всего лишь обращаю внимаие коллег на то, что эта парадигклассе столкнулся ма не всесильна... с
аналогичным приборне проблема RAII, IMHO...
2) как-то я на практике столкнулся с аналогичным прибором.
Практика показала, что в названии класса пропущено слово Dead...

Только это не проблема RAII, IMHO...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 12.10.12 17:56
Оценка:
Здравствуйте, Erop, Вы писали:

E>дык это же всю идею и разрушает.

E>хендл мы, конечно освободили, но поток остался, если, например его кто-то затормозит, то он останется навсегда... Вот такая вот дырка в RAII получается...
В смысле — остался? Если поток остался, мы вылетим в terminate()...
Re: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 17:57
Оценка:
E>предлагаю обсудить проблемы, которые есть в RAII в языке С++

Есть такое мнение, что если у меня всё написано в парадигме RAII, то код готов к исключениям.
На самом деле это не совсем так, а часто и вообще совсем не так.

Рассмотрим простой стандартный класс std::pair<std::shared_ptr<T1>, std::shared_ptr<T2>>
Для краткости будем называть его shared_pair<T1, T2>

Как его создать?
Первое что приходит в голову:
shared_pait<T1, T2> p( new T1, new T2);

Но, очевидно, что это опасно. Почему опасно? Да потому, что если у нас из конструктора T1 или Т2 может вылететь исключение, то может так выйти, что мы создадим один из объектов, потом попробуем создать второй, получим исключение, и уже созданный объект будет потерян.

Как же быть? Можно попробовать написать так:
shared_pair<T1, T2> p( shared_ptr<T1>( new T1 ), shared_ptr<T2>( new T2 ) );


К сожалению, это ничем не лучше. Компилятор волен сделать так.
создать Т1, создать Т2, потом создать умный указатель на первый, потом на второй и только потом вызвать конструктор пары. Соответственно, исключение в конструкторе Т2 сломает этот код не хуже первого. Так как же быть?

Для shared_ptr есть решение
shared_ptr<T1> p1( new T1);
shared_ptr<T2> p2( new T2);
shared_pair<T1, T2> p( p1, p2 );
но это так только потому, что shared_ptr можно копировать. Для какого-нибудь unique_ptr фокус не удастся, и прийдётся писать примерно так:
unique_pair<T1, T2> p;
p.first = new T1;
p.second = new T2;
но это не совсем RAII, и это требует возможности реинициализацйии unique_ptr.
И это надо учесть, что мы рассмотрели только самый тривиальный случай, а реальность бывает намного замысловатее. Так что, RAII -- это, конечно хорошо, но расслабляться всё равно нельзя...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 12.10.12 17:59
Оценка:
Здравствуйте, enji, Вы писали:

E>В смысле — остался? Если поток остался, мы вылетим в terminate()...


И что в этом хорошего?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: offtopic
От: aa_unique  
Дата: 12.10.12 18:12
Оценка: +1 :)))
E>1) я не спорю с пользой RAII, я всего лишь обращаю внимаие коллег на то, что эта парадигклассе столкнулся ма не всесильна... с
E> аналогичным приборне проблема RAII, IMHO...
E>2) как-то я на практике столкнулся с аналогичным прибором.
E>Практика показала, что в названии класса пропущено слово Dead...

Такое ощущение, что предыдущее сообщение написано несколькими несинхронизованными потоками (без использования SharedDataLocker).
Re: C++ и RAII
От: rg45 СССР  
Дата: 12.10.12 18:59
Оценка: +1
Здравствуйте, Erop, Вы писали:

E>Из внезапно возникшего тут обсуждения, что есть истинный Бог христианскийRAII я понял, что многие коллеги считают, что RAII в С++ является просто какой-то серебрянной пулей, и что всеми ресурсами нужно управлять так и только так.


E>Конечно я не против, а наоборот, всеми руками за RAII, и в таких штуках, как локеры или, например, умные указатели, парадигма RAII работает очень неплохо, но и на Солнце есть пятна. В целях всеобщего просвещения и духовного развития, предлагаю обсудить проблемы, которые есть в RAII в языке С++


Что-то, Егор, я не улавливаю сути вопроса. Ты хочешь, что бы мы совместными усилиями провели четкую границу применимости RAII?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Roman Odaisky Украина  
Дата: 12.10.12 19:00
Оценка:
Здравствуйте, Erop, Вы писали:

E>Для shared_ptr есть решение
shared_ptr<T1> p1( new T1);
shared_ptr<T2> p2( new T2);
shared_pair<T1, T2> p( p1, p2 );
но это так только потому, что shared_ptr можно копировать. Для какого-нибудь unique_ptr фокус не удастся


std::move?
До последнего не верил в пирамиду Лебедева.
Re[2]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ops Россия  
Дата: 12.10.12 19:02
Оценка: +3
Здравствуйте, Erop, Вы писали:

shared_pait<T1, T2> p( make_shared<T1>(), make_shared<T2>());
Не?
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[2]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ku-ku  
Дата: 12.10.12 19:41
Оценка: 2 (1)
Здравствуйте, Erop, Вы писали:

E>Первое что приходит в голову:
shared_pait<T1, T2> p( new T1, new T2);

E>Но, очевидно, что это опасно. Почему опасно? Да потому, что если у нас из конструктора T1 или Т2 может вылететь исключение

Такой код просто не скомпилится, так как неявного преобразования сырого указателя в shared_ptr быть не может.

E>Как же быть? Можно попробовать написать так:
shared_pair<T1, T2> p( shared_ptr<T1>( new T1 ), shared_ptr<T2>( new T2 ) );

E>К сожалению, это ничем не лучше.

Открой для себя std::make_shared и list-initialization.

shared_pair<T1, T2> p{ std::shared_ptr<T1>( new T1 ), std::shared_ptr<T2>( new T2 ) };

http://liveworkspace.org/code/7d0fe735c22f7f4a8bef2cbe2d199bbe

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack
expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and
side effect associated with a given initializer-clause is sequenced before every value computation and side
effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.
[ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies
when the elements of the initializer-list are interpreted as arguments of a constructor call, even though
ordinarily there are no sequencing constraints on the arguments of a call. —end note ]
Re[3]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 20:14
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>std::move?

Это эквивалентно поддержки реинициализации, и это всё равно надо не забыть сделать...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: 1) Родовая травма: исключеиния в деструкторах
От: Vain Россия google.ru
Дата: 12.10.12 20:19
Оценка: +1
Здравствуйте, Abyx, Вы писали:

A>там простой и легковесный код

A>
A>~thread()
A>{
A>   if (this->joinable()) // если поток еще выполняется
A>      std::terminate(); // то это баг.

A>   free_native_thread_handle(this->handle);
A>}
A>

плохая идея завершать поток вместе с объектом, вот указатель с подсчётом ссылок это другое дело.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: sts  
Дата: 12.10.12 20:28
Оценка: +1
Здравствуйте, Erop, Вы писали:

E>>предлагаю обсудить проблемы, которые есть в RAII в языке С++


E>Собственно, давайте рассмотрим такую штуку, как документ, попробуем спроектировать в рамках RAII класс для работы с документами.


Предлагаю списать это на неудачную пятницу
Re[4]: offtopic
От: Erop Россия  
Дата: 12.10.12 20:33
Оценка:
Здравствуйте, aa_unique, Вы писали:

_>Такое ощущение, что предыдущее сообщение написано несколькими несинхронизованными потоками (без использования SharedDataLocker).


Да уж, у нас, параноиков своя специфика
На самом деле это просто глюк безклавного ввода на загруженной таблетке. Часть потока ввода втыкается в случайные места текста...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 12.10.12 20:40
Оценка:
Здравствуйте, sts, Вы писали:

sts>Предлагаю списать это на неудачную пятницу


Если всем так рвёт шаблон восприятие сессии редактирования документа, как ресурса, который надо освободить, сохранив изменения на диск, можно рассмотреть стандартный пример, с CFile::Close, который нельзя звать из ~CFile...

Суть-то проблемы не в том, что ТС идиот и придумал смешной пример, а в том, что многие ресурсы требуют нетривильных действий в конце сессии использования ресурса. И такие ресурсы в С++ версию RAII ложатся плохо. И хорошего ОБОБЩЁННОГО решения, кроме, как отказаться от RAII или перенести его на более высокий уровень -- нет...

А все, как дети малые, пишут дружно, про MVC и make_shared...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: sts  
Дата: 12.10.12 20:52
Оценка:
Здравствуйте, Erop, Вы писали:

E>Суть-то проблемы не в том, что ТС идиот и придумал смешной пример, а в том, что многие ресурсы требуют нетривильных действий в конце сессии использования ресурса. И такие ресурсы в С++ версию RAII ложатся плохо. И хорошего ОБОБЩЁННОГО решения, кроме, как отказаться от RAII или перенести его на более высокий уровень -- нет...


Сессия использования ресурса гарантированно заканчивается удачно.
Ты или сохраняешь или нет.
Потому ни кто не мешает реализовать это в деструкторе.
Другой вопрос, что реализация этого в деструкторе именно документа — не совсем удачная идея.
Лучше это сделать в деструкторе сессии редактирования.
И там пришибать документ сохранив его либо нет.
Ну и т.д. — просто наращиваются уровни абстракции с усложне6нием задачи.
Ну а если монолит с одним классом Документ, то нет проблем всю логику закрытия сол всеми окошками в дестрктор запихать.
На дельфе всю жисть начинающие атлеты так и делали
Re[3]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 20:54
Оценка:
Здравствуйте, Ops, Вы писали:

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


Ops>
shared_pait<T1, T2> p( make_shared<T1>(), make_shared<T2>());
Не?


Предположим, что у Т1 и Т2 нетривиальные конструкторы, или, например, T1 -- это FILE*, с fclose в качестве делетера...

Ну и вообще, в случае с конкретно shared_ptr можно выкрутиться, если не забыть, что тут вообще надо выкручиваться.
Но проблема глубже.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: sts  
Дата: 12.10.12 21:00
Оценка: :)
Здравствуйте, Erop, Вы писали:

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


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


Ops>>
shared_pait<T1, T2> p( make_shared<T1>(), make_shared<T2>());
Не?


E>Предположим, что у Т1 и Т2 нетривиальные конструкторы, или, например, T1 -- это FILE*, с fclose в качестве делетера...


E>Ну и вообще, в случае с конкретно shared_ptr можно выкрутиться, если не забыть, что тут вообще надо выкручиваться.

E>Но проблема глубже.

с нормальными либами в прикладухе не приходится делать new и иже с ним — объект суть есть ссылочный тип, а уж интрузив он или шаред зависит от лени разработчика либы — прикладуха об этом знать не должна
к сожалению 90% разработчиков новых инновационных технологий не знают об этом
Re[5]: 1) Родовая травма: исключения в деструкторах
От: Erop Россия  
Дата: 12.10.12 21:05
Оценка:
Здравствуйте, sts, Вы писали:

sts>Сессия использования ресурса гарантированно заканчивается удачно.

Я ещё раз говорю, забудь про документы. Я специально привожу дурацкие примеры, что бы не углубляться в суть примеров, а обсуждать проблемы RAII. Примеры -- это просто иллюстрация.

На практике обсуждаемая в этой подветке проблема, которую я назвал "Родовая травма: исключения в деструкторах", проявляется в том, что в деструкторе всего из себя такого RAII файла нельзя позвать flush...
Существования этой проблемы ты же не станешь, наверное, отрицать?..

Грубо говоря, мы код вида
very_long_foo()
{
    FILE* file = fopen( ... // не забудь про fclose!!!

    // тут дофига кода

    fclose( file );
}
меняем на код вида
very_long_foo()
{
    RaiiFile file( ... // не забудь про file.close()!!!

    // тут дофига кода

    file.close( file );
}
И в чём, спрашивается, профит?
Потерю открытого файла в случае исключения, которая легко детектится автоматическими средствами тестирования, мы заменили на потерю в случае исключения flush, то есть, возможно, части данных, что в общем случае не детектится никак...

Только я прошу не скатываться опять в обсуждение того, как оно надо *правильно* работать с файлами. Ресурсов правда много бывает разных. И не все из них закрываются тривиально...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: C++ и RAII
От: andyp  
Дата: 12.10.12 21:09
Оценка: 9 (1) +1
А вот мне в RAII не нравится следующее:
1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.
2. Когда попадаем в деструктор не совсем ясно, то ли мы раскручаем стек из-за исключения, связанного с нашим ресурсом, то ли все нормально (ну те нет исключения), то ли исключение вызвано чем-то другим (не нашим ресурсом). Конечно в с++11 есть current_exception(), но проверять и отлавливать свои исключения...
3. есть такие ресурсы с которыми непонятно что делать в деструкторе — ну типа нитей или файлов, про которые народ писал
Re[5]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 21:10
Оценка:
Здравствуйте, sts, Вы писали:

E>>Но проблема глубже.


sts>с нормальными либами в прикладухе не приходится делать new и иже с ним — объект суть есть ссылочный тип, а уж интрузив он или шаред зависит от лени разработчика либы — прикладуха об этом знать не должна


Ну это один из способов борьбы с проблемой, которую я назвал "Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>".
Но я не уверен, что это всегда можно сделать, или, хотя бы, всегда можно сделать удобно.
В конце концов у тебя же могут быть просто объекты какие-то, собранные в какую-то структуру данных на указателях, например...
Код же бывает сложнее, чем просто вызов библиотечных сервисов...

sts>к сожалению 90% разработчиков новых инновационных технологий не знают об этом

+1, потому я и затеял обсуждение, что как-то переоценивает народ мощь RAII. Типа раз заРАИИлся, то и всё, проблем с владением ресурсами не будет, а лёд-то может оказаться весьма тонким...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: 1) Родовая травма: исключения в деструкторах
От: sts  
Дата: 12.10.12 21:12
Оценка: +1 :)
Здравствуйте, Erop, Вы писали:

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


sts>>Сессия использования ресурса гарантированно заканчивается удачно.

E>Я ещё раз говорю, забудь про документы. Я специально привожу дурацкие примеры, что бы не углубляться в суть примеров, а обсуждать проблемы RAII. Примеры -- это просто иллюстрация.

E>На практике обсуждаемая в этой подветке проблема, которую я назвал "Родовая травма: исключения в деструкторах", проявляется в том, что в деструкторе всего из себя такого RAII файла нельзя позвать flush...


Да, я это отлично понимаю.
И решений тут вижу 2:
1 — ну, это тебе не будет интересно — типа изменить дизайн, число уровней абстракции
2 — на уровне компилятора сделать стек исключений как, например, это сделано в дельфи — там нет проблемы исключений в деструкторе — он протсо в стек запихиватся в случае наслоения, а потом есть волзможность все достать или обработать блоками кэтч соответствующего уровня вложенности.
только вот сегодня в рамках темы кто сколько лет хреначит тут, читал эпический срач по поводу дельфи — там все есть и с тех пор ничего не изменилось
отношения типа метакласс как не было так и нет во всех распространенных языках, в плюсах стек исключений не могут ввести, хотя чего бы уж тут — а просто ни кому не нужно — о таких вопросах как ты поднимаешь задумываются нуль целых и фиг десятых от всего "богатства" программеров
Re[6]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: sts  
Дата: 12.10.12 21:16
Оценка:
Здравствуйте, Erop, Вы писали:

E>+1, потому я и затеял обсуждение, что как-то переоценивает народ мощь RAII. Типа раз заРАИИлся, то и всё, проблем с владением ресурсами не будет, а лёд-то может оказаться весьма тонким...


Знаеш, после 1.5 лет проганья на гольном С у меня есть ощущение, что RAII многие наоборот недооценивают
RAII — это охренительная сила, которую, возможно, познаешь только в ее отсутствии.
Следующими силами считаю инкапсуляцию, наследование и полиморфизм встроенные в язык.
И лишь на последнем месте пор удобству рассматриваю исключение.
Все могу аргументировать, но, боюсь, срач долгий будет
Re[2]: C++ и RAII
От: Erop Россия  
Дата: 12.10.12 21:16
Оценка:
Здравствуйте, rg45, Вы писали:

R>Что-то, Егор, я не улавливаю сути вопроса. Ты хочешь, что бы мы совместными усилиями провели четкую границу применимости RAII?


Ну можно сказать и так, в ответах на это сообщение я привёл пару примеров трудностей для RAII в С++
1) RAII откладывает нетривиальные действия на деструктор, а там в С++ с нетривиальными действиями проблемы имени CFile::Close
2) Когда мы пытаемся при помощи RAII передать в один объект владение сразу несколькими ресурсами, мы имеем некоторые сложности, вызванные тем, что порядок вычисления частей выражений и аргументов функции, не определён...

На самом деле у меня в запасе был ещё один прикол, как минимум, но я не успел его выложить, потому, что тему уже подняли.

К сожалению, я перестарался с провокативностью вопроса, поэтому конструктивную часть обсуждения заглушает треск рвущихся шаблонов. С т. з. личностного и проф. роста рвать шаблоны полезно, но уровень и тон обсуждения, как всегда доставляют. Всё-таки РСДН уже не торт...
  мелкий шрифт, не хотите расстроиться, не читайте
Просто меня тут поддостал кое-кто из боссов, и я решил проверить на вшивость публику. Пока что вшивость детектед...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: sts  
Дата: 12.10.12 21:22
Оценка:
Здравствуйте, Vain, Вы писали:

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


A>>там простой и легковесный код

A>>
A>>~thread()
A>>{
A>>   if (this->joinable()) // если поток еще выполняется
A>>      std::terminate(); // то это баг.

A>>   free_native_thread_handle(this->handle);
A>>}
A>>

V>плохая идея завершать поток вместе с объектом, вот указатель с подсчётом ссылок это другое дело.

Да, join в деструкторе — это немотивированная жесть
Re[3]: C++ и RAII
От: sts  
Дата: 12.10.12 21:25
Оценка: 18 (1) +1
Здравствуйте, Erop, Вы писали:

E>К сожалению, я перестарался с провокативностью вопроса, поэтому конструктивную часть обсуждения заглушает треск рвущихся шаблонов. С т. з. личностного и проф. роста рвать шаблоны полезно, но уровень и тон обсуждения, как всегда доставляют. Всё-таки РСДН уже не торт...

E>
  мелкий шрифт, не хотите расстроиться, не читайте
Просто меня тут поддостал кое-кто из боссов, и я решил проверить на вшивость публику. Пока что вшивость детектед...


ладно тебе, наадеюсь, не слишком тебя расстроил
Re[4]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ops Россия  
Дата: 12.10.12 21:39
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Предположим, что у Т1 и Т2 нетривиальные конструкторы, или, например, T1 -- это FILE*, с fclose в качестве делетера...


Нетривиальные конструкторы, блин, ну я даже не знаю, RTFM что ли по make_shared. О том, как она реализована, и как написать подобную с делетером, думаю, ты догадываешься.

E>Ну и вообще, в случае с конкретно shared_ptr можно выкрутиться, если не забыть, что тут вообще надо выкручиваться.

E>Но проблема глубже.

Да нет тут проблемы. Есть сложности у новичков, ну так на то они и новички.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 12.10.12 21:47
Оценка:
Здравствуйте, Vain, Вы писали:

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


A>>там простой и легковесный код

A>>
A>>~thread()
A>>{
A>>   if (this->joinable()) // если поток еще выполняется
A>>      std::terminate(); // то это баг.

A>>   free_native_thread_handle(this->handle);
A>>}
A>>

V>плохая идея завершать поток вместе с объектом, вот указатель с подсчётом ссылок это другое дело.

Этот код не завершает поток, а только закрывает хэндл.
joinable() может вернуть false, когда поток ещё выполняется, если до этого была вызвана функция detach(), т.е. мы явно указали, что не хотим следить за потоком через объект std::thread.

Обоснование вызова std::terminate приведено здесь.
Re[2]: C++ и RAII
От: Ku-ku  
Дата: 12.10.12 21:48
Оценка: +1
Здравствуйте, andyp, Вы писали:

A>А вот мне в RAII не нравится следующее:

A>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.

Надо обрабатывать сразу — обрабатывай сразу. В чём проблема?

A>2. Когда попадаем в деструктор не совсем ясно, то ли мы раскручаем стек из-за исключения, связанного с нашим ресурсом, то ли все нормально (ну те нет исключения), то ли исключение вызвано чем-то другим (не нашим ресурсом).


Зачем об этом знать при использовании деструктора по прямому назначению?

A>3. есть такие ресурсы с которыми непонятно что делать в деструкторе — ну типа нитей или файлов, про которые народ писал


Закрывать хэндлы.
Пластмассовое мусорное ведро предназначено для утилизации мусора, при попытке сварить в нём кофе оно может расплавиться. Это серьёзный недостаток ведра или не?
Re[2]: C++ и RAII
От: Erop Россия  
Дата: 12.10.12 21:53
Оценка:
Здравствуйте, andyp, Вы писали:

A>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.

Это интересно. Можешь привести пример того, что конкретно ты имеешь в виду. Пример можно несерьёзный, просто что бы проиллюстрировать мысль.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 21:55
Оценка:
Здравствуйте, sts, Вы писали:


sts>Знаеш, после 1.5 лет проганья на гольном С у меня есть ощущение, что RAII многие наоборот недооценивают

sts>RAII — это охренительная сила, которую, возможно, познаешь только в ее отсутствии.
Дык кто бы спорил. RAII, конечно же очень полезная концепция, просто её надо использовать там и так, где она на пользу, и не использовать там и так, где во вред...
А тут начинаются тонкости...

sts>Следующими силами считаю инкапсуляцию, наследование и полиморфизм встроенные в язык.

sts>И лишь на последнем месте пор удобству рассматриваю исключение.
sts>Все могу аргументировать, но, боюсь, срач долгий будет

Хе, понедеьник-то не скоро
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Roman Odaisky Украина  
Дата: 12.10.12 21:59
Оценка:
Здравствуйте, Erop, Вы писали:

RO>>std::move?

E>Это эквивалентно поддержки реинициализации, и это всё равно надо не забыть сделать... :xz:

Так если помнить правило — результат new всегда немедленно помещать в именованный умный указатель — всё вполне ясно и просто:
typedef std::unique_ptr<X> U;
U p1(new X(1));
U p2(new X(2));
std::pair<U, U> p(std::move(p1), std::move(p2));
До последнего не верил в пирамиду Лебедева.
Re[7]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Roman Odaisky Украина  
Дата: 12.10.12 22:01
Оценка:
Здравствуйте, sts, Вы писали:

sts>Знаеш, после 1.5 лет проганья на гольном С у меня есть ощущение, что RAII многие наоборот недооценивают ;)

sts>RAII — это охренительная сила, которую, возможно, познаешь только в ее отсутствии.
...
sts>И лишь на последнем месте пор удобству рассматриваю исключение.

Странная логика, ведь RAII неразрывно связано с исключениями.
До последнего не верил в пирамиду Лебедева.
Re[2]: C++ и RAII
От: Roman Odaisky Украина  
Дата: 12.10.12 22:07
Оценка: 14 (3) +1 :)
Здравствуйте, andyp, Вы писали:

A>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.


Хрени имеют обыкновение случаться где попало. Вешать обработчик на каждое хренеопасное место представляется куда менее жизнеспособным решением, чем один раз втолковать компилятору, что делать в случае наступления хрени. Тем более, что RAII элегантно объединяет создание нового объекта (что нужно в каком-либо виде делать в любом случае) с разрушением его в хренегенных ситуациях, ничего лишнего писать и не приходится.
До последнего не верил в пирамиду Лебедева.
Re[5]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ku-ku  
Дата: 12.10.12 22:09
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Так если помнить правило — результат new всегда немедленно помещать в именованный умный указатель — всё вполне ясно и просто:


А если помнить другие правила, то можно поступить ещё проще

typedef std::unique_ptr<X> U;
std::pair<U, U> p{U(new X(1)), U(new X(2))};
Re[3]: C++ и RAII
От: andyp  
Дата: 12.10.12 22:19
Оценка:
Здравствуйте, Erop, Вы писали:

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


A>>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.

E>Это интересно. Можешь привести пример того, что конкретно ты имеешь в виду. Пример можно несерьёзный, просто что бы проиллюстрировать мысль.

На счет кода сразу не соображу. Идея состоит в том, чтобы на выходе из скопа до вызова деструктров иметь возможность прицепить нечто типа лямбда-функции, имеющей доступ к переменным, определенным в скопе. В этом случае решение проблемы будет определяться по соседству с проблемным блоком кода. Пример такой — пусть, например, ресурс — сокет. Используется обычная с-шная библиотека, возвращающая коды ошибки при работе с сокетами. Тогда в этой "лямбде" можно будет проанализировать код ошибки и предпринять адекватные действия. Плюс отпадает надобность в написании RAIIшной обертки.
Re[5]: Кактусы вкусны, просто вы не умеете их готовить... (с) мышки ;)
От: Erop Россия  
Дата: 12.10.12 22:23
Оценка:
Здравствуйте, Ops, Вы писали:

Ops>Да нет тут проблемы. Есть сложности у новичков, ну так на то они и новички.


Кактусы вкусны, просто вы не умеете их готовить... (с) мышки

Хинт, попробуй вместо того, чтобы углубляться в тему shared_ptr, подумать, нет ли в твоём коде таких граблей, и почему ты уверен, что их нет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 22:26
Оценка:
Здравствуйте, Ops, Вы писали:

Ops>Нетривиальные конструкторы, блин, ну я даже не знаю, RTFM что ли по make_shared. О том, как она реализована, и как написать подобную с делетером, думаю, ты догадываешься.


Зачем что-то писать.
1) Можно просто сделать именованный автоматический shared_ptr с нужными типами и делетером, а потом ужо от него сконструировать shared_pair... Про shared_ptr вообще не интересно. Для него решение есть, я его привёл, и в целом всё понятно, кроме того, какой процент программистов не в курсе существования такой проблемы, и, соответственно, не предпринимает мер, что бы её избежать...

О том, что make_share это уход от концепции RAII, я вообще молчу. Просто что бы не сбивать народ с темы
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: C++ и RAII
От: andyp  
Дата: 12.10.12 22:30
Оценка: +1
Здравствуйте, Ku-ku, Вы писали:

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


A>>А вот мне в RAII не нравится следующее:

A>>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.

KK>Надо обрабатывать сразу — обрабатывай сразу. В чём проблема?


Проблемы нет.

A>>2. Когда попадаем в деструктор не совсем ясно, то ли мы раскручаем стек из-за исключения, связанного с нашим ресурсом, то ли все нормально (ну те нет исключения), то ли исключение вызвано чем-то другим (не нашим ресурсом).


KK>Зачем об этом знать при использовании деструктора по прямому назначению?


Финализация работы с ресурсом может быть разной в зависимости от того, что с ним произошло.

A>>3. есть такие ресурсы с которыми непонятно что делать в деструкторе — ну типа нитей или файлов, про которые народ писал


KK>Закрывать хэндлы.

KK>Пластмассовое мусорное ведро предназначено для утилизации мусора, при попытке сварить в нём кофе оно может расплавиться. Это серьёзный недостаток ведра или не?

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

Смысл поста в том, что про RAII говорят как о технике, позволяющей автоматически решать проблему освобождения ресурсов, но на мой взгляд в сложных ситуациях это не так. Мало того, RAII обертки могут быть бесполезны для некоторых ресурсов.
Re[5]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 22:35
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>>>std::move?

E>>Это эквивалентно поддержки реинициализации, и это всё равно надо не забыть сделать...

RO>Так если помнить правило — результат new всегда немедленно помещать в именованный умный указатель — всё вполне ясно и просто:

RO>
RO>typedef std::unique_ptr<X> U;
RO>U p1(new X(1));
RO>U p2(new X(2));
RO>std::pair<U, U> p(std::move(p1), std::move(p2));
RO>


Это эквивалентно
pair<U, U> p;
p.first = new X;
p.second = new X;
или аналог на swap...

вопрос лишь в том, что поддерживает U...

Но и в том или другом случае надо НЕ ЗАБЫТЬ это сделать...

Но вопрос-то вообще не про pair. Класс, конструктору которого во владение надо отдать два ресурса может быть и посложнее...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 22:42
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Странная логика, ведь RAII неразрывно связано с исключениями.


Почему?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: sts  
Дата: 12.10.12 22:43
Оценка: +1
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Roman Odaisky, Вы писали:


RO>>Странная логика, ведь RAII неразрывно связано с исключениями.


E>Почему?


исключения не могут без RAII
а воот RAII без исключений отлично живет
Re[10]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 12.10.12 22:45
Оценка:
Здравствуйте, sts, Вы писали:

sts>исключения не могут без RAII

Тоже могут, но криво...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: sts  
Дата: 12.10.12 22:49
Оценка: +1 :))
Здравствуйте, Erop, Вы писали:

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


sts>>исключения не могут без RAII

E>Тоже могут, но криво...

ну это ты про симбиан наверное
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 04:27
Оценка:
Здравствуйте, Erop, Вы писали:

E>То есть, если пользователь в алерт-боксе, нажмёт не "Сохранить" или "не сохранять", а "отменить", то мы вообще не сможем никак этого поддержать в нашей прекрасной RAII-архитектуре


имхо, к RAII это никак не относится.
Это больше вопрос про деструкторы, которые хотя и имеют отношение к RAII, но далеко не во всех случаях
Re[2]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 04:50
Оценка: 1 (1)
Здравствуйте, Erop, Вы писали:

E>Есть такое мнение, что если у меня всё написано в парадигме RAII, то код готов к исключениям.

E>На самом деле это не совсем так, а часто и вообще совсем не так.

В плане basic guarantee, часто уже готов. В плане strong guarantee — часто этого не достаточно.
С RAII писать код готовый к исключениям получится проще, чем без него. Например, в C#, Java, Python — RAII нет, но зато есть различные workarounds на эту тему.

E>Первое что приходит в голову:
shared_pait<T1, T2> p( new T1, new T2);

E>Но, очевидно, что это опасно. Почему опасно? Да потому, что если у нас из конструктора T1 или Т2 может вылететь исключение, то может так выйти, что мы создадим один из объектов, потом попробуем создать второй, получим исключение, и уже созданный объект будет потерян.

К RAII как идиоме, это не имеет отношения.
У Саттера этот кейс разжёван.
Re[6]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ops Россия  
Дата: 13.10.12 05:07
Оценка: +2
Здравствуйте, Erop, Вы писали:

E>О том, что make_share это уход от концепции RAII, я вообще молчу. Просто что бы не сбивать народ с темы


Уход от концепции — это как раз
x(new A, new B)
поскольку тут каждое выделение ресурса — это еще не инициализация. make_shared же позволяет это обойти, каждый ее вызов — это и выделение ресурса, и инициализация.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[6]: Кактусы вкусны, просто вы не умеете их готовить... (с) мышки ;)
От: Ops Россия  
Дата: 13.10.12 05:10
Оценка:
Здравствуйте, Erop, Вы писали:

E>Хинт, попробуй вместо того, чтобы углубляться в тему shared_ptr, подумать, нет ли в твоём коде таких граблей, и почему ты уверен, что их нет...


Именно таких скорее всего исчезающе мало, когда-то понаступал и научился обходить.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 05:31
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Если всем так рвёт шаблон восприятие сессии редактирования документа, как ресурса, который надо освободить, сохранив изменения на диск, можно рассмотреть стандартный пример, с CFile::Close, который нельзя звать из ~CFile...


можно
class RAII_Deferred
{
    bool fail_on_flush;
public:
    // Normal contrustor
    RAII_Deferred(bool fail_on_flush_) : fail_on_flush(fail_on_flush_)
    {
        cout << "acquiring resource" << endl;
    }
    // Release part of destructor.
    // Herb Sutter: "letting go of a resource" must never fail
    // (http://cpp-next.com/archive/2012/08/evil-or-just-misunderstood/)
    TWO_STAGE_DESTRUCTOR_RELEASE(RAII_Deferred)
    {
        cout << "release resource" << endl;
    }
    // Deferred part of destructor. May fail(for instance fflush).
    // Called when object is destroyed due to normal flow, not stack unwinding
    TWO_STAGE_DESTRUCTOR_DEFERRED(RAII_Deferred)
    {
        cout << "flush pending actions on resource" << endl;
        if(fail_on_flush) throw 1;
    }
};

И пример с выводом внизу.
Главное не показывать кидающие деструкторы людям со слабой психикой — разрыв шаблона гарантирован!

E>Суть-то проблемы не в том, что ТС идиот и придумал смешной пример, а в том, что многие ресурсы требуют нетривильных действий в конце сессии использования ресурса. И такие ресурсы в С++ версию RAII ложатся плохо. И хорошего ОБОБЩЁННОГО решения, кроме, как отказаться от RAII или перенести его на более высокий уровень -- нет...


RAII — это про resource acquisition и release, всё.
Но, не смотря на то, что описанная проблема не относится непосредственно к RAII, она, тем не менее, существует.
Например, очевидно, что вместо вот этого:
{
    File a(/*...*/),b(/*...*/);
    /* ... */
    /* use a and b*/
    b.flush();
    a.flush();
}
неплохо было бы иметь возможность писать
{
    File a(/*...*/),b(/*...*/);
    /* ... */
    /* use a and b*/
}
С сохранением семантики: если b.flush() кидает, то a.flush() не вызывается, деструкторы обоих вызываются всегда.

Одним из вариантов является введение понятия отложенных действий
Автор: Evgeny.Panasyuk
Дата: 01.10.12
, что позволяет чётко разграничить непосредственно освобождение ресурсов(которое не фейлится) и различные нетривиальные действия над этими ресурсами, которые выполняются перед их освобождением и по сути своей могут фейлится.
Re[6]: 1) Родовая травма: исключения в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 06:07
Оценка:
Здравствуйте, Erop, Вы писали:

E>Грубо говоря, мы код вида
very_long_foo()
E>{
E>    FILE* file = fopen( ... // не забудь про fclose!!!

E>    // тут дофига кода

E>    fclose( file );
E>}
меняем на код вида
very_long_foo()
E>{
E>    RaiiFile file( ... // не забудь про file.close()!!!

E>    // тут дофига кода

E>    file.close( file );
E>}
И в чём, спрашивается, профит?

E>Потерю открытого файла в случае исключения, которая легко детектится автоматическими средствами тестирования, мы заменили на потерю в случае исключения flush, то есть, возможно, части данных, что в общем случае не детектится никак...

Тут не сам flush теряется, а исключение из него — это огромная разница. Например, fclose он ведь тоже пытается flush'ить. Да и в общем случае, ни что не мешает вызывать flush-аналоги в деструкторе.
И к тому же, во многих случаях достаточно знать об одном, первом исключении.

For example, it might be reasonable to simply drop the second exception on the floor and propagate the original one. Before you freak out, consider this: the second exception doesn’t change the unwinding process in any way, at least, not until the exception is caught, and the original failure is still the root cause of the current unwind. The program or the user can likely deal just as well with that root cause without knowing anything about the second exception.

Важен другой момент — как дать пользователю(уровням выше) узнать возможность об этом первом исключении, не расставляя при file.close() вручную.
Один из вариантов — Throwing Destructors which are not Terminators:
class NotTerminator
{
public:
    void something_throwable()
    {
        throw 0;
    }
    DESTRUCTOR_BUT_NOT_TERMINATOR(NotTerminator)
    {
        something_throwable();
    }
};

И пример с выводом внизу.
Опять же — слабонервным не рекомендуется


E>Только я прошу не скатываться опять в обсуждение того, как оно надо *правильно* работать с файлами. Ресурсов правда много бывает разных. И не все из них закрываются тривиально...


Присоединяюсь к просьбе. А то может опять выяснится
Автор: Andrew S
Дата: 02.10.12
, что File + exceptions, это вообще не идеологично:

Как ни странно, но мой опыт говорит, что при работе с объектом уровня File исключение не должно произойти нигде

Re[6]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 13.10.12 06:20
Оценка: :)
Здравствуйте, Erop, Вы писали:

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


E>>В смысле — остался? Если поток остался, мы вылетим в terminate()...


E>И что в этом хорошего?


а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...
Re[7]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 13.10.12 06:37
Оценка:
Здравствуйте, enji, Вы писали:

E>а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...


... раз две недели роняя ответственный сервер клиента в непонятный момент по терминэйт, без логов и сохранения данных. Прекрасное решение, демонстрирующее всю мощь RAII в кривых руках...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 13.10.12 06:37
Оценка:
Здравствуйте, enji, Вы писали:

E>а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...


Кстати, почему не assert?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: rg45 СССР  
Дата: 13.10.12 07:06
Оценка: :)
Здравствуйте, Erop, Вы писали:


E>Как же быть? Можно попробовать написать так:
shared_pair<T1, T2> p( shared_ptr<T1>( new T1 ), shared_ptr<T2>( new T2 ) );


E>К сожалению, это ничем не лучше. Компилятор волен сделать так.

E>создать Т1, создать Т2, потом создать умный указатель на первый, потом на второй и только потом вызвать конструктор пары. Соответственно, исключение в конструкторе Т2 сломает этот код не хуже первого.

Мог бы ты подкрепить этот тезис какими-то ссылками на стандарт или практическим примером? Пример можешь запустить у себя в домашних условиях, а нам просто расскажешь, подтвердилось или нет, и на каких компиляторах, мы тебе поверим на слово.

Я думаю, ты ошибаешься. Если бы мы рассматривали выражение вида foo(a + x, a + y), то да — поскольку в этом случае выражения аргументов не разделены точками следования. А выражение вида foo(a(x), a(y)) — совсем другое дело — аргументы здесь инициализируются с использованием конструкторов, вход и выход в/из которых разделены точками следования, а это значит, что компилятор не приступит вычислению следующего аргумента, пока не закончит вычисление предыдущего.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 07:41
Оценка:
Здравствуйте, Erop, Вы писали:

E>>а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...

E>... раз две недели роняя ответственный сервер клиента в непонятный момент по терминэйт, без логов и сохранения данных. Прекрасное решение, демонстрирующее всю мощь RAII в кривых руках...

Precondition failure

Precondition failures indicate coding errors, which are non-recoverable conditions. Exceptions are for recoverable conditions. No point in unwinding if you won’t be able to recover. In general, you should only throw in response to a precondition failure if you can’t convince your customer that further arbitrarily erroneous (aka undefined) behavior is worse than termination.


Для логов есть set_terminate. Если допустим auto-join — сделать небольшую обвёртку.
Если важно не терять агонизирующий процесс — можно попробовать так.
#include <boost/test/included/prg_exec_monitor.hpp> 
#include <boost/test/execution_monitor.hpp>
#include <exception>
#include <iostream>
#include <ostream>
 
using namespace std;
 
void myterminate ()
{
  cerr << "terminate handler called\n";
}
int terminator()
{
    terminate();
}
int cpp_main( int, char* [] )
{
    set_terminate(myterminate);
    boost::execution_monitor m;
    try
    {
        m.execute(terminator);
    }
    catch ( boost::execution_exception const& ex )
    {
        cout << "Caught exception: " << ex.what() << endl;
    }
    cout << "Alive!" << endl;
    return 0;
}
Re[4]: C++ и RAII
От: Ku-ku  
Дата: 13.10.12 07:42
Оценка:
Здравствуйте, andyp, Вы писали:

A>Финализация работы с ресурсом может быть разной в зависимости от того, что с ним произошло.


Например?

A>>>3. есть такие ресурсы с которыми непонятно что делать в деструкторе — ну типа нитей или файлов, про которые народ писал


KK>>Закрывать хэндлы.

KK>>Пластмассовое мусорное ведро предназначено для утилизации мусора, при попытке сварить в нём кофе оно может расплавиться. Это серьёзный недостаток ведра или не?

A>Я не против. Я за то, чтобы иметь возможность снова обратиться к ресурсу после такого вызова деструктора.


То есть закрыли хэндл и после обращаемся к ресурсу?

A>Смысл поста в том, что про RAII говорят как о технике, позволяющей автоматически решать проблему освобождения ресурсов, но на мой взгляд в сложных ситуациях это не так.


Сложная ситуация — это когда "освобождение ресурса" таковым на самом деле не является?

A>Мало того, RAII обертки могут быть бесполезны для некоторых ресурсов.


Ну если API кривое, то да, выпрямить его не всегда возможно.
Re[2]: C++ и RAII
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 08:03
Оценка:
Здравствуйте, andyp, Вы писали:

A>2. Когда попадаем в деструктор не совсем ясно, то ли мы раскручаем стек из-за исключения, связанного с нашим ресурсом, то ли все нормально (ну те нет исключения), то ли исключение вызвано чем-то другим (не нашим ресурсом).


Наше/не наше проверяется флагом (мы же знаем когда сами кидаем).
Есть/нет исключение, с помощью Unwinding Aware Destructor
struct Foo
{
    UNWINDING_AWARE_DESTRUCTOR(Foo,due_to_unwinding)
    {
        if(due_to_unwinding)
            // ...
        else
            // ...
    }
};

И пример.

A>Конечно в с++11 есть current_exception(), но проверять и отлавливать свои исключения...


для проверки "is_unwinding" из деструктора — current_exception не пригоден:

18.8.5/8
current_exception Returns: An exception_ptr object that refers to the currently handled exception

Например MSVC2012 возвращает default constructed exception_ptr в деструкторе во время раскрутки.
Re[7]: 1) Родовая травма: исключения в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 08:22
Оценка:
Здравствуйте, sts, Вы писали:

sts>2 — на уровне компилятора сделать стек исключений как, например, это сделано в дельфи — там нет проблемы исключений в деструкторе — он протсо в стек запихиватся в случае наслоения, а потом есть волзможность все достать или обработать блоками кэтч соответствующего уровня вложенности.


Стэк исключений уже есть — компиляторы умеют с ним работать. Просто в стандарте std::terminate и всё тут.
А теперь внимание, беременных и детей убрать от мониторов: 500 <b>одновременных</b> не пойманных исключений!
Re[3]: C++ и RAII
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 08:40
Оценка:
Здравствуйте, Erop, Вы писали:

R>>Что-то, Егор, я не улавливаю сути вопроса. Ты хочешь, что бы мы совместными усилиями провели четкую границу применимости RAII?

E>Ну можно сказать и так, в ответах на это сообщение я привёл пару примеров трудностей для RAII в С++
E>1) RAII откладывает нетривиальные действия на деструктор, а там в С++ с нетривиальными действиями проблемы имени CFile::Close

RAII не откладывает нетривиальные действия на деструктор, а только release. fclose не фейлится в плане освобождения(release) хэндла.
Проблема с нетривиальными отложенными действиями есть, но это относится к декструкторам, а никак ни к RAII.
Re[5]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 13.10.12 08:46
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Например, очевидно, что вместо вот этого:

EP>
EP>{
EP>    File a(/*...*/),b(/*...*/);
EP>    /* ... */
EP>    /* use a and b*/
EP>    b.flush();
EP>    a.flush();
EP>}
EP>
неплохо было бы иметь возможность писать

EP>
EP>{
EP>    File a(/*...*/),b(/*...*/);
EP>    /* ... */
EP>    /* use a and b*/
EP>}
EP>
С сохранением семантики: если b.flush() кидает, то a.flush() не вызывается, деструкторы обоих вызываются всегда.


Мне это не очевидно. Здесь нарушается принцип "одна задача — одна функция", что ИМХО усложняет понимание кода. От использования

{
    File a(/*...*/),b(/*...*/);
    scope(success)
    {
        b.flush();
        a.flush();
    }
    /* ... */
    /* use a and b*/
}

я бы не отказался. Многофункциональные бросающие деструкторы в топку.
Re[3]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ku-ku  
Дата: 13.10.12 08:46
Оценка: 10 (1) +1
Здравствуйте, rg45, Вы писали:

E>>Как же быть? Можно попробовать написать так:
shared_pair<T1, T2> p( shared_ptr<T1>( new T1 ), shared_ptr<T2>( new T2 ) );


E>>К сожалению, это ничем не лучше. Компилятор волен сделать так.

E>>создать Т1, создать Т2, потом создать умный указатель на первый, потом на второй и только потом вызвать конструктор пары. Соответственно, исключение в конструкторе Т2 сломает этот код не хуже первого.

R>Мог бы ты подкрепить этот тезис какими-то ссылками на стандарт


Except where noted, evaluations of operands of individual operators and of subexpressions of individual
expressions are unsequenced.

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

R>А выражение вида foo(a(x), a(y)) — совсем другое дело — аргументы здесь инициализируются с использованием конструкторов, вход и выход в/из которых разделены точками следования


Точек следования больше нет

R>а это значит, что компилятор не приступит вычислению следующего аргумента, пока не закончит вычисление предыдущего.


Откуда это следует? Известно только то, что каждый конструктор будет вызван после вычисления соответствующего аргумента. Взаимный порядок вычисления этих аргументов между собой не определён.
Re[6]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Roman Odaisky Украина  
Дата: 13.10.12 08:52
Оценка:
Здравствуйте, Ku-ku, Вы писали:

RO>>Так если помнить правило — результат new всегда немедленно помещать в именованный умный указатель — всё вполне ясно и просто:


KK>А если помнить другие правила, то можно поступить ещё проще :-)


KK>
typedef std::unique_ptr<X> U;
std::pair<U, U> p{U(new X(1)), U(new X(2))};

А в списках инициализации есть некая особенная магия, которая позволит избежать типичной проблемы такого подхода?

1. Компилятор решил вызвать вещи в таком порядке: operator new, X::X, operator new, X::X, U::U, U::U, pair::pair;
2. Второй конструктор X бросил исключение;
3. К первому new никто не вызвал delete.
До последнего не верил в пирамиду Лебедева.
Re[4]: C++ и RAII
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 08:53
Оценка:
Здравствуйте, andyp, Вы писали:

A>>>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.

E>>Это интересно. Можешь привести пример того, что конкретно ты имеешь в виду. Пример можно несерьёзный, просто что бы проиллюстрировать мысль.
A>На счет кода сразу не соображу. Идея состоит в том, чтобы на выходе из скопа до вызова деструктров иметь возможность прицепить нечто типа лямбда-функции, имеющей доступ к переменным, определенным в скопе. В этом случае решение проблемы будет определяться по соседству с проблемным блоком кода. Пример такой — пусть, например, ресурс — сокет. Используется обычная с-шная библиотека, возвращающая коды ошибки при работе с сокетами. Тогда в этой "лямбде" можно будет проанализировать код ошибки и предпринять адекватные действия. Плюс отпадает надобность в написании RAIIшной обертки.

Звучит похоже на BOOST_SCOPE_EXIT, SCOPE_FAILURE, SCOPE_SUCCESS:
{
    SCOPE_FAILURE(void) {
        log("could not update user info");
    } SCOPE_FAILURE_END

    File passwd("/etc/passwd");

    SCOPE_SUCCESS( (&passwd) ) {
        passwd.flush();
    } SCOPE_SUCCESS_END

    BOOST_SCOPE_EXIT( (&passwd) ) {
        passwd.close();
    } BOOST_SCOPE_EXIT_END

    // ...
}
Re[3]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 13.10.12 08:57
Оценка: 10 (1)
Здравствуйте, rg45, Вы писали:

R>Мог бы ты подкрепить этот тезис какими-то ссылками на стандарт или практическим примером? Пример можешь запустить у себя в домашних условиях, а нам просто расскажешь, подтвердилось или нет, и на каких компиляторах, мы тебе поверим на слово.


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

Этот код (std тогда ещё не было )
#include "stdafx.h"

#include <iostream.h>


struct TestObj {
   virtual ~TestObj() {}
   TestObj() { cout << "Start build obj at 0x" << hex << (int)this << endl; }   
};

struct ThrowInCtor : TestObj {
   ThrowInCtor() { throw 1; }
};

struct ObjOwner {
   ObjOwner( TestObj* p ) { cout << "Take obj: 0x" << hex << (int)p << endl; }
};

void foo( ObjOwner a1, ObjOwner a2 ) {}



int main(int argc, char* argv[])
{
   try {
      foo( new ThrowInCtor, new TestObj );
   } catch( int i ) {
      cout << i << " catched" << endl;
      return i;
   }

    
    return 0;
}
собранный в дефолтных настройках консольного приложения даёт такой вывод:
Start build obj at 0x220030
Start build obj at 0x2217d0
1 catched
а если стереть строчку
   ThrowInCtor() { throw 1; }
то такой:
Start build obj at 0x430030
Start build obj at 0x4317d0
Take obj: 0x430030
Take obj: 0x4317d0


R>Я думаю, ты ошибаешься. Если бы мы рассматривали выражение вида foo(a + x, a + y), то да — поскольку в этом случае выражения аргументов не разделены точками следования. А выражение вида foo(a(x), a(y)) — совсем другое дело — аргументы здесь инициализируются с использованием конструкторов, вход и выход в/из которых разделены точками следования, а это значит, что компилятор не приступит вычислению следующего аргумента, пока не закончит вычисление предыдущего.

Вовсе и нет, это ничего такого не значит.
Например, в таком вот коде
for( int i = 0; i < bib_number; i++ ) {
    MyClass p( x+y+z+QQQ );
    p.Do( i );
}
компилятор может вынести вычисление x+y+z+QQQ не то, что пораньше, а вообще за пределы цикла. И никакая точка следования на входе в конструктор ему не мешает
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Roman Odaisky Украина  
Дата: 13.10.12 08:57
Оценка:
KK>>А если помнить другие правила, то можно поступить ещё проще :-)
KK>>std::pair<U, U> p{U(new X(1)), U(new X(2))};
RO>А в списках инициализации есть некая особенная магия, которая позволит избежать типичной проблемы такого подхода?

Прочитал твое сообщение ниже, магия действительно обнаружена. Интересно. Но опасно — вдруг кто-нибудь порефакторит pair p { X, Y } в pair p(X, Y), что, казалось бы, одно и то же...
До последнего не верил в пирамиду Лебедева.
Re[4]: C++ и RAII
От: Erop Россия  
Дата: 13.10.12 08:59
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>RAII не откладывает нетривиальные действия на деструктор, а только release. fclose не фейлится в плане освобождения(release) хэндла.

EP>Проблема с нетривиальными отложенными действиями есть, но это относится к декструкторам, а никак ни к RAII.

RAII можно не тока в С++...
Вся эта возня с деструкторами -- конкретная фишка сочетания RAII и C++.
О том и речь, как бы...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 13.10.12 09:01
Оценка: 1 (1) +1
Здравствуйте, Erop, Вы писали:

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


E>>а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...


E>Кстати, почему не assert?

наверное чтобы работало и в релизе...
Re[8]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 13.10.12 09:04
Оценка: +1
Здравствуйте, Erop, Вы писали:

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


E>>а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...


E>... раз две недели роняя ответственный сервер клиента в непонятный момент по терминэйт, без логов и сохранения данных. Прекрасное решение, демонстрирующее всю мощь RAII в кривых руках...

Ну дык мозг тоже нужен. Хочется логов — сделай свою обертку над thread и приверни в деструктор логи, проблем нет... Библиотечный класс всяко не может ничего логировать.

ИМХО, на уровне библиотеки — вполне нормальная реализация. Ошибся программист — библиотека выдала ошибку. Да, с++ не от всех ошибок позволяет восстановиться. Ну дык если надо что-то неубиваемое — создаем отдельный вотчдог, который перезапускает рабочий процесс, если тот перестал подавать признаки жизни.
Re[8]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ku-ku  
Дата: 13.10.12 09:06
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>вдруг кто-нибудь порефакторит pair p { X, Y } в pair p(X, Y)


Ноги поотрывать, уши пооборвать.
Re[6]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 09:15
Оценка:
Здравствуйте, Ku-ku, Вы писали:

EP>>Например, очевидно, что вместо вот этого:

EP>>
EP>>{
EP>>    File a(/*...*/),b(/*...*/);
EP>>    /* ... */
EP>>    /* use a and b*/
EP>>    b.flush();
EP>>    a.flush();
EP>>}
EP>>
неплохо было бы иметь возможность писать

EP>>
EP>>{
EP>>    File a(/*...*/),b(/*...*/);
EP>>    /* ... */
EP>>    /* use a and b*/
EP>>}
EP>>
С сохранением семантики: если b.flush() кидает, то a.flush() не вызывается, деструкторы обоих вызываются всегда.

KK>Мне это не очевидно. Здесь нарушается принцип "одна задача — одна функция", что ИМХО усложняет понимание кода. От использования

Видимо лучше переименовать
TWO_STAGE_DESTRUCTOR_DEFERRED -> DEFERRED_ACTION
TWO_STAGE_DESTRUCTOR_RELEASE -> RESOURCE_RELEASE
и для симметрии конструктор -> RESOURCE_ACQUISITION
Одна задача — одна функция.

KK>
{
KK>    File a(/*...*/),b(/*...*/);
KK>    scope(success)
KK>    {
KK>        b.flush();
KK>        a.flush();
KK>    }
KK>    /* ... */
KK>    /* use a and b*/
KK>}

KK>я бы не отказался.

Сейчас такой синтаксис:
{
    File a(/*...*/),b(/*...*/);
    SCOPE_SUCCESS(&a,&b)
    {
        b.flush();
        a.flush();
    } SCOPE_SUCCESS_END
    /* ... */
    /* use a and b*/
}

KK>Многофункциональные бросающие деструкторы в топку.

То что они многофункциональные и бросающие — это деталь реализации, которая скрыта за слоем абстракции.
Пользователь реализует DEFERRED_ACTION и RESOURCE_RELEASE по отдельности.
Re[9]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 13.10.12 09:18
Оценка:
Здравствуйте, enji, Вы писали:

E>Ну дык мозг тоже нужен. Хочется логов — сделай свою обертку над thread и приверни в деструктор логи, проблем нет... Библиотечный класс всяко не может ничего логировать.


Логов захочется из других ниток, а им не дадут сброситься, потому, что терминируют
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: C++ и RAII
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 09:27
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>>RAII не откладывает нетривиальные действия на деструктор, а только release. fclose не фейлится в плане освобождения(release) хэндла.

EP>>Проблема с нетривиальными отложенными действиями есть, но это относится к декструкторам, а никак ни к RAII.
E>RAII можно не тока в С++...

Я знаю что в D, wikipedia говорит что ещё в Ada.

E>Вся эта возня с деструкторами -- конкретная фишка сочетания RAII и C++.

E>О том и речь, как бы...

В языке D тоже деструкторы.
О чём речь?
Re[4]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: rg45 СССР  
Дата: 13.10.12 10:12
Оценка: :)
Здравствуйте, Erop, Вы писали:

R>>Я думаю, ты ошибаешься. Если бы мы рассматривали выражение вида foo(a + x, a + y), то да — поскольку в этом случае выражения аргументов не разделены точками следования. А выражение вида foo(a(x), a(y)) — совсем другое дело — аргументы здесь инициализируются с использованием конструкторов, вход и выход в/из которых разделены точками следования, а это значит, что компилятор не приступит вычислению следующего аргумента, пока не закончит вычисление предыдущего.

E>Вовсе и нет, это ничего такого не значит.
E>Например, в таком вот коде
for( int i = 0; i < bib_number; i++ ) {
E>    MyClass p( x+y+z+QQQ );
E>    p.Do( i );
E>}
компилятор может вынести вычисление x+y+z+QQQ не то, что пораньше, а вообще за пределы цикла. И никакая точка следования на входе в конструктор ему не мешает


Да, попутал я мягкое с теплым, каюсь
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 13.10.12 10:25
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Видимо лучше переименовать

EP>TWO_STAGE_DESTRUCTOR_DEFERRED -> DEFERRED_ACTION
EP>TWO_STAGE_DESTRUCTOR_RELEASE -> RESOURCE_RELEASE
EP>и для симметрии конструктор -> RESOURCE_ACQUISITION
EP>Одна задача — одна функция.

В конечном счёте всё равно два действия запихиваются в один деструктор. Или я что-то не так понял?

EP>Сейчас такой синтаксис:

EP>
EP>{
EP>    File a(/*...*/),b(/*...*/);
EP>    SCOPE_SUCCESS(&a,&b)
EP>    {
EP>        b.flush();
EP>        a.flush();
EP>    } SCOPE_SUCCESS_END
EP>    /* ... */
EP>    /* use a and b*/
EP>}
EP>

С помощью лямбд можно сделать

{
    File a(/*...*/),b(/*...*/);
    SCOPE_SUCCESS
    {
        b.flush();
        a.flush();
    };
    /* ... */
    /* use a and b*/
}


KK>>Многофункциональные бросающие деструкторы в топку.


EP>То что они многофункциональные и бросающие — это деталь реализации, которая скрыта за слоем абстракции.

EP>Пользователь реализует DEFERRED_ACTION и RESOURCE_RELEASE по отдельности.

Зато клиентская сторона в общем случае не может инициировать отложенное действие и освобождение ресурсов по отдельности. Деструктор — это не только тело { /*...*/ }, но и уничтожение подобъектов, которое компилятор реализует сам.
Re[8]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 11:29
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>Мне это не очевидно. Здесь нарушается принцип "одна задача — одна функция", что ИМХО усложняет понимание кода. От использования

EP>>Видимо лучше переименовать
EP>>TWO_STAGE_DESTRUCTOR_DEFERRED -> DEFERRED_ACTION
EP>>TWO_STAGE_DESTRUCTOR_RELEASE -> RESOURCE_RELEASE
EP>>и для симметрии конструктор -> RESOURCE_ACQUISITION
EP>>Одна задача — одна функция.
KK>В конечном счёте всё равно два действия запихиваются в один деструктор. Или я что-то не так понял?

Да, но какая разница? Это же деталь реализации. "single responsibility principle" ведь не нарушается.

EP>>Сейчас такой синтаксис:

EP>>
EP>>{
EP>>    File a(/*...*/),b(/*...*/);
EP>>    SCOPE_SUCCESS(&a,&b)
EP>>    {
EP>>        b.flush();
EP>>        a.flush();
EP>>    } SCOPE_SUCCESS_END
EP>>    /* ... */
EP>>    /* use a and b*/
EP>>}
EP>>

KK>С помощью лямбд можно сделать
KK>
{
KK>    File a(/*...*/),b(/*...*/);
KK>    SCOPE_SUCCESS
KK>    {
KK>        b.flush();
KK>        a.flush();
KK>    };
KK>    /* ... */
KK>    /* use a and b*/
KK>}

С лямбда вот такой синтаксис:
{
    File a(/*...*/),b(/*...*/);
    SCOPE_SUCCESS_ALL(&)
    {
        b.flush();
        a.flush();
    };
    /* ... */
    /* use a and b*/
}
есть прям сейчас — то есть всего один макро шаг от варианта выше.

Можно вообще сделать:
{
    File a(/*...*/),b(/*...*/);
    scope(success)
    {
        b.flush();
        a.flush();
    };
    /* ... */
    /* use a and b*/
}

кстати, надо будет такой пример добавить.


KK>>>Многофункциональные бросающие деструкторы в топку.

EP>>То что они многофункциональные и бросающие — это деталь реализации, которая скрыта за слоем абстракции.
EP>>Пользователь реализует DEFERRED_ACTION и RESOURCE_RELEASE по отдельности.
KK>Зато клиентская сторона в общем случае не может инициировать отложенное действие и освобождение ресурсов по отдельности.

этот вопрос решается одним флагом.

KK>Деструктор — это не только тело { /*...*/ }, но и уничтожение подобъектов, которое компилятор реализует сам.


да, но не вижу в этом каких-либо проблем
Re[10]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 13.10.12 12:17
Оценка: :)
Здравствуйте, Erop, Вы писали:
E>>Ну дык мозг тоже нужен. Хочется логов — сделай свою обертку над thread и приверни в деструктор логи, проблем нет... Библиотечный класс всяко не может ничего логировать.

E>Логов захочется из других ниток, а им не дадут сброситься, потому, что терминируют

Ты увидишь в логах — поток не был завершен правильно, произошел терминейт. Дальше начнешь разбираться, как эту ситуацию победить. Как победишь — получишь логи из самого потока...
Re[4]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: rg45 СССР  
Дата: 13.10.12 12:45
Оценка: +2 :)
Здравствуйте, Ku-ku, Вы писали:

KK>Except where noted, evaluations of operands of individual operators and of subexpressions of individual

KK>expressions are unsequenced.
KK>Тут как раз этот случай. Но как я уже сказал, в случае с инициализацией p достаточно просто заменить круглые скобки на фигурные.
KK>Точек следования больше нет
R>>а это значит, что компилятор не приступит вычислению следующего аргумента, пока не закончит вычисление предыдущего.
KK>Откуда это следует? Известно только то, что каждый конструктор будет вызван после вычисления соответствующего аргумента. Взаимный порядок вычисления этих аргументов между собой не определён.

Я стесняюсь спросить, ты действительно новенький, или кто-то из "стареньких", но под новым ником? Манера общения у тебя знакомая. Признавайся!
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[11]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 13.10.12 12:56
Оценка:
Здравствуйте, enji, Вы писали:

E>>Логов захочется из других ниток, а им не дадут сброситься, потому, что терминируют

E>Ты увидишь в логах — поток не был завершен правильно, произошел терминейт. Дальше начнешь разбираться, как эту ситуацию победить. Как победишь — получишь логи из самого потока...

Понимаю, всё для клиента, типа?
Вообще-то коммерческое поделие должно не молча дохнуть, а дампить максиму инфы, для отладки...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 13.10.12 13:10
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

KK>>В конечном счёте всё равно два действия запихиваются в один деструктор. Или я что-то не так понял?


EP>Да, но какая разница? Это же деталь реализации.


Реализации чего? Деструктора что ли? Дык деструктор — часть интерфейса класса, и его исконное назначение — освобождение ресурсов. Активные действия типа flush к освобождению никаким боком не относятся.

EP>С лямбда вот такой синтаксис:

EP>
EP>{
EP>    File a(/*...*/),b(/*...*/);
EP>    SCOPE_SUCCESS_ALL(&)
EP>    {
EP>        b.flush();
EP>        a.flush();
EP>    };
EP>    /* ... */
EP>    /* use a and b*/
EP>}
EP>
есть прям сейчас

Я как-то не подумал, что некоторые переменные может быть полезно захватывать по значению

KK>>>>Многофункциональные бросающие деструкторы в топку.

EP>>>То что они многофункциональные и бросающие — это деталь реализации, которая скрыта за слоем абстракции.
EP>>>Пользователь реализует DEFERRED_ACTION и RESOURCE_RELEASE по отдельности.
KK>>Зато клиентская сторона в общем случае не может инициировать отложенное действие и освобождение ресурсов по отдельности.

EP>этот вопрос решается одним флагом.


То есть клиент ещё о флагах должен помнить? ИМХО лучше эти две задачи сразу разделить на уровне интерфейса и выполнять по отдельности.

На крайняк, если хочется краткости, вместо SCOPE_SUCCESS сделать какой-нибудь костыль COMMIT_ON_SCOPE_SUCCESS

{
    File a(/*...*/),b(/*...*/);
    COMMIT_ON_SCOPE_SUCCESS(a, b);
    /* ... */
    /* use a and b*/
}

который бы дёргал a.commit() и b.commit(). А commit реализовать через flush. Человек, читающий код, сразу видит, что в конце блока следует ожидать какие-то нетривиальные действия.

Кстати, вопрос на засыпку. Ты помнишь когда деструктор имеет спецификацию исключений noexcept(false) и когда noexcept(true)? И что случается, когда noexcept функция пытается бросать исключения?
Re[5]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ku-ku  
Дата: 13.10.12 13:13
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>Я стесняюсь спросить, ты действительно новенький, или кто-то из "стареньких", но под новым ником? Манера общения у тебя знакомая. Признавайся!


Я тут проездом
Re[5]: C++ и RAII
От: andyp  
Дата: 13.10.12 13:13
Оценка:
Здравствуйте, Ku-ku, Вы писали:

A>>Финализация работы с ресурсом может быть разной в зависимости от того, что с ним произошло.


KK>Например?


Например база данных. Требуется откатить несколько предыдущих транзакций из-за неудавшейся.
A>>>>3. есть такие ресурсы с которыми непонятно что делать в деструкторе — ну типа нитей или файлов, про которые народ писал

KK>>>Закрывать хэндлы.

KK>>>Пластмассовое мусорное ведро предназначено для утилизации мусора, при попытке сварить в нём кофе оно может расплавиться. Это серьёзный недостаток ведра или не?

A>>Я не против. Я за то, чтобы иметь возможность снова обратиться к ресурсу после такого вызова деструктора.


KK>То есть закрыли хэндл и после обращаемся к ресурсу?


Да, например снова пытаемся открыть тот же файл. Иначе нет смысла восстанавливаться после ошибки.

A>>Смысл поста в том, что про RAII говорят как о технике, позволяющей автоматически решать проблему освобождения ресурсов, но на мой взгляд в сложных ситуациях это не так.


KK>Сложная ситуация — это когда "освобождение ресурса" таковым на самом деле не является?


A>>Мало того, RAII обертки могут быть бесполезны для некоторых ресурсов.


KK>Ну если API кривое, то да, выпрямить его не всегда возможно.


Вобщем, вопрос видимо в том, что понимать под "ресурсом". Но как-то неконкретно и бесплодно все это...
Re[5]: C++ и RAII
От: andyp  
Дата: 13.10.12 13:24
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Звучит похоже на BOOST_SCOPE_EXIT, SCOPE_FAILURE, SCOPE_SUCCESS:

EP>
EP>{
EP>    SCOPE_FAILURE(void) {
EP>        log("could not update user info");
EP>    } SCOPE_FAILURE_END

EP>    File passwd("/etc/passwd");

EP>    SCOPE_SUCCESS( (&passwd) ) {
EP>        passwd.flush();
EP>    } SCOPE_SUCCESS_END

EP>    BOOST_SCOPE_EXIT( (&passwd) ) {
EP>        passwd.close();
EP>    } BOOST_SCOPE_EXIT_END

EP>    // ...
EP>}
EP>


Да, именно такое. Спасибо. Просто иногда RAII обертка используется только в одном месте кода и не хочется класс городить. Но это наверное в основном вопрос синтаксического сахара.
Re[12]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 13.10.12 13:54
Оценка:
Здравствуйте, Erop, Вы писали:

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


E>>>Логов захочется из других ниток, а им не дадут сброситься, потому, что терминируют

E>>Ты увидишь в логах — поток не был завершен правильно, произошел терминейт. Дальше начнешь разбираться, как эту ситуацию победить. Как победишь — получишь логи из самого потока...

E>Понимаю, всё для клиента, типа?

E>Вообще-то коммерческое поделие должно не молча дохнуть, а дампить максиму инфы, для отладки...

Ну так пиши обертку над thread, которая пишет в лог проблемы. Плюс она может делать какие-то действия вместо terminate — например сигнализировать потоку, что пора закрываться, или подождать несколько секунд его закрытия...
Разговор о том, что общая библиотека всего этого сделать не может, поэтому terminate — вполне разумное поведение для нее
Re[5]: 1) Родовая травма: исключеиния в деструкторах
От: Vain Россия google.ru
Дата: 13.10.12 14:35
Оценка:
Здравствуйте, Ku-ku, Вы писали:

V>>плохая идея завершать поток вместе с объектом, вот указатель с подсчётом ссылок это другое дело.

KK>Этот код не завершает поток, а только закрывает хэндл.
KK>joinable() может вернуть false, когда поток ещё выполняется, если до этого была вызвана функция detach(), т.е. мы явно указали, что не хотим следить за потоком через объект std::thread.
KK>Обоснование вызова std::terminate приведено здесь.
Вообщето под некоторыми платформами terminate может быть не реализован, что автоматом приводит к "ликам" в виде неостановленных потоков. Если хотите терминировать поток — вызывайте функцию явно, а если она не поддерживается, то можно хотябы схватить ошибку компиляции, здесь же код станет либо автоматом некомпилируемым, либо вы об "лике" вы узнаете в последный момент (сюрприз!).
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[10]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 16:22
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>В конечном счёте всё равно два действия запихиваются в один деструктор. Или я что-то не так понял?

EP>>Да, но какая разница? Это же деталь реализации.
KK>Реализации чего? Деструктора что ли?

Деталь реализации DEFERRED_ACTION и RESOURCE_RELEASE.

KK>Дык деструктор — часть интерфейса класса, и его исконное назначение — освобождение ресурсов.


Исконное, не исконное — какая разница? В C++ много вещей которые успешно используются несмотря на исконное назначение. enable_if вообще добавили в стандарт, хотя наверняка авторы SFINAE о нём даже не думали.

KK>Активные действия типа flush к освобождению никаким боком не относятся.


К освобождению не относятся, но, тем не менее, неплохо иметь возможность их за-schedule'ить прям перед освобождением, автоматически.
Постоянные дискуссии вокруг этого самого flush — тому подтверждение

KK>>>>>Многофункциональные бросающие деструкторы в топку.

EP>>>>То что они многофункциональные и бросающие — это деталь реализации, которая скрыта за слоем абстракции.
EP>>>>Пользователь реализует DEFERRED_ACTION и RESOURCE_RELEASE по отдельности.
KK>>>Зато клиентская сторона в общем случае не может инициировать отложенное действие и освобождение ресурсов по отдельности.
EP>>этот вопрос решается одним флагом.
KK>То есть клиент ещё о флагах должен помнить? ИМХО лучше эти две задачи сразу разделить на уровне интерфейса и выполнять по отдельности.

не должен — их можно легко вшить в DEFERRED_ACTION и RESOURCE_RELEASE.

KK>На крайняк, если хочется краткости, вместо SCOPE_SUCCESS сделать какой-нибудь костыль COMMIT_ON_SCOPE_SUCCESS

KK>
{
KK>    File a(/*...*/),b(/*...*/);
KK>    COMMIT_ON_SCOPE_SUCCESS(a, b);
KK>    /* ... */
KK>    /* use a and b*/
KK>}

KK>который бы дёргал a.commit() и b.commit(). А commit реализовать через flush. Человек, читающий код, сразу видит, что в конце блока следует ожидать какие-то нетривиальные действия.

Да, я думал об этом. Но что именно дёргать, думал реализовать через traits: по умолчанию ничего бы не дёргалось(либо даже лучше вообще compile-error), а если класс поддерживает коммит в той или иной степени (может даже набор нескольких действий) — то специализация.

KK>Кстати, вопрос на засыпку. Ты помнишь когда деструктор имеет спецификацию исключений noexcept(false) и когда noexcept(true)?


По-моему планировали сделать noexcept(true) по-умолчанию, к чему в итоге пришли — ещё не знаю. Планировал посмотреть, никак руки не доходили — мне это нужно будет знать для scope(success), так как из его деструктора — кидать вполне легально.

KK>И что случается, когда noexcept функция пытается бросать исключения?


по-моему либо UB либо terminate
Re[12]: 1) Родовая травма: исключеиния в деструкторах
От: Centaur Россия  
Дата: 13.10.12 16:24
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>>Ты увидишь в логах — поток не был завершен правильно, произошел терминейт. Дальше начнешь разбираться, как эту ситуацию победить. Как победишь — получишь логи из самого потока...


E>Понимаю, всё для клиента, типа?

E>Вообще-то коммерческое поделие должно не молча дохнуть, а дампить максиму инфы, для отладки...

Вообще-то terminate и дампит максимум инфы для отладки — core dump.
Re[6]: C++ и RAII
От: Ku-ku  
Дата: 13.10.12 17:47
Оценка:
Здравствуйте, andyp, Вы писали:

A>Например база данных. Требуется откатить несколько предыдущих транзакций из-за неудавшейся.


Операции транзакции — это не ресурсы, и их откат — это не освобождение ресурсов с точки зрения модели захват-использование-освобождение. Неудивительно, что с помощью одного RAII изящное решение получить не удаётся.

A>>>>>3. есть такие ресурсы с которыми непонятно что делать в деструкторе — ну типа нитей или файлов, про которые народ писал


KK>>То есть закрыли хэндл и после обращаемся к ресурсу?


A>Да, например снова пытаемся открыть тот же файл.


В чём проблема создать новый объект? Или сделать так, чтобы время жизни старого не закончилось слишком рано?

A>Иначе нет смысла восстанавливаться после ошибки.


Ну почему же нет? Программа что-то записывала на диск и тот внезапно переполнился. Пользователь видит ошибку, но возобновлять попытку записи не хочет. Такого не может быть?

A>>>Смысл поста в том, что про RAII говорят как о технике, позволяющей автоматически решать проблему освобождения ресурсов, но на мой взгляд в сложных ситуациях это не так.


A>Вобщем, вопрос видимо в том, что понимать под "ресурсом".


Я затрудняюсь дать точное определение, но транзакции — это явно из другой оперы.
Re[6]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 13.10.12 17:47
Оценка:
Здравствуйте, Vain, Вы писали:

V>Вообщето под некоторыми платформами terminate может быть не реализован


Это какие платформы например?

V>что автоматом приводит к "ликам" в виде неостановленных потоков. Если хотите терминировать поток — вызывайте функцию явно, а если она не поддерживается, то можно хотябы схватить ошибку компиляции, здесь же код станет либо автоматом некомпилируемым, либо вы об "лике" вы узнаете в последный момент (сюрприз!).


Вообще-то задача std::terminate — убивать программу целиком, а не отдельный поток.
Re[11]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 13.10.12 17:54
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Деталь реализации DEFERRED_ACTION и RESOURCE_RELEASE.


DEFERRED_ACTION и RESOURCE_RELEASE — это детали реализации самих себя? Что-то я потерял нить. Например, у нас есть класс File. Что делает его деструктор?

KK>>Дык деструктор — часть интерфейса класса, и его исконное назначение — освобождение ресурсов.


EP>Исконное, не исконное — какая разница?


Разница в самодокументируемости кода. Код, использующий конструкции языка по их прямому назначению, проще понимать.

Ещё один важный момент. Когда бросание исключений из деструкторов запрещено стандартом кодирования, ты знаешь, что выход из любого блока в программе бессбойный, не надо лезть в документацию или внутренности деструкторов. Разрешив бросать исключения из деструкторов, ты уже не можешь на это рассчитывать. Тебе, скорее всего, придётся часто думать, стреляет какой-нибудь деструктор исключениями или нет. Оно того стоит?

EP>В C++ много вещей которые успешно используются несмотря на исконное назначение.


Однако на освоение всего этого добра уходят годы. Специалистов, которые хорошо во всё это втыкаются, считанные единицы.

EP>enable_if вообще добавили в стандарт, хотя наверняка авторы SFINAE о нём даже не думали.


Хороший пример плохой фичи. Лучше бы спец. языковую конструкцию ввели вместо этого уродства.

KK>>То есть клиент ещё о флагах должен помнить? ИМХО лучше эти две задачи сразу разделить на уровне интерфейса и выполнять по отдельности.


EP>не должен — их можно легко вшить в DEFERRED_ACTION и RESOURCE_RELEASE.


Если я не хочу делать DEFERRED_ACTION, но хочу выполнить RESOURCE_RELEASE, что мне для этого надо?

KK>>Кстати, вопрос на засыпку. Ты помнишь когда деструктор имеет спецификацию исключений noexcept(false) и когда noexcept(true)?


EP>По-моему планировали сделать noexcept(true) по-умолчанию


Сделали вот как:

A declaration of a destructor that does not have an exception-specification is implicitly considered to have
the same exception-specification as an implicit declaration (15.4).

An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is
an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment
operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only
if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall
allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions
if every function it directly invokes allows no exceptions.

То есть в большинстве случаев когда ничего не указывается, деструктор неявно становится noexcept(true).

KK>>И что случается, когда noexcept функция пытается бросать исключения?


EP>по-моему либо UB либо terminate


Whenever an exception is thrown and the search for a handler (15.3) encounters the outermost block of a
function with an exception-specification that does not allow the exception, then,
— if the exception-specification is a dynamic-exception-specification, the function std::unexpected() is
called (15.5.2),
— otherwise, the function std::terminate() is called (15.5.1).
Re[12]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 13.10.12 18:00
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>Если я не хочу делать DEFERRED_ACTION, но хочу выполнить RESOURCE_RELEASE, что мне для этого надо?


Поправлюсь. Если я не хочу делать DEFERRED_ACTION, но хочу выполнить полную очистку ресурсов, а не только то, что делает RESOURCE_RELEASE, что мне для этого надо?
Re[7]: 1) Родовая травма: исключеиния в деструкторах
От: Vain Россия google.ru
Дата: 13.10.12 18:13
Оценка:
Здравствуйте, Ku-ku, Вы писали:

V>>Вообщето под некоторыми платформами terminate может быть не реализован

KK>Это какие платформы например?
имел ввиду убивать поток, к примеру, PowerTv ос это не умеет

V>>что автоматом приводит к "ликам" в виде неостановленных потоков. Если хотите терминировать поток — вызывайте функцию явно, а если она не поддерживается, то можно хотябы схватить ошибку компиляции, здесь же код станет либо автоматом некомпилируемым, либо вы об "лике" вы узнаете в последный момент (сюрприз!).

KK>Вообще-то задача std::terminate — убивать программу целиком, а не отдельный поток.
Если это имелось ввиду, то вообще жесть
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: C++ и RAII
От: ioj Ниоткуда  
Дата: 13.10.12 20:25
Оценка: 1 (1)
Здравствуйте, andyp, Вы писали:

A>А вот мне в RAII не нравится следующее:

A>1. Хрень случается в одном месте кода, а обрабатывать ее приходится в другом.
A>2. Когда попадаем в деструктор не совсем ясно, то ли мы раскручаем стек из-за исключения, связанного с нашим ресурсом, то ли все нормально (ну те нет исключения), то ли исключение вызвано чем-то другим (не нашим ресурсом). Конечно в с++11 есть current_exception(), но проверять и отлавливать свои исключения...

это не проблема RAII, это проблема исключений как таковых, т.к. они вносят сторонний control flow.
нормально делай — нормально будет
Re[12]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 21:46
Оценка:
Здравствуйте, Ku-ku, Вы писали:

EP>>Деталь реализации DEFERRED_ACTION и RESOURCE_RELEASE.

KK>DEFERRED_ACTION и RESOURCE_RELEASE — это детали реализации самих себя? Что-то я потерял нить.

Нет, то что для реализации DEFERRED_ACTION и RESOURCE_RELEASE используется двух-фазный деструктор — это деталь реализации.

KK>Например, у нас есть класс File. Что делает его деструктор?


Полностью рабочий пример:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <ostream>
#include <cstdio>
#include <stdexcept>
#include <two_stage_destructor.hpp>
using namespace std;

class File
{
    FILE *file_handle;
public:
    explicit File(const char *filename)
    {
        if( (file_handle=fopen(filename,"w"))==0 )
            throw runtime_error("fopen failed");
    }
    void puts(const char *str)
    {
        if(fputs(str,file_handle)<0)
            throw runtime_error("fputs failed");
    }
    TWO_STAGE_DESTRUCTOR_RELEASE(File)
    {
        fclose(file_handle);
    }
    TWO_STAGE_DESTRUCTOR_DEFERRED(File)
    {
        if(fflush(file_handle)!=0)
            throw runtime_error("fflush failed");
    }
};

int main(int,char *[])
{
    try
    {
        File test("out.txt");
        test.puts("Hello");
    }
    catch(const exception &e)
    {
        cout << e.what();
    }
    try
    {
        File test("out2.txt");
        test.puts("Hello2");
        throw runtime_error("test");
    }
    catch(const exception &e)
    {
        cout << e.what();
    }
    return 0;
}

fflush вызывается только в первом случае.


KK>>>Дык деструктор — часть интерфейса класса, и его исконное назначение — освобождение ресурсов.

EP>>Исконное, не исконное — какая разница?
KK>Разница в самодокументируемости кода. Код, использующий конструкции языка по их прямому назначению, проще понимать.

Да, но иногда в язык добавляются новые конструкции — ничего плохого в этом нет.
Конкретно deferred action — это один из вариантов решения проблемы flush и ему подобных. Какая разница что используется внутри, для реализации — чистокровные языковые конструкции или нет?

KK>Ещё один важный момент. Когда бросание исключений из деструкторов запрещено стандартом кодирования, ты знаешь, что выход из любого блока в программе бессбойный, не надо лезть в документацию или внутренности деструкторов. Разрешив бросать исключения из деструкторов, ты уже не можешь на это рассчитывать. Тебе, скорее всего, придётся часто думать, стреляет какой-нибудь деструктор исключениями или нет. Оно того стоит?


Моя точка зрения на это: http://www.rsdn.ru/forum/cpp/4909480.1
Автор: Evgeny.Panasyuk
Дата: 28.09.12

При текущем положении дел, кидание исключений из деструкторов опасное занятие, даже если не брать во внимание технические проблемы — большинство кода просто не рассчитано на исключения из деструкторов. Я не агитирую за необдуманное кидание исключений из деструкторов налево и направо (несмотря на название топика )
Но в то же время, помимо непосредственно деструкции и освобождения ресурсов, на деструкторы навешиваются разного рода отложенные действия, например тот же flush, которые могут фэйлится. Проглатывание всех этих фэйлов по-умолчанию — решение далёкое от идеала.

DEFERRED + RELEASE это попытка найти решение проблемы flush, причём практическая — решение можно "пощупать" в действии, на живом коде.

EP>>В C++ много вещей которые успешно используются несмотря на исконное назначение.

KK>Однако на освоение всего этого добра уходят годы. Специалистов, которые хорошо во всё это втыкаются, считанные единицы.

Ну может быть, когда-нибудь, появятся "специально обученные люди", использующие Deferred Actions

EP>>enable_if вообще добавили в стандарт, хотя наверняка авторы SFINAE о нём даже не думали.

KK>Хороший пример плохой фичи. Лучше бы спец. языковую конструкцию ввели вместо этого уродства.

Во-первых, добавить enable_if намного проще чем языковую конструкцию.
Во-вторых, таки планируется добавить языковую конструкцию — static if.
И в-третьих, то что люди использовали enable_if, оно стало распространённой практикой, как раз и показало необходимость в чистокровной языковой конструкции.

KK>>>То есть клиент ещё о флагах должен помнить? ИМХО лучше эти две задачи сразу разделить на уровне интерфейса и выполнять по отдельности.

EP>>не должен — их можно легко вшить в DEFERRED_ACTION и RESOURCE_RELEASE.
KK>Если я не хочу делать DEFERRED_ACTION, но хочу выполнить RESOURCE_RELEASE, что мне для этого надо?
[...]
KK>Поправлюсь. Если я не хочу делать DEFERRED_ACTION, но хочу выполнить полную очистку ресурсов, а не только то, что делает RESOURCE_RELEASE, что мне для этого надо?

В смысле отменить DEFERRED_ACTION на стороне пользователя? Ну если это действительно нужно, можно автоматом добавить метод cancel_deferred_action().
RESOURCE_RELEASE — вызывается всегда, ничего дополнительно делать не нужно.

KK>>>Кстати, вопрос на засыпку. Ты помнишь когда деструктор имеет спецификацию исключений noexcept(false) и когда noexcept(true)?


KK>То есть в большинстве случаев когда ничего не указывается, деструктор неявно становится noexcept(true).


да, надо повнимательней изучить.
Re[13]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 13.10.12 21:55
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Вообще-то terminate и дампит максимум инфы для отладки — core dump.


Это зависит от ОС...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 13.10.12 21:57
Оценка: 1 (1)
Здравствуйте, enji, Вы писали:

E>Разговор о том, что общая библиотека всего этого сделать не может, поэтому terminate — вполне разумное поведение для нее


IMHO, разумное поведение -- регистрировать обработчик, с terminate в качестве обработчика по умолчанию...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: C++ и RAII
От: andyp  
Дата: 13.10.12 22:47
Оценка:
Хотелось бы сказать особое спасибо Егору за поднятую тему и Evgeny.Panasyuk за ссылки и замечание про current_exception(). В описании библиотеки Boost.ScopeExit, на которую ссылался Евгений, упоминается очень простая реализация scope_exit сделанная с использованием с++11 и классный ролик Александреску про реализацию scope(exit) в языке D. Ролик (особенно первые полчаса) показались очень релевантными в разговоре про RAII.
Re[3]: C++ и RAII
От: andyp  
Дата: 13.10.12 23:06
Оценка:
Кино можно посмотреть здесь — http://www.gotw.ca/gotw/047.htm
Re[3]: C++ и RAII
От: Evgeny.Panasyuk Россия  
Дата: 13.10.12 23:44
Оценка: 5 (1)
Здравствуйте, andyp, Вы писали:

A>Хотелось бы сказать особое спасибо Егору за поднятую тему и Evgeny.Panasyuk за ссылки и замечание про current_exception(). В описании библиотеки Boost.ScopeExit, на которую ссылался Евгений, упоминается очень простая реализация scope_exit сделанная с использованием с++11 и классный ролик Александреску про реализацию scope(exit) в языке D. Ролик (особенно первые полчаса) показались очень релевантными в разговоре про RAII.


Спасибо за отзыв
Ссылка на ролик — http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D .
Там, кстати, в самом конце видео, самый последний вопрос тоже про scope(*) и ScopeGuard.
С лямбдами С++11 действительно получается очень просто и близко к D.
Только что добавил пример:
try
{
    int some_var=1;
    cout << "Case #1: stack unwinding" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var << endl;
        ++some_var;
    };
    throw 1;
} catch(int){}
{
    int some_var=1;
    cout << "Case #2: normal exit" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var << endl;
        ++some_var;
    };
}
Re[14]: 1) Родовая травма: исключеиния в деструкторах
От: Centaur Россия  
Дата: 14.10.12 05:10
Оценка: :)
Здравствуйте, Erop, Вы писали:

C>>Вообще-то terminate и дампит максимум инфы для отладки — core dump.


E>Это зависит от ОС...


Не от ОС, а от реализации стандартной библиотеки. terminate зовёт terminate_handler, terminate_handler по умолчанию зовёт abort, abort поднимает сигнал SIGABRT (что в libc приводит к записи core dump). В Microsoft’овской реализации:

When the application is linked with a debug version of the run-time libraries, abort creates a message box with three buttons: Abort, Retry, and Ignore. If the user clicks Abort, the program aborts immediately. If the user clicks Retry, the debugger is called and the user can debug the program if just-in-time (JIT) debugging is enabled. If the user clicks Ignore, abort continues with its normal execution: creating the message box with the OK button.

abort also invokes an error reporting mechanism to report failures to Microsoft. This behavior can be disabled by calling _set_abort_behavior.


В любом случае, прикладному программисту никто не мешает повесить свой обработчик terminate_handler или сигнала SIGABRT, который сделает свой дамп.
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: Кодт Россия  
Дата: 14.10.12 07:27
Оценка: 1 (1)
Здравствуйте, Vain, Вы писали:

A>>
A>>   if (this->joinable()) // если поток еще выполняется
A>>      std::terminate(); // то это баг.
A>>

V>плохая идея завершать поток вместе с объектом, вот указатель с подсчётом ссылок это другое дело.

Вот именно от этой плохой идеи данный код и отучивает. Фигачит по пальцам.

То же самое будет и с подсчётом ссылок. Какая разница, вылетит terminate на деструкторе первого же std::thread или на деструкторе последнего shared_ptr<thread> ?
Перекуём баги на фичи!
Re[13]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 14.10.12 11:28
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

KK>>DEFERRED_ACTION и RESOURCE_RELEASE — это детали реализации самих себя? Что-то я потерял нить.


EP>Нет, то что для реализации DEFERRED_ACTION и RESOURCE_RELEASE используется двух-фазный деструктор — это деталь реализации.


Ты меня окончательно запутал Давай ещё раз по порядку.

Каждый класс имеет деструктор. Он может быть тривиальным, но всё равно должен присутствовать. Назначение деструктора, согласно задумке того, кто придумал деструкторы в C++, заключается в освобождении ресурсов. Во многих случаях деструктор вызывается компилятором автоматически. Хочешь ты того или нет, деструктор — это всегда часть интерфейса класса. Даже если сделать деструктор приватным или deleted, невозможность позвать деструктор — это неотъемлемая составляющая интерфейса.

Теперь давай разберёмся, сколько задач выполняет деструктор твоего File. Он выполняет или не выполняет какое-то отложенное действие и всегда выполняет освобождение ресурса. То есть этот деструктор ещё имеет два независимых постусловия, одно из которых может нарушиться, а другое должно выполняться всегда. Выброс исключения сигнализирует о нарушении ровно одного постусловий из двух. ИМХО, нарушение single responsibility principle налицо.

KK>>Активные действия типа flush к освобождению никаким боком не относятся.


EP>К освобождению не относятся, но, тем не менее, неплохо иметь возможность их за-schedule'ить прям перед освобождением, автоматически.


Вот ты уверен, что пользователь всегда захочет делать такое шедуливание перед освобождением? Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда? ИМХО, операции типа flush должны выполняться по требованию клиента, а не быть зашитыми намертво в деструктор. Сокращение кода за счёт переизбытка неявных действий — это псевдоудобство. По мне так лучше дописать какую-нибудь управляющую конструкцию — например, тот же SCOPE_SUCCESS, — если понятность кода от этого существенно увеличится.

EP>>>enable_if вообще добавили в стандарт, хотя наверняка авторы SFINAE о нём даже не думали.

KK>>Хороший пример плохой фичи. Лучше бы спец. языковую конструкцию ввели вместо этого уродства.

EP>Во-первых, добавить enable_if намного проще чем языковую конструкцию.


Кто бы сомневался. Эту хреновину так быстро добавили в стандартную библиотеку, что к моменту появления alias templates её уже поздно было допиливать до

template <bool Condition, typename T = void>
using enable_if = typename enable_if_impl<Condition, T>::type;

что хотя бы позволило б юзерам использовать менее громоздкий синтаксис.

EP>Во-вторых, таки планируется добавить языковую конструкцию — static if.


Ага, читал я пропозалы по static if. Там фиг поймёшь, что эти if из себя представляют: то ли условное добавление объявления в scope, то ли условное включение функции в список кандидатов — это то, для чего сейчас применяется std::enable_if. Непонятно как должен происходить поиск имён — очевидно, что условное добавление имён ломает на корню синтаксический разбор определений шаблонов. Короче муть какая-то.

EP>И в-третьих, то что люди использовали enable_if, оно стало распространённой практикой, как раз и показало необходимость в чистокровной языковой конструкции.


По-моему, необходимость чистокровной языковой конструкции была видна с самого начала, но из-за её отсутствия приходилось искать обходные пути. В результате поисков решения проблемы выбора родился костыль в виде enable_if, который впоследствии зачем-то решили поместить в стандартную библиотеку. Ещё была попытка замутить концепты, призванные решать подобные задачи за счёт введения каких-то супер-пупер абстракций, но попытка с треском провалилась.

KK>>Поправлюсь. Если я не хочу делать DEFERRED_ACTION, но хочу выполнить полную очистку ресурсов, а не только то, что делает RESOURCE_RELEASE, что мне для этого надо?


EP>В смысле отменить DEFERRED_ACTION на стороне пользователя? Ну если это действительно нужно, можно автоматом добавить метод cancel_deferred_action().


А может вместо того чтобы явно отменять deferred action лучше бы его явно разрешать?
Re[4]: C++ и RAII
От: Ku-ku  
Дата: 14.10.12 12:15
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>С лямбдами С++11 действительно получается очень просто и близко к D.


В чём-то даже круче. Можно сохранять часть контекста, захватывая отдельные переменные по значению.
Re[5]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 14.10.12 12:16
Оценка:
Здравствуйте, Кодт, Вы писали:

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


A>>>
A>>>   if (this->joinable()) // если поток еще выполняется
A>>>      std::terminate(); // то это баг.
A>>>

V>>плохая идея завершать поток вместе с объектом, вот указатель с подсчётом ссылок это другое дело.

К>Вот именно от этой плохой идеи данный код и отучивает. Фигачит по пальцам.


Суть в том, что пользователя вынуждают явно вызывать join или detach. Вызывая join, он сообщает, что хочет ждать завершение потока, а вызывая detach, — что он хочет отпустить поток в свободное плавание. В обоих случаях ресурсы, отведённые под выполнение потока, освобождаются не раньше, чем поток самопроизвольно завершится или его насильно прибьют каким-нибудь способом. Закрытие хэндла не приводит к убийству потока, это только уменьшение счётчика наблюдателей за потоком.
Re[14]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 14.10.12 14:43
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>DEFERRED_ACTION и RESOURCE_RELEASE — это детали реализации самих себя? Что-то я потерял нить.

EP>>Нет, то что для реализации DEFERRED_ACTION и RESOURCE_RELEASE используется двух-фазный деструктор — это деталь реализации.
KK>Ты меня окончательно запутал Давай ещё раз по порядку.

Давай

KK>Теперь давай разберёмся, сколько задач выполняет деструктор твоего File. Он выполняет или не выполняет какое-то отложенное действие и всегда выполняет освобождение ресурса. То есть этот деструктор ещё имеет два независимых постусловия, одно из которых может нарушиться, а другое должно выполняться всегда. Выброс исключения сигнализирует о нарушении ровно одного постусловий из двух. ИМХО, нарушение single responsibility principle налицо.


Сфейлится может только Deferred. Release не фейлится — "“letting go of a resource” must never fail"
http://www.rsdn.ru/forum/cpp/4927484.1
Автор: Evgeny.Panasyuk
Дата: 13.10.12

Одним из вариантов является введение понятия отложенных действий

, что позволяет чётко разграничить непосредственно освобождение ресурсов(которое не фейлится) и различные нетривиальные действия над этими ресурсами, которые выполняются перед их освобождением и по сути своей могут фейлится.

Сейчас, без двух фаз Deferred и Release, мы имеем фактически только одну — Release. Которая по всем coding standards и best practices не должна кидать исключений.

KK>>>Активные действия типа flush к освобождению никаким боком не относятся.

EP>>К освобождению не относятся, но, тем не менее, неплохо иметь возможность их за-schedule'ить прям перед освобождением, автоматически.
KK>Вот ты уверен, что пользователь всегда захочет делать такое шедуливание перед освобождением?

Нет, я не утверждаю что deferred действия нужны всегда.

KK>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?


Зависит от use-case — может пользователю будет достаточно того, что программа не вернёт EXIT_SUCCESS на не пойманном исключении.

KK>ИМХО, операции типа flush должны выполняться по требованию клиента, а не быть зашитыми намертво в деструктор. Сокращение кода за счёт переизбытка неявных действий — это псевдоудобство.


В темах про кидающие деструкторы, откладывание flush таким образом, чтобы была возможность поймать его исключение — является одной из самый главных претензий к языку. Например 1
Автор: Erop
Дата: 13.10.12
, 2. Значит кому-то это всё-таки нужно.

KK>По мне так лучше дописать какую-нибудь управляющую конструкцию — например, тот же SCOPE_SUCCESS, — если понятность кода от этого существенно увеличится.


Понятность увеличивается — не спорю. И в production (по-крайней мере на данный момент), я бы как раз использовал SCOPE_SUCCESS.
Но, например, если в проекте есть класс File_deferred, и все разработчики знают как он работает, и что от него ожидать — не вижу ничего плохого в его использовании.

EP>>>>enable_if вообще добавили в стандарт, хотя наверняка авторы SFINAE о нём даже не думали.

KK>>>Хороший пример плохой фичи. Лучше бы спец. языковую конструкцию ввели вместо этого уродства.
EP>>Во-первых, добавить enable_if намного проще чем языковую конструкцию.
KK>Кто бы сомневался. Эту хреновину так быстро добавили в стандартную библиотеку, что к моменту появления alias templates её уже поздно было допиливать до
KK>
template <bool Condition, typename T = void>
KK>using enable_if = typename enable_if_impl<Condition, T>::type;

KK>что хотя бы позволило б юзерам использовать менее громоздкий синтаксис.

согласен, было бы удобней — не надо писать typename + ::type.

EP>>Во-вторых, таки планируется добавить языковую конструкцию — static if.

KK>Ага, читал я пропозалы по static if. Там фиг поймёшь, что эти if из себя представляют: то ли условное добавление объявления в scope, то ли условное включение функции в список кандидатов — это то, для чего сейчас применяется std::enable_if. Непонятно как должен происходить поиск имён — очевидно, что условное добавление имён ломает на корню синтаксический разбор определений шаблонов. Короче муть какая-то.

У него несколько предназначений:
1. как у enable_if
2. в scope функций — то есть можно код выключать/включать
3. в scope объявлений — например можно в классе выключать некоторые члены

Стандартизировать могут разные их комбинации. Но 1 вариант будет наверняка.
В языке D, кстати, есть static if.
Andrei Alexandrescu. Static If I Had a Hammer — там и enable_if упоминается.

EP>>И в-третьих, то что люди использовали enable_if, оно стало распространённой практикой, как раз и показало необходимость в чистокровной языковой конструкции.

KK>По-моему, необходимость чистокровной языковой конструкции была видна с самого начала, но из-за её отсутствия приходилось искать обходные пути. В результате поисков решения проблемы выбора родился костыль в виде enable_if, который впоследствии зачем-то решили поместить в стандартную библиотеку. Ещё была попытка замутить концепты, призванные решать подобные задачи за счёт введения каких-то супер-пупер абстракций, но попытка с треском провалилась.

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

KK>>>Поправлюсь. Если я не хочу делать DEFERRED_ACTION, но хочу выполнить полную очистку ресурсов, а не только то, что делает RESOURCE_RELEASE, что мне для этого надо?

EP>>В смысле отменить DEFERRED_ACTION на стороне пользователя? Ну если это действительно нужно, можно автоматом добавить метод cancel_deferred_action().
KK>А может вместо того чтобы явно отменять deferred action лучше бы его явно разрешать?

Если нужно не отменять, а именно явно разрешать, то наверное в этом случае лучше использовать что-то типа SCOPE_SUCCESS которое дёргает deferred метод описанный в специализации traits.
Re[8]: 1) Родовая травма: исключеиния в деструкторах
От: Кодт Россия  
Дата: 14.10.12 15:45
Оценка:
Здравствуйте, Vain, Вы писали:

KK>>Вообще-то задача std::terminate — убивать программу целиком, а не отдельный поток.

V>Если это имелось ввиду, то вообще жесть

Это не жесть, а ритуальное самоубийство. Не пишите бажные программы, не доводите их до наложения рук.
Перекуём баги на фичи!
Re[6]: 1) Родовая травма: исключеиния в деструкторах
От: Кодт Россия  
Дата: 14.10.12 15:45
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>Суть в том, что пользователя вынуждают явно вызывать join или detach. Вызывая join, он сообщает, что хочет ждать завершение потока, а вызывая detach, — что он хочет отпустить поток в свободное плавание. В обоих случаях ресурсы, отведённые под выполнение потока, освобождаются не раньше, чем поток самопроизвольно завершится или его насильно прибьют каким-нибудь способом. Закрытие хэндла не приводит к убийству потока, это только уменьшение счётчика наблюдателей за потоком.


Поскольку хэндл у нас ровно один — объект std::thread — то и наблюдатель ровно один. (Если, конечно, мы не лезем в обход и не получаем второй хэндл средствами ОС). Хэндл умер, наблюдателей нет, поток доживает самостоятельно без присмотра.

Хочется больше наблюдателей — делаем shared_ptr<thread>.
Перекуём баги на фичи!
Re[9]: 1) Родовая травма: исключеиния в деструкторах
От: Vain Россия google.ru
Дата: 14.10.12 17:37
Оценка:
Здравствуйте, Кодт, Вы писали:

KK>>>Вообще-то задача std::terminate — убивать программу целиком, а не отдельный поток.

V>>Если это имелось ввиду, то вообще жесть
К>Это не жесть, а ритуальное самоубийство. Не пишите бажные программы, не доводите их до наложения рук.
спасибо кэп
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[6]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: jazzer Россия Skype: enerjazzer
Дата: 14.10.12 17:44
Оценка: +1 :)
Здравствуйте, Erop, Вы писали:

E>...какой процент программистов не в курсе существования такой проблемы, и, соответственно, не предпринимает мер, что бы её избежать...


все, кто прочитал книжку Саттера, в курсе, не волнуйся.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[15]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 14.10.12 18:34
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

KK>>Теперь давай разберёмся, сколько задач выполняет деструктор твоего File. Он выполняет или не выполняет какое-то отложенное действие и всегда выполняет освобождение ресурса. То есть этот деструктор ещё имеет два независимых постусловия, одно из которых может нарушиться, а другое должно выполняться всегда. Выброс исключения сигнализирует о нарушении ровно одного постусловий из двух. ИМХО, нарушение single responsibility principle налицо.


EP>Сфейлится может только Deferred. Release не фейлится


Я это прекрасно понимаю. Получается, что комбо-деструктор имеет два постусловия: постусловие отложенного действия и "ресурсы освобождены". Первое может зафейлиться, второе соблюдается всегда. Функциям с single responsibility такое не свойственно.

KK>>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?


EP>Зависит от use-case — может пользователю будет достаточно того, что программа не вернёт EXIT_SUCCESS на не пойманном исключении.


Итог непойманного исключения на таких уровнях — вызов std::terminate. А поймать их никак нельзя, потому что к моменту вызова деструктора управление уже успеет покинуть любой try блок в потоке, где происходит этот вызов.

EP>В темах про кидающие деструкторы, откладывание flush таким образом, чтобы была возможность поймать его исключение — является одной из самый главных претензий к языку. Например 1
Автор: Erop
Дата: 13.10.12


Его претензии не обоснованы. С тем же успехом можно сетовать на то, что пылесос не умеет варить кофе. Если уж на то пошло, автор вообще часто берётся с умным видом судить о том, в чём ни фига не разбирается. Но это его личные проблемы и нас они не касаются.

EP>2


Это что-то очень длинное, у меня нет времени всё читать.

KK>>По мне так лучше дописать какую-нибудь управляющую конструкцию — например, тот же SCOPE_SUCCESS, — если понятность кода от этого существенно увеличится.


EP>Понятность увеличивается — не спорю. И в production (по-крайней мере на данный момент), я бы как раз использовал SCOPE_SUCCESS.


Я рад, что тут наши взгляды совпадают

EP>Но, например, если в проекте есть класс File_deferred, и все разработчики знают как он работает, и что от него ожидать — не вижу ничего плохого в его использовании.


Но ты ведь своё библиотечное решение не для одного единственного класса предлагаешь использовать? А если подобных классов много, то обо всех трудно помнить, особенно новеньким людям.
Re[16]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny Panasyuk Россия  
Дата: 14.10.12 19:26
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>Теперь давай разберёмся, сколько задач выполняет деструктор твоего File. Он выполняет или не выполняет какое-то отложенное действие и всегда выполняет освобождение ресурса. То есть этот деструктор ещё имеет два независимых постусловия, одно из которых может нарушиться, а другое должно выполняться всегда. Выброс исключения сигнализирует о нарушении ровно одного постусловий из двух. ИМХО, нарушение single responsibility principle налицо.

EP>>Сфейлится может только Deferred. Release не фейлится
KK>Я это прекрасно понимаю. Получается, что комбо-деструктор имеет два постусловия: постусловие отложенного действия и "ресурсы освобождены". Первое может зафейлиться, второе соблюдается всегда. Функциям с single responsibility такое не свойственно.

Ну вот допустим вызовы deferred расставлялись бы компилятором, как вызов отдельной функции. Какая разница для пользователя, кто делает эти вызовы — компилятор, или сгенерированный макросами деструктор?

KK>>>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?

EP>>Зависит от use-case — может пользователю будет достаточно того, что программа не вернёт EXIT_SUCCESS на не пойманном исключении.
KK>Итог непойманного исключения на таких уровнях — вызов std::terminate. А поймать их никак нельзя, потому что к моменту вызова деструктора управление уже успеет покинуть любой try блок в потоке, где происходит этот вызов.

Ну так я же и говорю, для кого-то может достаточно return code != EXIT_SUCCESS :
http://ideone.com/8H0WA
#include <cstdlib>
struct Terminator
{
    ~Terminator()
    {
        throw 0;
    }
} global;
int main()
{
    return EXIT_SUCCESS;
}


EP>>В темах про кидающие деструкторы, откладывание flush таким образом, чтобы была возможность поймать его исключение — является одной из самый главных претензий к языку. Например 1
Автор: Erop
Дата: 13.10.12

KK>Его претензии не обоснованы. С тем же успехом можно сетовать на то, что пылесос не умеет варить кофе. Если уж на то пошло, автор вообще часто берётся с умным видом судить о том, в чём ни фига не разбирается. Но это его личные проблемы и нас они не касаются.
EP>>2
KK>Это что-то очень длинное, у меня нет времени всё читать.

Ну вкратце, вот что пишет Дэйв Абрахамс:

In that case, let’s suppose my aim is to ensure that the file is closed and data gets to disk successfully. I’ll write a function whose postcondition is that data gets to disk. It writes a buffer to disk (which can fail because the disk is full), calls fclose and fsync, and I’ll call that from my destructor.
:::
Here’s another way of looking at it: destructors are the only way we have in C++ of attaching side-effects to block exit (c.f. finally in other languages), and, yes, to the destruction of other objects, neither one of which is inherently a bad thing to do. Side-effects can fail to complete, and when they do, there are reasons to want to know about it.


EP>>Но, например, если в проекте есть класс File_deferred, и все разработчики знают как он работает, и что от него ожидать — не вижу ничего плохого в его использовании.

KK>Но ты ведь своё библиотечное решение не для одного единственного класса предлагаешь использовать? А если подобных классов много, то обо всех трудно помнить, особенно новеньким людям.

Не спорю, какая-никакая гигиена необходима. Может суффикс у классов, может ещё что-нибудь.
Более того, необходимо помнить об транзитивности кидающих деструкторов.
Re[2]: Erop скучает
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 15.10.12 08:50
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Чтобы хорошо горело надо масла подливать. Небольшой кусочек кода и Хо Ли Вар пройдет тропой Хо Ши Мина. Вот, тебе Erop серебренная пуля. Позволяет безопасно доступиться к данным из множества потоков:

BFE>Eсть класс
Упадёт в segfault.
Sic luceat lux!
Re[3]: Erop скучает
От: B0FEE664  
Дата: 15.10.12 09:39
Оценка:
Здравствуйте, Kernan, Вы писали:

BFE>>Чтобы хорошо горело надо масла подливать. Небольшой кусочек кода и Хо Ли Вар пройдет тропой Хо Ши Мина. Вот, тебе Erop серебренная пуля. Позволяет безопасно доступиться к данным из множества потоков:

BFE>>Eсть класс
K>Упадёт в segfault.
Почему? И при каких условиях?
И каждый день — без права на ошибку...
Re[4]: Erop скучает
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 15.10.12 10:09
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему? И при каких условиях?


Да вот тут.
const std::string str = m_data.front();
Sic luceat lux!
Re[17]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 15.10.12 11:00
Оценка: -1
Здравствуйте, Evgeny Panasyuk, Вы писали:

EP>Ну вот допустим вызовы deferred расставлялись бы компилятором


То есть что-то вроде этого?

class File
{
public:
    /*...*/
    ~File(std::finalize_t) { flush(); }
    ~File() { releaseResources(); }
};

void foo(const wchar_t* filename)
{
    {
        File file(filename);
        /*...*/
    } // calls file.File::~File(std::finalize_t()), then file.File::~File()
}

Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо? Мой поинт в том, что клиент сам должен принимать решение насчёт вызова deferred, а автор класса не должен навязывать какую-то "единственно правильную" стратегию. Например, я могу захотеть выйти из блока через return, не вызывая deferred.

EP>Какая разница для пользователя, кто делает эти вызовы — компилятор, или сгенерированный макросами деструктор?


Разница будет хотя бы в раздельной спецификации исключений для ~File(std::finalize_t) и ~File(). Если временем жизни объекта управляет библиотечный код, он смог бы вызывать эти две функции по отдельности.

KK>>>>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?

EP>>>Зависит от use-case — может пользователю будет достаточно того, что программа не вернёт EXIT_SUCCESS на не пойманном исключении.
KK>>Итог непойманного исключения на таких уровнях — вызов std::terminate. А поймать их никак нельзя, потому что к моменту вызова деструктора управление уже успеет покинуть любой try блок в потоке, где происходит этот вызов.

EP>Ну так я же и говорю, для кого-то может достаточно return code != EXIT_SUCCESS :


Я смотрю на это как на потенциальную замаскированную ошибку.

EP>Более того, необходимо помнить об транзитивности кидающих деструкторов.


Если бы была поддержка на уровне компилятора, думаю, эту проблему можно было бы легко разрулить. Типа финализатор неявно вызывал бы финализаторы подобъектов, а деструктор — деструкторы подобъектов.
Re[7]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 15.10.12 11:27
Оценка:
Здравствуйте, jazzer, Вы писали:

J>все, кто прочитал книжку Саттера, в курсе, не волнуйся.


1) Судя по коду, который я встречаю во время работы, популярность этой книжки несколько преувеличена

2) Мне вообще не ясна логика этого аргумента. Насколько я понимаю коллег, рассуждение идёт так, что, то, что Саттер в своей книжке рассматривал эту проблему, и методы борьбы с ней, доказывает, что этой проблемы нет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Erop скучает
От: B0FEE664  
Дата: 15.10.12 12:42
Оценка:
Здравствуйте, Kernan, Вы писали:

BFE>>Почему? И при каких условиях?


K>Да вот тут.

K>
K>const std::string str = m_data.front();
K>


Из-за отсутствия проверки на пустоту?

const std::string str = !m_data.empty() ? m_data.front() : "";

Так лучше?
И каждый день — без права на ошибку...
Re[6]: Erop скучает
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 15.10.12 13:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Так лучше?

Сгодится.
Sic luceat lux!
Re[8]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: jazzer Россия Skype: enerjazzer
Дата: 15.10.12 23:21
Оценка: :)
Здравствуйте, Erop, Вы писали:

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


J>>все, кто прочитал книжку Саттера, в курсе, не волнуйся.


E>1) Судя по коду, который я встречаю во время работы, популярность этой книжки несколько преувеличена


Ужесточите отбор при приеме на работу, что я могу сказать
Мы вот с кандидатом на позицию С++ просто дальше не разговариваем, если он букварей (Майерс/Саттер) не читал — вычищать за ним потом себе дороже.

E>2) Мне вообще не ясна логика этого аргумента. Насколько я понимаю коллег, рассуждение идёт так, что, то, что Саттер в своей книжке рассматривал эту проблему, и методы борьбы с ней, доказывает, что этой проблемы нет...


Для начала, это не проблема RAII вообще. С ручным управлением будет все то же самое:
void use_and_delete(int* p1, int* p2)
{
  // use p1 and p2
  delete p1;
  delete p2;
}

int main()
{
  use_and_delete( new int(1), new int(2) );
}

Так что у проблемы нескольких голых new в аргументах функции вообще решения нет, хоть с RAII, хоть без.
Но если RAII хоть какое-то решение предлагает через использование вспомогательных функций (а теперь еще и списков инициализации), то у ручного управления тут вообще полный швах.
И ты все еще считаешь, что это ты тут показал проблему RAII?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: 1) Родовая травма: исключеиния в деструкторах
От: CreatorCray  
Дата: 15.10.12 23:59
Оценка:
Здравствуйте, Erop, Вы писали:

E>Суть-то проблемы не в том, что ТС идиот и придумал смешной пример, а в том, что многие ресурсы требуют нетривильных действий в конце сессии использования ресурса. И такие ресурсы в С++ версию RAII ложатся плохо. И хорошего ОБОБЩЁННОГО решения, кроме, как отказаться от RAII или перенести его на более высокий уровень -- нет...


Просто ты сейчас троллинга ради пытаешься натянуть RAII на глобус. А это всего лишь инструмент, которым надо уметь пользоваться и применять его по месту. А не везде как ты сейчас сам себе придумал и теперь пытаешься опровергнуть.
Тема ни о чём, поехали в КСВ.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: 1) Родовая травма: исключеиния в деструкторах
От: landerhigh Пират  
Дата: 16.10.12 00:11
Оценка: -1
Здравствуйте, Erop, Вы писали:

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


E>>а что в этом плохого? Оставшийся поток = ошибка программиста. ПРограмма нам о ней и сообщает...


E>... раз две недели роняя ответственный сервер клиента в непонятный момент по терминэйт, без логов и сохранения данных. Прекрасное решение, демонстрирующее всю мощь RAII в кривых руках...


Во-первых, RAII тут ни при чем то есть совсем и полностью.
Во-вторых, ронять сервер по терминейт с крешдампом, если криворучко накосячил с потоками — это очень и очень хорошо. Примерно в сто тридцать пять миллионов четыреста восемьдесят три тысячи семьсот шестьдесят четыре раза лучше, нежели оставить этот поток крутиться и стрелять по памяти "ответственного сервера".
В-третьих, кривые руки могут сами знаешь что сломать.
www.blinnov.com
Re[9]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 16.10.12 04:53
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Во-первых, RAII тут ни при чем то есть совсем и полностью.

В смысле не стоит хранить в RAII-обёртке нитку? Да, не стоит, тут я согласен...

L>Во-вторых, ронять сервер по терминейт с крешдампом, если криворучко накосячил с потоками — это очень и очень хорошо. Примерно в сто тридцать пять миллионов четыреста восемьдесят три тысячи семьсот шестьдесят четыре раза лучше,

Интересно, тебя не учили, что магические числа в тексте есть зло и один из источников нечитабельности текстов, или ты сознательно игнорируешь науку?..


L>нежели оставить этот поток крутиться и стрелять по памяти "ответственного сервера".

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

L>В-третьих, кривые руки могут сами знаешь что сломать.

Дык тут мы этот случай, IMHO, и имеем...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: 1) Родовая травма: исключеиния в деструкторах
От: landerhigh Пират  
Дата: 16.10.12 06:20
Оценка:
Здравствуйте, Erop, Вы писали:

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


L>>Во-первых, RAII тут ни при чем то есть совсем и полностью.

E>В смысле не стоит хранить в RAII-обёртке нитку? Да, не стоит, тут я согласен...

Почему не стоит? Этот случай показывает, что очень даже стоит.

L>>Во-вторых, ронять сервер по терминейт с крешдампом, если криворучко накосячил с потоками — это очень и очень хорошо. Примерно в сто тридцать пять миллионов четыреста восемьдесят три тысячи семьсот шестьдесят четыре раза лучше,

E>Интересно, тебя не учили, что магические числа в тексте есть зло и один из источников нечитабельности текстов, или ты сознательно игнорируешь науку?..

Сознательно. У Кларксона научился.

L>>нежели оставить этот поток крутиться и стрелять по памяти "ответственного сервера".

E>Почему сразу "стрелять по памяти", а не "аккуратно дампиться и завершаться", например?

Просто потому, что данное условие в данной обертке сработает в одном — единственном случае, когда про поток забыли. Поскольку про него забыли, то безопасно предположить, что он уже вовсю стреляет по памяти. Единственно верное решение в данном случае — совершить харакири. Обработчик terminate дамп запишет и все, баиньки.

E>Кроме того, почему termitate рассматривается, как единственная альтернатива подходу с полным гнорированием ошибок? Можно же исключение, например, кинуть...


Исключение из деструктора?

L>>В-третьих, кривые руки могут сами знаешь что сломать.

E>Дык тут мы этот случай, IMHO, и имеем...

Завершение потоков из дестурктора — очень, очень плохая идея. Порой мне кажется, что каждый должен наступить на эти грабли сам, чтобы, так сказать, прочувствовать. Эта обертка даст тебе по рукам гораздо раньше, нежели тебе прилетит в лоб от забытого потока (или коллеги, который ловил баг неделю).
www.blinnov.com
Re[9]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 16.10.12 06:25
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Мы вот с кандидатом на позицию С++ просто дальше не разговариваем, если он букварей (Майерс/Саттер) не читал — вычищать за ним потом себе дороже.


У всех свои причуды. Можно искать кодеров подороже, а можно просто коде стайл построже сделать и всё...
У нас, кстати, не требуется гарантий безопасности исключений, не строгая, не нестрогая. Требуется нечно иное -- безопасное разрушение и запись отладочной инфы в лог. Потеря памяти возможна, но нежелательна. При этом усложнять код ради борьбы с потерей памяти при исключениях ЗАПРЕЩЕНО...
А для того, что бы не терять при исколючениях действительно критичные ресурсы, используются техники, отличные от всеми так любимой RAII

J>Для начала, это не проблема RAII вообще.

Новый поворт?
Для начала, я сформулировал примерно чётко. В случае, когда ресурса два, а конструктор один, RAII уже не лечит беду, ради лечения которой было изобретено...

J>С ручным управлением будет все то же самое:

J>
J>void use_and_delete(int* p1, int* p2)
J>{
J>  // use p1 and p2
J>  delete p1;
J>  delete p2;
J>}
Это непрравда.

Ручное управлениеучитывающее возможность исключений, будет выглядеть как-то так, например:
void use_and_delete(int* p1, int* p2)
{
    try {
        // use p1 and p2
   } catch( ... ) {
        delete p2;
        delete p1;
        throw;
   }
}
При этом мы полагаем, что в delete pX исключения быть не может (раз уж мы полагаем, что тут можно заюзать RAII в принципе )
Только это всё к RAII не имеет никакого отношеня. В RAII мы указатель передаём в конструктор какой-то RAII-обёртки, а не в левую первую попавшуюся функцию

J>И ты все еще считаешь, что это ты тут показал проблему RAII?

Да, считаю, суть проблемы в том, что если ты отдаёшь в конструктор более одного ресурса, то не имеешь никаких гарантий, хотя можешь думать, что имеешь...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 16.10.12 07:17
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Почему не стоит? Этот случай показывает, что очень даже стоит.

Тогда почему это RAII тут не при чём?

L>Просто потому, что данное условие в данной обертке сработает в одном — единственном случае, когда про поток забыли. Поскольку про него забыли, то безопасно предположить, что он уже вовсю стреляет по памяти.

Этот вывод можно как-то обоснвоать? И почему считается, что до деструктора thread он стрелял по памяти безопасно,а теперь стал опасно?

L>Единственно верное решение в данном случае — совершить харакири. Обработчик terminate дамп запишет и все, баиньки.

Видимо это догмат какой-то веры...
Кстати, а как при таком подходе гарантировать освобождение критических ресурсов? terminate их же не освободит?

L>Исключение из деструктора?

Ну исключения из деструктора плохи только тем, что могут вызвать termainate...

L>Завершение потоков из дестурктора — очень, очень плохая идея.

Я с этим и не спорю. Именно поэтому мне представляется странной идея рулить ими через RAII...

L>Порой мне кажется, что каждый должен наступить на эти грабли сам, чтобы, так сказать, прочувствовать. Эта обертка даст тебе по рукам гораздо раньше, нежели тебе прилетит в лоб от забытого потока (или коллеги, который ловил баг неделю).


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

То, что мы выбросили хэндл потока, говоит всего лишь о том, что мы не хотим ждать его завершения.
Почему мы явно должны сказать, что не хотим?
Кроме того, даже если мы хотим с потоком как-то взаимодействовать, то даже это не обозначает, что мы всё равно хотим ждать его завершения. По умолчанию же потоки всё-таки должны звершаться САМИ...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: jazzer Россия Skype: enerjazzer
Дата: 16.10.12 07:47
Оценка: 1 (1) +2
Здравствуйте, Erop, Вы писали:

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


J>>Мы вот с кандидатом на позицию С++ просто дальше не разговариваем, если он букварей (Майерс/Саттер) не читал — вычищать за ним потом себе дороже.


E>У всех свои причуды. Можно искать кодеров подороже, а можно просто коде стайл построже сделать и всё...

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

E>У нас, кстати, не требуется гарантий безопасности исключений, не строгая, не нестрогая. Требуется нечно иное -- безопасное разрушение и запись отладочной инфы в лог. Потеря памяти возможна, но нежелательна. При этом усложнять код ради борьбы с потерей памяти при исключениях ЗАПРЕЩЕНО...

E>А для того, что бы не терять при исколючениях действительно критичные ресурсы, используются техники, отличные от всеми так любимой RAII
"У всех свои причуды."
У нас достаточно просто указать в комменте, какая гарантия предоставляется.

J>>Для начала, это не проблема RAII вообще.

E>Новый поворт?
E>Для начала, я сформулировал примерно чётко. В случае, когда ресурса два, а конструктор один, RAII уже не лечит беду, ради лечения которой было изобретено...
там аж три конструктора, вообще-то, а не один.
И проблема там не RAII, а неопределенного порядка вычислений аргументов.
Не помню, чтобы кто-то предлагал RAII для лечения этой проблемы.

J>>С ручным управлением будет все то же самое:

E>Ручное управлениеучитывающее возможность исключений, будет выглядеть как-то так, например:
void use_and_delete(int* p1, int* p2)
E>{
E>    try {
E>        // use p1 and p2
E>   } catch( ... ) {
E>        delete p2;
E>        delete p1;
E>        throw;
E>   }
E>}
При этом мы полагаем, что в delete pX исключения быть не может (раз уж мы полагаем, что тут можно заюзать RAII в принципе )

E>Только это всё к RAII не имеет никакого отношеня. В RAII мы указатель передаём в конструктор какой-то RAII-обёртки, а не в левую первую попавшуюся функцию
Ты опять отвечаешь на несущественное, стирая главное.
Нет проблем, я напомню:
int main()
{
  use_and_delete( new int(1), new int(2) );
}

Как твоя замечательная новая use_and_delete с try-catch внутри защитит нас от утечки при вычислении аргументов?

J>>И ты все еще считаешь, что это ты тут показал проблему RAII?

E>Да, считаю, суть проблемы в том, что если ты отдаёшь в конструктор более одного ресурса, то не имеешь никаких гарантий, хотя можешь думать, что имеешь...
А если не в конструктор, а просто в функцию, то проблема вдруг исчезнет?
Еще раз — проблема здесь исключительно в неопределенном порядке вычисления аргументов. И только в нем. RAII тут сбоку припёка.

ЗЫ Ты из спортивного интереса все мои ответы тебе смайликами помечаешь, или это у тебя просто настроение очень веселое?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[11]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Centaur Россия  
Дата: 16.10.12 09:09
Оценка:
Здравствуйте, jazzer, Вы писали:

J>ЗЫ Ты из спортивного интереса все мои ответы тебе смайликами помечаешь, или это у тебя просто настроение очень веселое?


Affects me too, так что, видимо, настроение.
Re[11]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 16.10.12 09:09
Оценка:
Здравствуйте, jazzer, Вы писали:

J>За исполнением код-стайла тоже надо кому-то следить

На С++ много за чем надо следить. Для того и есть ревью

J>Так что, имхо, лучше сразу набирать программеров, за которыми следить не надо, чем потом ходить за ними с пеленкой код-стайла.

То есть у вас ревью нет?..

J>У нас достаточно просто указать в комменте, какая гарантия предоставляется.

Суть не в том, что что-то можно просто указать в комментарии, а в том, что обычно нет никакого смысла в предоставлении гарантии, даже нестрогой. Код усложняется, при этом усложняется за счёт обработки ошибочной ситуации. Так как в наших продуктах ошибки редкость, то покрыть такой код тестами трудно, так что имеем редкоисполняемый плохооттестированный код, часто ещё и нетривиальный. Зачем бы оно надо?

J>там аж три конструктора, вообще-то, а не один.

Ну хоть пять, если ещё и конструкторы копий посчитать, только не помогает...
J>И проблема там не RAII, а неопределенного порядка вычислений аргументов.
J>Не помню, чтобы кто-то предлагал RAII для лечения этой проблемы.

Проблема другая -- автоматическое управление владением ресурсами. В том случае RAII на C++ не справляется со своим предназначением, вернее плохо справляется.

J>Как твоя замечательная новая use_and_delete с try-catch внутри защитит нас от утечки при вычислении аргументов?

Никак, она и не предназначена для этого. Ты занял какую-то странную позицию, что если с утечкой ресурсов не борются при помощи RAII, то не борятся вообще ниак. А это неправда.

J>А если не в конструктор, а просто в функцию, то проблема вдруг исчезнет?

А если просто в функцию, то ты, наверное, как-то иначе следишь за проблемами, возникающими из-за исключений, и имеешь какие-то иные грабли, специфичные для того, другого способа...

J>Еще раз — проблема здесь исключительно в неопределенном порядке вычисления аргументов. И только в нем. RAII тут сбоку припёка.

RAII тут при том, что неопределённость вычисления параметров в С++ ставит в этом случае RAII раком. Тема как раз про это, какие трудности и шероховатости есть в реализации RAII в С++
Трудность и шероховатость настолько крутая, что про неё в азбуке пишут, а ты не веришь, что она есть
мне логика этого силлогизма не ясна.

J>или это у тебя просто настроение очень веселое?

Забавно читать тексты умного парня, который, тем не менее, не может преодолеть в своём уме стереотип, что с утечной ресурсов можно бороться только RAII и больше никак
Например, может быть фабрика, которая ресурсы захватывает, а в случае их потери освобождает. При этом при открытии можно указывать оганичение на время жизни ресурса, например ID стекового фрейма, который не ресурс не должен пережить
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[12]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: jazzer Россия Skype: enerjazzer
Дата: 16.10.12 09:51
Оценка:
Здравствуйте, Erop, Вы писали:

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


J>>За исполнением код-стайла тоже надо кому-то следить

E>На С++ много за чем надо следить. Для того и есть ревью
J>>Так что, имхо, лучше сразу набирать программеров, за которыми следить не надо, чем потом ходить за ними с пеленкой код-стайла.
E>То есть у вас ревью нет?..
Формального — нет. Когда были плохие программеры — было.

J>>У нас достаточно просто указать в комменте, какая гарантия предоставляется.

E>Суть не в том, что что-то можно просто указать в комментарии, а в том, что обычно нет никакого смысла в предоставлении гарантии, даже нестрогой.
Зависит от приложения, не находишь?
E>Код усложняется, при этом усложняется за счёт обработки ошибочной ситуации. Так как в наших продуктах ошибки редкость, то покрыть такой код тестами трудно, так что имеем редкоисполняемый плохооттестированный код, часто ещё и нетривиальный. Зачем бы оно надо?
У нас работают достаточно квалифицированные программисты, чтобы оценить, какого уровня гарантии можно предоставить, и написать соответствующий комментарий.

J>>там аж три конструктора, вообще-то, а не один.

E>Ну хоть пять, если ещё и конструкторы копий посчитать, только не помогает...
J>>И проблема там не RAII, а неопределенного порядка вычислений аргументов.
J>>Не помню, чтобы кто-то предлагал RAII для лечения этой проблемы.

E>Проблема другая -- автоматическое управление владением ресурсами. В том случае RAII на C++ не справляется со своим предназначением, вернее плохо справляется.

Тут RAII неправильно используется, до A дело не доходит. Ресурс утекает ДО того, как попадает в RAII. Это происходит из-за неопределенного порядка вычислений аргументов.
Но у тебя это почему-то считается недостатком RAII.

Собственно, никто не говорит, что RAII невозможно использовать неправильно, вроде. Вот тебе навскидку еще один пример неправильного использования — тоже будешь говорить, что тут RAII виновато?
std::auto_ptr<int> p1( new int );
std::auto_ptr<int> p2( p1.get() );

Но при этом RAII гораздо сложнее использовать неправильно, чем ручное управление (хотя и легче, чем сборку мусора).
При этом код таки становится на порядок чище, почти как со сборкой мусора.

J>>Как твоя замечательная новая use_and_delete с try-catch внутри защитит нас от утечки при вычислении аргументов?

E>Никак, она и не предназначена для этого. Ты занял какую-то странную позицию, что если с утечкой ресурсов не борются при помощи RAII, то не борятся вообще ниак. А это неправда.
Соответствующая этому утверждению цитата очень бы помогла.

J>>А если не в конструктор, а просто в функцию, то проблема вдруг исчезнет?

E>А если просто в функцию, то ты, наверное, как-то иначе следишь за проблемами, возникающими из-за исключений, и имеешь какие-то иные грабли, специфичные для того, другого способа...
Замечательно. А теперь объясни мне, неразумному, почему если указатели передаются в конструктор — то это RAII, а если в функцию — то нет?

J>>Еще раз — проблема здесь исключительно в неопределенном порядке вычисления аргументов. И только в нем. RAII тут сбоку припёка.

E>RAII тут при том, что неопределённость вычисления параметров в С++ ставит в этом случае RAII раком. Тема как раз про это, какие трудности и шероховатости есть в реализации RAII в С++
E>Трудность и шероховатость настолько крутая, что про неё в азбуке пишут, а ты не веришь, что она есть
E>мне логика этого силлогизма не ясна.
Я могу только повторить: проблема здесь исключительно в неопределенном порядке вычисления аргументов. RAII имеет к ней отношение примерно такое же, как дядка в Киеве к бузине в огороде.

J>>или это у тебя просто настроение очень веселое?

E>Забавно читать тексты умного парня, который, тем не менее, не может преодолеть в своём уме стереотип, что с утечной ресурсов можно бороться только RAII и больше никак
Соответствующая этому утверждению цитата очень бы помогла.

E>Например, может быть фабрика, которая ресурсы захватывает, а в случае их потери освобождает. При этом при открытии можно указывать оганичение на время жизни ресурса, например ID стекового фрейма, который не ресурс не должен пережить

ты хочешь сказать, что вы там у себя на работе врезались непосредственно в компилятор и фигачите асмом по фреймам? Или что?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[13]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 16.10.12 10:27
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Формального — нет. Когда были плохие программеры — было.

Что значит "формального"?

J>Зависит от приложения, не находишь?

Ну если приложение бажное, и оттестировать что будет если так у падёт, и если сяк упадёт не проблема, то да, зависит.
Вообще я не против RAII, я даже за всеми ластами. Только надо отдавать себе отчёт в том, что поддержка RAII не без проблем в С++, и что RAII -- не единственная стратегия борьбы с утечками ресурсов, не самая надёжная, не самая быстрая, кстати, зачастую, но, зато, довольно простая концептуально и хорошо ложащаяся на концепцию владения объектами друг дуругом.
Что же касается гарантий, то это вопрос более тонкий. Ещё в библиотеках этот может быть актуально, хотя вопрос тонкий. Например, если в приложении/библиотеке исключения обозначают логические ошибки, то не понятно чем так уж нестрогая гарантия безопасности нужна и хороша. Зачем она нужна, для какой практической цели?

J>У нас работают достаточно квалифицированные программисты, чтобы оценить, какого уровня гарантии можно предоставить, и написать соответствующий комментарий.

Ну у нас тоже. Потому, что правилный ответ почти всюду "никакой уровень гарантий из трёх общщепринятых не нужен". Это ближе к С, чем принято в С++ сейчас, но это вполне рабочий подход, весьма хорошо работающий. И он не единственный.

J>Но у тебя это почему-то считается недостатком RAII.

Это проблема поддержки RAII в С++. Например, если бы можно было как-то указать, что тут вот не надо оптимизитровать порядок излишне агрессивно, то было бы лучше, ещё лучше было бы, если бы это можно было указать в определении классов RAII-обёрток...

J>При этом код таки становится на порядок чище, почти как со сборкой мусора.

Это не единственный способ автоматизации управления ресурсами. Ты почему-то рассматриваешь альтернативу "или RAII или совсем врукопашную", а это ложная альтернатива
Всё познаётся в сравнении, в том числе и плюсы и минусы RAII. Только сравнивать надо не с отсутсвием автоматизации управления ресурсами, а с альтернативными стратегиями...
Кстати, что касается сборки мусора. Для управления памятью она хорошо подходит во многих случаях, хотя тоже не во всех. Типичный источник проблем с GC -- кэши.
Но управление памятью сейчас не стоит особо остро. В конце концов мы же миримся с тем, что GC какую-то часть памяти всё время держит неосвобождённой? Ну так и схемы с "примерным управлением" памятью обладают схожими свойствами. И даже схема, когда мы всё чистим, но в случае логической ошибки можем немного потечь, тоже, на круг, обладают примерно теми же свойствами. Дальше можно ввести количественные показатели, вроде степени фрагментации (ну там, сколько памяти мы преально используем против того, сколько сожрали у системы, например после часа, суток, недели и года работы). И просто сравнивать циферьки и трудозатраты. И не факт, что тут GC всех порвёт на всех, или, хотя бы, на большинстве задач
Потом в области памяти RAII и прочие основанные на владении объетков объектами схемы совершенно криво работают в случае сложных структур данных на указателях. Там циклы всякие любят возникать, например, ну и вообще вся эта рекурсиваная разрушительная деятельность неоправданно дорога, потому, что не делает, на самом деле, ничего полезного. То есть там начинают рулить другие подходы. Типа у тебя есть аллокатор на структуру данных, и на нем аллокируются узлы, а потом грохается вся структура, вместе с аллокатором, а узлы просто теряются. Экономит много времени и рабочего множества, кстати, но, противоречит техникам, основанным на владении объектов объектами. Но, ради такой штуки можно юзать альтернативные всякие техники автоматизвции управления критическими ресурсами...

При этом для управления именно вот критическими ресурсами, которые не память или не сводятся к памяти, а тратят ещё какой-то лимит в системе, логический или физический, через GC вообще неудобно, криво и нехорошо. А через RAII и прочие техники с владением хорошо, но не всегда.

J>Замечательно. А теперь объясни мне, неразумному, почему если указатели передаются в конструктор — то это RAII, а если в функцию — то нет?


Потому, что в С++ RAII реализуется, как захват ресурсов В КОНСТРУКТОРЕ и освобождение в деструкторе... Ваш К. О...

J>Я могу только повторить: проблема здесь исключительно в неопределенном порядке вычисления аргументов. RAII имеет к ней отношение примерно такое же, как дядка в Киеве к бузине в огороде.


Ну так RAII в версии
shared_pair<T1, T2> p( neq T1, new T2 );
не работает жеж? А отношения не имеет. Странно...

E>>Забавно читать тексты умного парня, который, тем не менее, не может преодолеть в своём уме стереотип, что с утечкой ресурсов можно бороться только RAII и больше никак

J>Соответствующая этому утверждению цитата очень бы помогла.
Чему помогла бы? Понять что меня забавляет? Я тебе прямым текстом написал с чего мне смешно...

J>ты хочешь сказать, что вы там у себя на работе врезались непосредственно в компилятор и фигачите асмом по фреймам? Или что?

Зачем так грубо?
Я ещё раз говорю тебе, представь себе, что RAII от чего-то нельзя. Предложи три других способа автоматизировать управление ресурсами в С и в С++...
Потом можешь начать сравнивать и решать так ли уж хороша RAII. Конечно, если ты считаешь эту парадигмой безальтернативным способом, то говорить не о чём, ты просто не видишь других вариантов. Во всяком случае, все сравнения плюсов и минусов RAII проводишь в сравнеии с кодом, который вообще ресурсами автоматически никак не управляет, а не с альтренативными схемами автоматизации управления ресурсами.
Либо я пропустил какой-то из твоих доводов...
Тогда можешь просто повторить его, что бы обратить моё внимание.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Ну и вообще, к вопросу о знании азбук при найме ;)
От: Erop Россия  
Дата: 16.10.12 10:55
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ужесточите отбор при приеме на работу, что я могу сказать

J>Мы вот с кандидатом на позицию С++ просто дальше не разговариваем, если он букварей (Майерс/Саттер) не читал — вычищать за ним потом себе дороже.

Ну, например, тут есть вполне так себе вменяемый и знающий коллега rg45. Он, кажется, отчасти разделяет твою позицию
Автор: jazzer
Дата: 16.10.12
...
Так вот чуть выше по ветке, он выражал сомнения в существовании обсуждаемого эффекта
Автор: Erop
Дата: 13.10.12
, например, хотя я не думаю, что он такой уж лохой программист...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Ну и вообще, к вопросу о знании азбук при найме ;)
От: rg45 СССР  
Дата: 16.10.12 11:17
Оценка: :)
Здравствуйте, Erop, Вы писали:

J>>Ужесточите отбор при приеме на работу, что я могу сказать

J>>Мы вот с кандидатом на позицию С++ просто дальше не разговариваем, если он букварей (Майерс/Саттер) не читал — вычищать за ним потом себе дороже.

E>Ну, например, тут есть вполне так себе вменяемый и знающий коллега rg45. Он, кажется, отчасти разделяет твою позицию
Автор: jazzer
Дата: 16.10.12
...

E>Так вот чуть выше по ветке, он выражал сомнения в существовании обсуждаемого эффекта
Автор: Erop
Дата: 13.10.12
, например, хотя я не думаю, что он такой уж лохой программист...


Ну вот, лоханулся разок, теперь всю дорогу вспоминать будут У меня, может, затмение было — мимолетное
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[11]: Ну и вообще, к вопросу о знании азбук при найме ;)
От: Erop Россия  
Дата: 16.10.12 11:41
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну вот, лоханулся разок, теперь всю дорогу вспоминать будут У меня, может, затмение было — мимолетное


Я против тебя ничего не имею. Я, наоборот, считаю, что проверять при найме занание кандидатом таких тонкостей глупо. Если уж вы так кодируете, что занние таких тонкостей необходимо, то надо нанятого им обучить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[12]: 1) Родовая травма: исключеиния в деструкторах
От: landerhigh Пират  
Дата: 16.10.12 20:40
Оценка:
Здравствуйте, Erop, Вы писали:

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


L>>Почему не стоит? Этот случай показывает, что очень даже стоит.

E>Тогда почему это RAII тут не при чём?

Потому что эта обертка — нифига не RAII. Period.
Хотя use case очень хороший. Помогает провести границу применимости RAII.

L>>Просто потому, что данное условие в данной обертке сработает в одном — единственном случае, когда про поток забыли. Поскольку про него забыли, то безопасно предположить, что он уже вовсю стреляет по памяти.

E>Этот вывод можно как-то обоснвоать? И почему считается, что до деструктора thread он стрелял по памяти безопасно,а теперь стал опасно?

Медленно и по буквам — ловушка в деструкторе этой обертки срабатывает только в одном случае — если про поток забыли. К моменту разрушения обертки поток либо должен быть завершен, отпущен в свободное плавание. Если программер забыл позаботиться о потоке, то, скорее всего он много чего еще забыл. рассчитывать на безопасное продолжение работы в этом случае нельзя. Энд оф стори.

L>>Единственно верное решение в данном случае — совершить харакири. Обработчик terminate дамп запишет и все, баиньки.

E>Видимо это догмат какой-то веры...

Видимо, просто кто-то что-то не догоняет.

E>Кстати, а как при таком подходе гарантировать освобождение критических ресурсов? terminate их же не освободит?


Каких ресурсов? Их уже твой забытый поток покромсал в салат.

L>>Исключение из деструктора?

E>Ну исключения из деструктора плохи только тем, что могут вызвать termainate...

Кто здесь?

L>>Завершение потоков из дестурктора — очень, очень плохая идея.

E>Я с этим и не спорю. Именно поэтому мне представляется странной идея рулить ими через RAII...

А что, кто-то предлагает так делать?

L>>Порой мне кажется, что каждый должен наступить на эти грабли сам, чтобы, так сказать, прочувствовать. Эта обертка даст тебе по рукам гораздо раньше, нежели тебе прилетит в лоб от забытого потока (или коллеги, который ловил баг неделю).


E>А как можно неделю ловить "забытый поток"? Имеющиеся в любой момент времени потоки можно же перечислить?


Можно и месяц. Ты, очевидно, не совсем понимаешь, о чем я говорю. Ничего, будет и на твоей улице праздник, когда чертовщину, вызванную маааленьким таким расстрелом памяти из потока, которому "забыли" вовремя сказать, что пора бы уже и закругляться, будете всей конторой с отладчиком наперевес отлавливать.

E>Кроме того, я не понимаю, почему поток, на который у нас в программе не омталось хэнедла считается забытым? Нормальные потоки завершают себя САМИ, а не через хэндл их кто-то прибивает же...


Это ты сейчас с кем разговаривал?

E>То, что мы выбросили хэндл потока, говоит всего лишь о том, что мы не хотим ждать его завершения.

E>Почему мы явно должны сказать, что не хотим?

Потому что. Это ловушка для дурака. Если не хочешь ждать завершения — сделай ему detach или чего у него там есть в API. Если забыл, то, скорее всего, ты много чего еще "забыл".

E>Кроме того, даже если мы хотим с потоком как-то взаимодействовать, то даже это не обозначает, что мы всё равно хотим ждать его завершения. По умолчанию же потоки всё-таки должны звершаться САМИ...


Забыл подписаться, кэп.
www.blinnov.com
Re[13]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 16.10.12 20:58
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Потому что эта обертка — нифига не RAII. Period.

L>Хотя use case очень хороший. Помогает провести границу применимости RAII.

Ну тогда я дальше и не спорю. Потоки -- пример ресурсов, которые для RAII негодятся...

L>А что, кто-то предлагает так делать?

Ну в этом обсуждениии они же всплыли?


L>Медленно и по буквам — ловушка в деструкторе этой обертки срабатывает только в одном случае — если про поток забыли. К моменту разрушения обертки поток либо должен быть завершен, отпущен в свободное плавание. Если программер забыл позаботиться о потоке, то, скорее всего он много чего еще забыл. рассчитывать на безопасное продолжение работы в этом случае нельзя. Энд оф стори.

А если не забыла, а не успели?..

L>Видимо, просто кто-то что-то не догоняет.

Ну как объясняют, так и догоняют

L>Каких ресурсов?

Ну там реактор заглушить, или пятьсот гектар временных файлов потереть...
Ресурсы они разные бывают

L>Их уже твой забытый поток покромсал в салат.

Ну недо писать такие потоки, которые сразу, как где-то перестали за ними следить через хэндл, сразу бросаются что-то там кромсать...
В РФ это, кроме всего прочего, ещё и УК, кстати...


L>Можно и месяц. Ты, очевидно, не совсем понимаешь, о чем я говорю. Ничего, будет и на твоей улице праздник, когда чертовщину, вызванную маааленьким таким расстрелом памяти из потока, которому "забыли" вовремя сказать, что пора бы уже и закругляться, будете всей конторой с отладчиком наперевес отлавливать.


Думаю, что не будем...

L>Это ты сейчас с кем разговаривал?

Извини, я думал, что ты в теме...

L>Потому что. Это ловушка для дурака. Если не хочешь ждать завершения — сделай ему detach или чего у него там есть в API. Если забыл, то, скорее всего, ты много чего еще "забыл".

Странное обобщение. А если ты, например, показывал прогресс-бар, а потом тебе нажали "отмена".
Ты послал потоку сообщение, типа тухни, и тут случился logic_error. Почему надо падать в терминэйт?


L>Забыл подписаться, кэп.

Тогда я всё равно не понимаю, при чём тут расстрел памяти и забытость потока?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: 1) Родовая травма: исключеиния в деструкторах
От: landerhigh Пират  
Дата: 16.10.12 23:20
Оценка:
Здравствуйте, Erop, Вы писали:

L>>Потому что эта обертка — нифига не RAII. Period.

L>>Хотя use case очень хороший. Помогает провести границу применимости RAII.

E>Ну тогда я дальше и не спорю. Потоки -- пример ресурсов, которые для RAII негодятся...


Ну зачем же так огульно. Есть use cases, где очень даже годятся. И даже с потоками.

L>>А что, кто-то предлагает так делать?

E>Ну в этом обсуждениии они же всплыли?
E>

L>>Медленно и по буквам — ловушка в деструкторе этой обертки срабатывает только в одном случае — если про поток забыли. К моменту разрушения обертки поток либо должен быть завершен, отпущен в свободное плавание. Если программер забыл позаботиться о потоке, то, скорее всего он много чего еще забыл. рассчитывать на безопасное продолжение работы в этом случае нельзя. Энд оф стори.

E>А если не забыла, а не успели?..


"Не успели" есть частный случай "забыли". Задача сведена к предыдущей.

L>>Каких ресурсов?

E>Ну там реактор заглушить, или пятьсот гектар временных файлов потереть...

Интересно, что ты реактор упомянул. Из нас двоих кое-кто один действительно пишет софт для электростанций. Не атомных, к счастью, а то хрен мне был бы поперек рыла, а не RSDN на рабочем месте.

E>Ресурсы они разные бывают


Так вот, по поводу реактора. Пока ты там подтираешь 100500 гектар временных файлов, поток, которому ты не успел забыл сказать, что пора домой, тихонько продолжал вытаскивать стержни из актвной зоны. К моменту, когда ты блестяще избавишься от временных файлов, всем будет уже кагбе пофиг.

L>>Их уже твой забытый поток покромсал в салат.

E>Ну недо писать такие потоки, которые сразу, как где-то перестали за ними следить через хэндл, сразу бросаются что-то там кромсать...

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

E>В РФ это, кроме всего прочего, ещё и УК, кстати...


Тебе все хиханьки да хаханьки, а у нас перед релизом обязательный safety review. Потому что косяк в коде имеет вполне реальные шансы превратиться в невовремя замкнутый разрыватель на 600 КВ линии переменного тока с сопутствующими спецэфеектами и органическими удобрениями вдоль линии.

L>>Можно и месяц. Ты, очевидно, не совсем понимаешь, о чем я говорю. Ничего, будет и на твоей улице праздник, когда чертовщину, вызванную маааленьким таким расстрелом памяти из потока, которому "забыли" вовремя сказать, что пора бы уже и закругляться, будете всей конторой с отладчиком наперевес отлавливать.


E>Думаю, что не будем...


У вас все впереди. Не зарекайся.

L>>Это ты сейчас с кем разговаривал?

E>Извини, я думал, что ты в теме...

Давай договоримся — ты не строишь из себя дурочку не по делу, а я не пишу ничего о счете и косточковых фруктах?

L>>Потому что. Это ловушка для дурака. Если не хочешь ждать завершения — сделай ему detach или чего у него там есть в API. Если забыл, то, скорее всего, ты много чего еще "забыл".

E>Странное обобщение. А если ты, например, показывал прогресс-бар, а потом тебе нажали "отмена".
E>Ты послал потоку сообщение, типа тухни, и тут случился logic_error. Почему надо падать в терминэйт?

logic еггог? Если только у программиста.
Если твой лоджик еггог приведет к размотке стека и удалению владельца потока, то твоя прямая обязанность удостовериться в корректности состояния потока. В большинстве случаев это означает сигнал к завершению потока и ожидание его завершения. В некоторых случаях можно просто просигнализировать и не ждать. В редких случаях поток можно отпустить на вольные хлеба.
Если ты не сделал ничего из вышеперечисленного, то ты, скорее всего, не подумал. Самое безопасное, что в этом случае можно сделать — это упасть. Что обсуждаемая обертка и делает.

L>>Забыл подписаться, кэп.

E>Тогда я всё равно не понимаю, при чём тут расстрел памяти и забытость потока?
www.blinnov.com
Re[15]: 1) Родовая травма: исключеиния в деструкторах
От: Erop Россия  
Дата: 17.10.12 12:23
Оценка:
Здравствуйте, landerhigh, Вы писали:


L>Так вот, по поводу реактора. Пока ты там подтираешь 100500 гектар временных файлов, поток, которому ты не успел забыл сказать, что пора домой, тихонько продолжал вытаскивать стержни из актвной зоны. К моменту, когда ты блестяще избавишься от временных файлов, всем будет уже кагбе пофиг.


А не надо писать такие потоки, которые без всякого смысла и толка вытаскивают стержни из зон...
Если у тебя есть поток, который рулит реактором, например, по потоку на реактор, то он должен рулить даже если основная программа затупит, ради того в отдельный поток и вынесен. И чего его убивать, что бы реактором вообще не рулили, если в основной программе где-то контрола на найдётся в диалоге, например, лично мне не понятно.

E>>Ну недо писать такие потоки, которые сразу, как где-то перестали за ними следить через хэндл, сразу бросаются что-то там кромсать...


L>Поток, которому "не успели" вовремя сказать, что пора завершиться, остается один на один с данными, которые уже могут быть потерты и с объектами, котрые уже тоже давно в лучшем случае зомби. То, что в результате он чего-то там покромсал, вина исключительно программиста.


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

E>>В РФ это, кроме всего прочего, ещё и УК, кстати...


L>Тебе все хиханьки да хаханьки, а у нас перед релизом обязательный safety review. Потому что косяк в коде имеет вполне реальные шансы превратиться в невовремя замкнутый разрыватель на 600 КВ линии переменного тока с сопутствующими спецэфеектами и органическими удобрениями вдоль линии.


Дык потому и не надо писать опасный код...
Кстати, а если ваше ПО уходит по терминэйту в астрал, то что со станцией случается?

E>>Думаю, что не будем...

L>У вас все впереди. Не зарекайся.

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

L>Давай договоримся — ты не строишь из себя дурочку не по делу, а я не пишу ничего о счете и косточковых фруктах?

Ты намекаешь на то, что ты слил? Я думал тебе по существу вопроса что-то обсудить хочется, если ты за косточкой сюда пришёл, то иди себе, соси свою косточку. Ты слил...

L>>>Потому что. Это ловушка для дурака. Если не хочешь ждать завершения — сделай ему detach или чего у него там есть в API. Если забыл, то, скорее всего, ты много чего еще "забыл".

Есть вариант круче, просто не пользоваться этим придурошным классом.

L>logic еггог? Если только у программиста.

И что?

E>>Тогда я всё равно не понимаю, при чём тут расстрел памяти и забытость потока?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[18]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 17.10.12 13:52
Оценка:
Здравствуйте, Ku-ku, Вы писали:

EP>>Ну вот допустим вызовы deferred расставлялись бы компилятором

KK>То есть что-то вроде этого?
KK>
class File
KK>{
KK>public:
KK>    /*...*/
KK>    ~File(std::finalize_t) { flush(); }
KK>    ~File() { releaseResources(); }
KK>};

KK>void foo(const wchar_t* filename)
KK>{
KK>    {
KK>        File file(filename);
KK>        /*...*/
KK>    } // calls file.File::~File(std::finalize_t()), then file.File::~File()
KK>}


да, примерно так.

KK>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?


По исключениям. Ну он же знает когда terminate звать
Ну или как-то же scope(success) в языке D реализован.

KK>Мой поинт в том, что клиент сам должен принимать решение насчёт вызова deferred, а автор класса не должен навязывать какую-то "единственно правильную" стратегию. Например, я могу захотеть выйти из блока через return, не вызывая deferred.


Ну вот сейчас авторы классов навязывают стратегию — деструкторы тоже вызываются по return.
А вот в каких случаях не нужно вызывать flush по return, но нужно close — трудно представляю.
Но суть не в этом — выбор-то всё-равно есть. Автор класса может сделать доступным метод flush, и в private хранить и проверять is_flushed..

EP>>Какая разница для пользователя, кто делает эти вызовы — компилятор, или сгенерированный макросами деструктор?

KK>Разница будет хотя бы в раздельной спецификации исключений для ~File(std::finalize_t) и ~File().

Это действительно минус текущей реализации — согласен

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


Может да, а может и не так важно.
Меня волнует больше другой аспект: вот допустим есть контейнер для таких объектов — какое поведение должно быть у деструктора контейнера?
Видимо такое, которое подражает поведению стэковых объектов — то есть если во время деструкции произошло исключению в одном из под-объектов, то его нужно временно поймать, тихо (без deferred) погасить другие, и сделать throw;
Вопрос в том как вызывать остальные без deferred — ведь они фактически вызывается не во время раскрутки стэка.
То есть нужно что-то типа
try
{
   // try to destroy all with deferred
   // ...
   obj->~T();
}
catch(...)
{
   // destroy rest without deferred:
   // ..
   obj_some->~T(true);
   throw;
}

где true — это аргумент для Unwinding Aware Destructor. Причём он должен передаваться и всем родителям и членам.
Сейчас это можно сделать модификацией unwinding_indicator — добавить TLS счётчик, и сделать макрос для подражания синтаксису выше. Не очень красиво, но должно работать. В общем надо такой контейнер для примера реализовать.

KK>>>>>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?

EP>>>>Зависит от use-case — может пользователю будет достаточно того, что программа не вернёт EXIT_SUCCESS на не пойманном исключении.
KK>>>Итог непойманного исключения на таких уровнях — вызов std::terminate. А поймать их никак нельзя, потому что к моменту вызова деструктора управление уже успеет покинуть любой try блок в потоке, где происходит этот вызов.
EP>>Ну так я же и говорю, для кого-то может достаточно return code != EXIT_SUCCESS :
KK>Я смотрю на это как на потенциальную замаскированную ошибку.

Контр-пример — как ловить исключения из конструктора такого объекта?

EP>>Более того, необходимо помнить об транзитивности кидающих деструкторов.

KK>Если бы была поддержка на уровне компилятора, думаю, эту проблему можно было бы легко разрулить. Типа финализатор неявно вызывал бы финализаторы подобъектов, а деструктор — деструкторы подобъектов.

На счёт того что лучше — сначала всё deferred, а потом все деструкторы, или вперемешку — имхо лучше вперемешку, так как есть сейчас, но я это ещё не рассматривал.
А транзитивность я упомянул в контексте того, что допустим есть некий класс A, он используется по всей программе, никогда ничего из деструктора не кидал, код его использующий на это не расчитан. Теперь если где-то в глубине его композиции добавится член кидающий из деструктора, то сам этот класс A будет иметь кидающий деструктор.
Re[9]: 1) Родовая травма: исключеиния в деструкторах
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 17.10.12 15:05
Оценка: +3
Здравствуйте, enji, Вы писали:

E> Ну дык если надо что-то неубиваемое — создаем отдельный вотчдог, который перезапускает рабочий процесс, если тот перестал подавать признаки жизни.


Ага, а для контроля этого вотчдога — ещё один вотчдог Видел я такие конструкции в продакшене — довольно унылое зрелище...
[КУ] оккупировала армия.
Re[19]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 17.10.12 16:47
Оценка: 12 (1)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

KK>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?


EP>По исключениям.


У меня возникают сомнения насчёт того, что этого достаточно.

KK>>Мой поинт в том, что клиент сам должен принимать решение насчёт вызова deferred, а автор класса не должен навязывать какую-то "единственно правильную" стратегию. Например, я могу захотеть выйти из блока через return, не вызывая deferred.


EP>Ну вот сейчас авторы классов навязывают стратегию — деструкторы тоже вызываются по return.


Только для автоматических переменных. Никто не мешает управлять временем жизни объекта вручную.

EP>А вот в каких случаях не нужно вызывать flush по return, но нужно close — трудно представляю.


Например, когда функция не может бросать исключения и должна использовать коды возврата для сообщения об ошибках. Можно конечно бросить исключение и тут же его поймать, после чего сделать return, но с одним return было бы как-то проще.

EP>Но суть не в этом — выбор-то всё-равно есть. Автор класса может сделать доступным метод flush, и в private хранить и проверять is_flushed..


Как выполнить только очистку ресурсов?

EP>Меня волнует больше другой аспект: вот допустим есть контейнер для таких объектов — какое поведение должно быть у деструктора контейнера?


При поддержке со стороны компилятора можно было бы сделать так: финализатор зовёт финализаторы, деструктор зовёт деструкторы.

С комбинированными деструкторами можно поступить вот таким образом

template <typename T>
void destroy(T& t)
{
    t.~T();
}

template <typename BidirectionalIterator>
void destroyRange(BidirectionalIterator first, BidirectionalIterator last)
{
    while (last != first)
        (destroy)(*--last);
    SCOPE_EXIT(&) // or SCOPE_FAILURE(&)
    {
        while (last != first)
            (destroy)(*--last);
    };
}


KK>>>>>>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?


KK>>Я смотрю на это как на потенциальную замаскированную ошибку.


EP>Контр-пример — как ловить исключения из конструктора такого объекта?


Для нелокальных объектов — никак. Для локальных объектов — с помощью обычных try и catch.

void foo(const wchar_t* filename)
{
    try
    {
        static File file(filename);
    }
    catch (/*...*/)
    {
        /*...*/
    }
}

EP>>>Более того, необходимо помнить об транзитивности кидающих деструкторов.
KK>>Если бы была поддержка на уровне компилятора, думаю, эту проблему можно было бы легко разрулить. Типа финализатор неявно вызывал бы финализаторы подобъектов, а деструктор — деструкторы подобъектов.

EP>На счёт того что лучше — сначала всё deferred, а потом все деструкторы, или вперемешку — имхо лучше вперемешку, так как есть сейчас, но я это ещё не рассматривал.


Вызов вперемешку снова отдаляет нас от следования принципу "одна задача — одна функция".

EP>А транзитивность я упомянул в контексте того, что допустим есть некий класс A, он используется по всей программе, никогда ничего из деструктора не кидал, код его использующий на это не расчитан. Теперь если где-то в глубине его композиции добавится член кидающий из деструктора, то сам этот класс A будет иметь кидающий деструктор.


Ну вот и пример того, что потенциально бросающие операции не следует выполнять неявно.
Re[20]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 17.10.12 17:54
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?

EP>>По исключениям.
KK>У меня возникают сомнения насчёт того, что этого достаточно.

В смысле? Сейчас из деструктора нельзя кидать когда летит другое исключение, прям на выходе из него — компилятор же знает когда это происходит.
По-крайней мере он может различить когда он из обычного flow, переходит на unwinding и начинает звать деструкторы. Дальше, самое простое решение — передавать в деструктор флаг is_unwinding, и деструкторы передают этот флаг деструкторам вложенных объектов, а потом родителям.

KK>>>Мой поинт в том, что клиент сам должен принимать решение насчёт вызова deferred, а автор класса не должен навязывать какую-то "единственно правильную" стратегию. Например, я могу захотеть выйти из блока через return, не вызывая deferred.

EP>>Ну вот сейчас авторы классов навязывают стратегию — деструкторы тоже вызываются по return.
KK>Только для автоматических переменных. Никто не мешает управлять временем жизни объекта вручную.

точно также с объектами, которые имеют deferred действия.

EP>>А вот в каких случаях не нужно вызывать flush по return, но нужно close — трудно представляю.

KK>Например, когда функция не может бросать исключения и должна использовать коды возврата для сообщения об ошибках. Можно конечно бросить исключение и тут же его поймать, после чего сделать return, но с одним return было бы как-то проще.

Если flush кидает, то скорей всего и конструктор кидает, да и write тоже кидает — ловить придётся в любом случае.


EP>>Но суть не в этом — выбор-то всё-равно есть. Автор класса может сделать доступным метод flush, и в private хранить и проверять is_flushed..

KK>Как выполнить только очистку ресурсов?

Если это действительно нужно — об этом может позаботится автор класса на основе того же is_flushed.
Но, вряд ли это нужно — например fclose всегда flush'ит, его нельзя попросить только освободить хэндл.

EP>>Меня волнует больше другой аспект: вот допустим есть контейнер для таких объектов — какое поведение должно быть у деструктора контейнера?

KK>При поддержке со стороны компилятора можно было бы сделать так: финализатор зовёт финализаторы, деструктор зовёт деструкторы.

да, вот только я пока не вижу каких-то явных преимуществ этой группировки.
Может это даже немного хуже — ведь по-идеи, когда flush произошёл, объект можно сразу деструцировать. При группировке, объекты будут жить (и кушать ресурсы) немного дольше.

KK>С комбинированными деструкторами можно поступить вот таким образом


KK>template <typename BidirectionalIterator>

KK>void destroyRange(BidirectionalIterator first, BidirectionalIterator last)
KK>{
KK> while (last != first)
KK> (destroy)(*--last);
KK> SCOPE_EXIT(&) // or SCOPE_FAILURE(&)
KK> {
KK> while (last != first)
KK> (destroy)(*--last);
KK> };
KK>}[/ccode]

О, отличная идея!
Я даже не сразу въехал — действительно, ведь мы исключение никакое не ловим, uncaught_exception_count тем самым не изменяем!
Большое спасибо!
Небольшая поправка — SCOPE_EXIT/SCOPE_FAILUE нужно поставить до первого while иначе оно не вызовется при исключении (точно также как и в языке D).
Это даже можно в макрос завернуть, чтобы убрать дублирование. Я бы наверное предпочёл здесь SCOPE_FAILURE.
То есть новый макрос бы назывался как-то типа DO_NOW_AND_RETRY_FAILURE (только надо придумать что-нибудь более лаконичное). Либо в функцию принимающую лямбду.

KK>>>>>>>Может, ему взбредёт в голову сделать объект статическим или thread_local. Зачем ему тогда flush в деструкторе, и как он будет ловить возможные исключения оттуда?

KK>>>Я смотрю на это как на потенциальную замаскированную ошибку.
EP>>Контр-пример — как ловить исключения из конструктора такого объекта?
KK>Для нелокальных объектов — никак. Для локальных объектов — с помощью обычных try и catch.

Да, и это же не делает нелокальные объекты не-юзабельными?

EP>>>>Более того, необходимо помнить об транзитивности кидающих деструкторов.

KK>>>Если бы была поддержка на уровне компилятора, думаю, эту проблему можно было бы легко разрулить. Типа финализатор неявно вызывал бы финализаторы подобъектов, а деструктор — деструкторы подобъектов.
EP>>На счёт того что лучше — сначала всё deferred, а потом все деструкторы, или вперемешку — имхо лучше вперемешку, так как есть сейчас, но я это ещё не рассматривал.
KK>Вызов вперемешку снова отдаляет нас от следования принципу "одна задача — одна функция".

Представь что это две разные функции — одна вызывается по условию, вторая идёт за ней и без условия.

EP>>А транзитивность я упомянул в контексте того, что допустим есть некий класс A, он используется по всей программе, никогда ничего из деструктора не кидал, код его использующий на это не расчитан. Теперь если где-то в глубине его композиции добавится член кидающий из деструктора, то сам этот класс A будет иметь кидающий деструктор.

KK>Ну вот и пример того, что потенциально бросающие операции не следует выполнять неявно.

Когда не было исключений вообще, код точно также не был не рассчитан на них, как сейчас не рассчитан на кидающие деструкторы.
Re[21]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 17.10.12 20:52
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

KK>>>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?

EP>>>По исключениям.
KK>>У меня возникают сомнения насчёт того, что этого достаточно.

EP>В смысле? Сейчас из деструктора нельзя кидать когда летит другое исключение


А ещё, например, нельзя кидать из деструктора thread_local объекта. Будем звать оттуда deferred action или не? Вопрос вообще о том, хочет ли клиент вызова deferred action непосредственно до освобождения ресурсов.

EP>>>Ну вот сейчас авторы классов навязывают стратегию — деструкторы тоже вызываются по return.

KK>>Только для автоматических переменных. Никто не мешает управлять временем жизни объекта вручную.

EP>точно также с объектами, которые имеют deferred действия.


Не точно и не так же, если я не могу выполнить освобождение ресурсов без deferred action.

EP>>>А вот в каких случаях не нужно вызывать flush по return, но нужно close — трудно представляю.

KK>>Например, когда функция не может бросать исключения и должна использовать коды возврата для сообщения об ошибках. Можно конечно бросить исключение и тут же его поймать, после чего сделать return, но с одним return было бы как-то проще.

EP>Если flush кидает, то скорей всего и конструктор кидает, да и write тоже кидает — ловить придётся в любом случае.


Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.

KK>>Как выполнить только очистку ресурсов?


EP>Если это действительно нужно — об этом может позаботится автор класса на основе того же is_flushed.


А поконкретнее? Как это будет выглядеть в клиентском коде?

EP>Но, вряд ли это нужно — например fclose всегда flush'ит, его нельзя попросить только освободить хэндл.


fclose — очень древняя функция. Сомневаюсь, что десятилетия назад её авторы задумывались о таких вещах, как SRP.

EP>>>Меня волнует больше другой аспект: вот допустим есть контейнер для таких объектов — какое поведение должно быть у деструктора контейнера?

KK>>При поддержке со стороны компилятора можно было бы сделать так: финализатор зовёт финализаторы, деструктор зовёт деструкторы.

EP>да, вот только я пока не вижу каких-то явных преимуществ этой группировки.


Получается чёткое разделение обязанностей. Финализаторы и деструкторы не проверяют внутри себя никаких флагов. Проверка нужна только перед вызовом финализатора контейнера, и делать её должен тот, кто собирается его вызывать. Причём ровно один раз.

EP>Может это даже немного хуже — ведь по-идеи, когда flush произошёл, объект можно сразу деструцировать. При группировке, объекты будут жить (и кушать ресурсы) немного дольше.


Совсем немного

EP>Небольшая поправка — SCOPE_EXIT/SCOPE_FAILUE нужно поставить до первого while иначе оно не вызовется при исключении


Согласен, тут я протупил.

EP>>>Контр-пример — как ловить исключения из конструктора такого объекта?

KK>>Для нелокальных объектов — никак. Для локальных объектов — с помощью обычных try и catch.

EP>Да, и это же не делает нелокальные объекты не-юзабельными?


Смотря какие объекты. Я бы испытал некоторый восторг, если бы при невозможности открыть какой-то файл на старте программа падала из-за std::terminate.

KK>>Вызов вперемешку снова отдаляет нас от следования принципу "одна задача — одна функция".


EP>Представь что это две разные функции — одна вызывается по условию, вторая идёт за ней и без условия.


Не получается представить. Деструктор контейнера будет выполнять сначала часть deferred action, потом часть освобождения ресурсов, потом снова часть deferred action, потом снова часть освобождения ресурсов и т.д.
Re[22]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 18.10.12 11:05
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?

EP>>>>По исключениям.
KK>>>У меня возникают сомнения насчёт того, что этого достаточно.
EP>>В смысле? Сейчас из деструктора нельзя кидать когда летит другое исключение
KK>А ещё, например, нельзя кидать из деструктора thread_local объекта. Будем звать оттуда deferred action или не? Вопрос вообще о том, хочет ли клиент вызова deferred action непосредственно до освобождения ресурсов.

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

EP>>>>Ну вот сейчас авторы классов навязывают стратегию — деструкторы тоже вызываются по return.

KK>>>Только для автоматических переменных. Никто не мешает управлять временем жизни объекта вручную.
EP>>точно также с объектами, которые имеют deferred действия.
KK>Не точно и не так же, если я не могу выполнить освобождение ресурсов без deferred action.

1. Я не уверен что это действительно проблема.
2. fclose флушит всегда
3. Автора класса может добавить интерфейс для отмены deferred
4.
template<typename T>
void delete_without_deferred(const T *const ptr)
{
    try
    {
        scope(failure)
        {
            delete ptr;
        };
        throw 1;
    }
    catch(int){}
}
...
{
    File *f=new File("temp.txt");
    delete_without_deferred(f);
}



EP>>>>А вот в каких случаях не нужно вызывать flush по return, но нужно close — трудно представляю.

KK>>>Например, когда функция не может бросать исключения и должна использовать коды возврата для сообщения об ошибках. Можно конечно бросить исключение и тут же его поймать, после чего сделать return, но с одним return было бы как-то проще.
EP>>Если flush кидает, то скорей всего и конструктор кидает, да и write тоже кидает — ловить придётся в любом случае.
KK>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.

1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.
2. Может точно также реализовать класс который позволяет контролировать кидаются исключения из deferred или нет.

KK>>>Как выполнить только очистку ресурсов?

EP>>Если это действительно нужно — об этом может позаботится автор класса на основе того же is_flushed.
KK>А поконкретнее? Как это будет выглядеть в клиентском коде?

например:
{
    SomeWithDeferred obj;
    obj.do_not_flush();
}


EP>>Но, вряд ли это нужно — например fclose всегда flush'ит, его нельзя попросить только освободить хэндл.

KK>fclose — очень древняя функция. Сомневаюсь, что десятилетия назад её авторы задумывались о таких вещах, как SRP.

Я сомневаюсь что SRP полезен во всех случаях.

Вот например есть std::transform и std::accumulate. С помощью них можно вычислить скалярное произведение двух векторов.
А есть std::inner_product, который эти две операции комбинирует. И что, нарушение SRP? Причём злостный SRP нарушитель — inner_product, справляется с задачей намного быстрее и не требует промежуточных буферов.

Другой пример, есть autotools — всякие autoscan, aclocal, autoheader, automake, autoconf, configure, make — каждая тулза SRP'шная. Граф их комбинирования не самый простой (см wiki).
А есть CMake — который нисколько не SRP'шный, но использовать его намного проще

EP>>>>Меня волнует больше другой аспект: вот допустим есть контейнер для таких объектов — какое поведение должно быть у деструктора контейнера?

KK>>>При поддержке со стороны компилятора можно было бы сделать так: финализатор зовёт финализаторы, деструктор зовёт деструкторы.
EP>>да, вот только я пока не вижу каких-то явных преимуществ этой группировки.
KK>Получается чёткое разделение обязанностей. Финализаторы и деструкторы не проверяют внутри себя никаких флагов.

При поддержки со стороны компиляторов комбинированного варианта, деструкторы тоже не будут проверять флаги, по крайней мере явно.

KK>Проверка нужна только перед вызовом финализатора контейнера, и делать её должен тот, кто собирается его вызывать. Причём ровно один раз.

EP>>Может это даже немного хуже — ведь по-идеи, когда flush произошёл, объект можно сразу деструцировать. При группировке, объекты будут жить (и кушать ресурсы) немного дольше.
KK>Совсем немного

Если выбирать между излишним временем жизни объектов и проверками (пусть даже авто-генерированными), я бы выбрал лишние проверки.

EP>>>>Контр-пример — как ловить исключения из конструктора такого объекта?

KK>>>Для нелокальных объектов — никак. Для локальных объектов — с помощью обычных try и catch.
EP>>Да, и это же не делает нелокальные объекты не-юзабельными?
KK>Смотря какие объекты. Я бы испытал некоторый восторг, если бы при невозможности открыть какой-то файл на старте программа падала из-за std::terminate.

Ну так, тем не менее, этот corner-case же не перечёркивает преимущества кидающих конструкторов.

KK>>>Вызов вперемешку снова отдаляет нас от следования принципу "одна задача — одна функция".

EP>>Представь что это две разные функции — одна вызывается по условию, вторая идёт за ней и без условия.
KK>Не получается представить. Деструктор контейнера будет выполнять сначала часть deferred action, потом часть освобождения ресурсов, потом снова часть deferred action, потом снова часть освобождения ресурсов и т.д.

Это не деструктор контейнера будет делать, а комбинированный деструктор объектов.
Кстати, такое выполнение "вперемешку", соответствует тому, что происходит со стековыми объектами.
Конечно, можно и для стэковых реализовать вариант когда сначала все deferred, а потом все release, но тогда:
{
   Some a;
   Some b;
}

и
{
   Some a;
   {
      Some b;
   }
}

Имели бы разный порядок операций, что ИМХО намного хуже и менее интуитивно.
Re[23]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 18.10.12 15:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, Ku-ku, Вы писали:


KK>>>>>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?

EP>>>>>По исключениям.
KK>>>>У меня возникают сомнения насчёт того, что этого достаточно.
EP>>>В смысле? Сейчас из деструктора нельзя кидать когда летит другое исключение
KK>>А ещё, например, нельзя кидать из деструктора thread_local объекта. Будем звать оттуда deferred action или не? Вопрос вообще о том, хочет ли клиент вызова deferred action непосредственно до освобождения ресурсов.

EP>Ну та же самая ситуация с конструкторами глобальных объектов.


Ты не ответил на вопрос. И ситуация с конструкторами совсем не та же самая, так как у меня в распоряжении есть штатные приёмы для откладывания момента инициализации и размещения её там, где я могу поймать все вылетающие исключения. Прямого контроля над уничтожением объекта с static или thread storage duration у меня нет.

EP>Техническая невозможность запретить глобальные объекты с кидающими конструкторами, не делает их неюзабельными в общем случае.


Речь не о невозможности что-то запретить, а о полноте и удобстве контроля над объектом.

KK>>Не точно и не так же, если я не могу выполнить освобождение ресурсов без deferred action.


EP>1. Я не уверен что это действительно проблема.


То, что проблемы нет, тоже не очевидно.

EP>2. fclose флушит всегда


Это просто пример существующего интерфейса, необязательно хорошего.

EP>3. Автора класса может добавить интерфейс для отмены deferred


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

EP>4.

EP>
EP>template<typename T>
EP>void delete_without_deferred(const T *const ptr)
EP>{
EP>    try
EP>    {
EP>        scope(failure)
EP>        {
EP>            delete ptr;
EP>        };
EP>        throw 1;
EP>    }
EP>    catch(int){}
EP>}
EP>...
EP>{
EP>    File *f=new File("temp.txt");
EP>    delete_without_deferred(f);
EP>}

Угу, и ещё такой же шаблон для прямого вызова деструктора. Итого получим усложнение и возможный оверхэд на ровном месте.

EP>>>Если flush кидает, то скорей всего и конструктор кидает, да и write тоже кидает — ловить придётся в любом случае.

KK>>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.

EP>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.


А что делать с общими бессбойными операциями? Дублировать? Не так уж и привлекательно.

EP>2. Может точно также реализовать класс который позволяет контролировать кидаются исключения из deferred или нет.


И какую же версию будет звать деструктор: бросающую или небросающую?

EP>Я сомневаюсь что SRP полезен во всех случаях.


EP>Вот например есть std::transform и std::accumulate. С помощью них можно вычислить скалярное произведение двух векторов.

EP>А есть std::inner_product, который эти две операции комбинирует. И что, нарушение SRP?

Нет. SRP не подразумевает, что задача не может состоять из подзадач.

EP>>>>>Контр-пример — как ловить исключения из конструктора такого объекта?

KK>>>>Для нелокальных объектов — никак. Для локальных объектов — с помощью обычных try и catch.
EP>>>Да, и это же не делает нелокальные объекты не-юзабельными?
KK>>Смотря какие объекты. Я бы испытал некоторый восторг, если бы при невозможности открыть какой-то файл на старте программа падала из-за std::terminate.

EP>Ну так, тем не менее, этот corner-case же не перечёркивает преимущества кидающих конструкторов.


Аналогия с конструкторами неудачная. Конструктор всегда получает управление от видимой в коде конструкции, чего не скажешь о деструкторе.

EP>Кстати, такое выполнение "вперемешку", соответствует тому, что происходит со стековыми объектами.

EP>Конечно, можно и для стэковых реализовать вариант когда сначала все deferred, а потом все release, но тогда:
...
EP>Имели бы разный порядок операций, что ИМХО намного хуже и менее интуитивно.

Мне оба варианта не нравятся, и ломать голову над тем, какой из них хуже, как-то особо не хочется.
Re[24]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 18.10.12 20:15
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>>>>>>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?

EP>>>>>>По исключениям.
KK>>>>>У меня возникают сомнения насчёт того, что этого достаточно.
EP>>>>В смысле? Сейчас из деструктора нельзя кидать когда летит другое исключение
KK>>>А ещё, например, нельзя кидать из деструктора thread_local объекта. Будем звать оттуда deferred action или не? Вопрос вообще о том, хочет ли клиент вызова deferred action непосредственно до освобождения ресурсов.
EP>>Ну та же самая ситуация с конструкторами глобальных объектов.
KK>Ты не ответил на вопрос.

На выделенный? Если класс поддерживает deferred — да, будем звать
Точно также как будем кидать исключение из конструктора глобального объекта, если это интерфейс его класса.

KK>И ситуация с конструкторами совсем не та же самая, так как у меня в распоряжении есть штатные приёмы для откладывания момента инициализации и размещения её там, где я могу поймать все вылетающие исключения. Прямого контроля над уничтожением объекта с static или thread storage duration у меня нет.


Например, какие есть штатные приёмы для глобальных объектов? auto_ptr, optional, и т.п.? Ну сделай тоже самое с deferred — явно уничтожай их.

EP>>Техническая невозможность запретить глобальные объекты с кидающими конструкторами, не делает их неюзабельными в общем случае.

KK>Речь не о невозможности что-то запретить, а о полноте и удобстве контроля над объектом.

Ну вот смотри, есть итераторы, есть различные view чего-нибудь, да хоть обычные указатели, то есть это что-то позволяющие взаимодействовать с неким третьим объектом когда он жив, но при этом вызывающие UB/расстрел памяти/segfault если вдруг объект мёртв.
И что, из-за того, что есть всякие corner-case'ы, они же не становятся неюзабельными?

KK>>>Не точно и не так же, если я не могу выполнить освобождение ресурсов без deferred action.

EP>>1. Я не уверен что это действительно проблема.
KK>То, что проблемы нет, тоже не очевидно.
EP>>2. fclose флушит всегда
KK>Это просто пример существующего интерфейса, необязательно хорошего.

Я ни разу не видел возмущения на счёт того, что fclose флушит. А вот обсуждения на тему flush в деструкторе + исключения, идут до сих пор.

KK>Угу, и ещё такой же шаблон для прямого вызова деструктора. Итого получим усложнение и возможный оверхэд на ровном месте.


А ещё тратится место на unwinding_indicator — это же библиотечное решение, а не вшитое в компилятор. При поддержки со стороны компилятора, можно реализовать решение "pay as you go".

EP>>>>Если flush кидает, то скорей всего и конструктор кидает, да и write тоже кидает — ловить придётся в любом случае.

KK>>>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.
EP>>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.
KK>А что делать с общими бессбойными операциями? Дублировать? Не так уж и привлекательно.

Зачем дублировать? Пусть тот который с исключениями, наследует того который без исключений.
Продолжение в том же духе: а что если не только исключения запрещены, но и интерфейс должен быть полностью plain C

EP>>2. Может точно также реализовать класс который позволяет контролировать кидаются исключения из deferred или нет.

KK>И какую же версию будет звать деструктор: бросающую или небросающую?

Версия одна, бросает или нет — зависит от флага. Также как и в ostream — достаточно ведь exceptions установить, а методы остаются теме же.

EP>>Я сомневаюсь что SRP полезен во всех случаях.

EP>>Вот например есть std::transform и std::accumulate. С помощью них можно вычислить скалярное произведение двух векторов.
EP>>А есть std::inner_product, который эти две операции комбинирует. И что, нарушение SRP?
KK>Нет. SRP не подразумевает, что задача не может состоять из подзадач.

Ну так и тут есть задача финализации объекта, которая состоит из подзадач — deferred и release.

EP>>>>>>Контр-пример — как ловить исключения из конструктора такого объекта?

KK>>>>>Для нелокальных объектов — никак. Для локальных объектов — с помощью обычных try и catch.
EP>>>>Да, и это же не делает нелокальные объекты не-юзабельными?
KK>>>Смотря какие объекты. Я бы испытал некоторый восторг, если бы при невозможности открыть какой-то файл на старте программа падала из-за std::terminate.
EP>>Ну так, тем не менее, этот corner-case же не перечёркивает преимущества кидающих конструкторов.
KK>Аналогия с конструкторами неудачная. Конструктор всегда получает управление от видимой в коде конструкции, чего не скажешь о деструкторе.

Ну и что, что её видно? Допустим, опять-таки, есть глобальный объект, даже несколько, в разных TU, порядок вызова этих "видимых" конструкций определён?

EP>>Имели бы разный порядок операций, что ИМХО намного хуже и менее интуитивно.

KK>Мне оба варианта не нравятся, и ломать голову над тем, какой из них хуже, как-то особо не хочется.

А мне свекла не нравится А если серьёзно, то я не увидел сильных аргументов против.
Re[25]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 19.10.12 08:21
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

KK>>>>>>>>Cразу возникает вопрос: а как компилятор будет угадывать, когда надо вызывать deferred и когда не надо?

EP>>>>>>>По исключениям.
KK>>>>>>У меня возникают сомнения насчёт того, что этого достаточно.
EP>>>>>В смысле? Сейчас из деструктора нельзя кидать когда летит другое исключение
KK>>>>А ещё, например, нельзя кидать из деструктора thread_local объекта. Будем звать оттуда deferred action или не? Вопрос вообще о том, хочет ли клиент вызова deferred action непосредственно до освобождения ресурсов.
EP>Если класс поддерживает deferred — да, будем звать

Ну то есть безопасность вызова не является критерием и ориентируемся мы исключительно на то, вызван ли деструктор из-за раскрутки стека. Позиция понятна.

KK>>И ситуация с конструкторами совсем не та же самая, так как у меня в распоряжении есть штатные приёмы для откладывания момента инициализации и размещения её там, где я могу поймать все вылетающие исключения. Прямого контроля над уничтожением объекта с static или thread storage duration у меня нет.


EP>Например, какие есть штатные приёмы для глобальных объектов? auto_ptr, optional, и т.п.?


Объекты, чьё время жизни регулируется классами auto_ptr и optional, имеют dynamic storage duration. Я имел в виду создание функции наподобие

File& file()
{
    static File f(getFilename());
    return f;
}

Ещё можно создать объект, не связанный с каким-либо файлом и потом открыть файл через какой-нибудь open.

EP>Ну вот смотри, есть итераторы, есть различные view чего-нибудь, да хоть обычные указатели, то есть это что-то позволяющие взаимодействовать с неким третьим объектом когда он жив, но при этом вызывающие UB/расстрел памяти/segfault если вдруг объект мёртв.

EP>И что, из-за того, что есть всякие corner-case'ы, они же не становятся неюзабельными?

Неюзабельными не становятся. Зато становятся error prone в какой-то степени.

У нас есть два подхода: засовывать в деструкторы отложенные операции, не являющиеся очисткой ресурсов, или не засовывать. Оба подхода можно сравнить на предмет читаемости, предрасположенности к провоцированию совершения ошибок и простоты поддержки. Для меня, с учётом моих субъективных качеств, первый подход вчистую проигрывает второму по пунктам 1 и 3 и как минимум не лучше по пункту 2. Но да, каких-то фатальных недостатков, делающих первый подход абсолютно неюзабельным, я не вижу.

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

void errorHandler()
{
    try
    {
        throw;
    }
    catch (/*...*/)
    {
        /*...*/
    }
}

class File
{
/*...*/
public:
    File(const wchar_t* filename, const std::function<void()>& errorHandler) :
        errorHandler(errorHandler)
    {
        /*...*/
    }
    ~File()
    {
        try
        {
            flush();
        }
        catch (...)
        {
            errorHandler();
        }
        release();
    }
private:
    std::function<void> errorHandler;
};

void foo(const wchar_t* filename)
{
    File(filename, errorHandler);
}

Или вытаскивать наружу исключения не с помощью раскрутки стека, а через передачу наверх std::exception_ptr

class File
{
/*...*/
public:
    File(const wchar_t* filename, std::exception_ptr& exception) :
        exception(exception)
    {
        /*...*/
    }
    ~File()
    {
        try
        {
            flush();
        }
        catch (...)
        {
            exception = std::current_exception();
        }
        release();
    }
private:
    std::exception_ptr& exception;
};

void foo(const wchar_t* filename, std::exception_ptr& exception)
{
    File(filename, exception);
}


EP>>>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.

KK>>А что делать с общими бессбойными операциями? Дублировать? Не так уж и привлекательно.

EP>Зачем дублировать? Пусть тот который с исключениями, наследует того который без исключений.


Не пойму в чём прелесть такого финта ушами.

EP>Продолжение в том же духе: а что если не только исключения запрещены, но и интерфейс должен быть полностью plain C


Тогда дублирование оправдано, поскольку пользоваться C++ интерфейсом там, где это возможно, гораздо удобнее, чем сишным.

EP>>>2. Может точно также реализовать класс который позволяет контролировать кидаются исключения из deferred или нет.

KK>>И какую же версию будет звать деструктор: бросающую или небросающую?

EP>Версия одна, бросает или нет — зависит от флага.


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

EP>Также как и в ostream — достаточно ведь exceptions установить, а методы остаются теме же.


ostream — это настоящая помойка и пример того, как делать не нужно.

KK>>Нет. SRP не подразумевает, что задача не может состоять из подзадач.


EP>Ну так и тут есть задача финализации объекта, которая состоит из подзадач — deferred и release.


Финализация объекта — это не задача. Если бы произвольную сумму действий можно было назвать задачей, то SRP не имел бы смысла.

KK>>Аналогия с конструкторами неудачная. Конструктор всегда получает управление от видимой в коде конструкции, чего не скажешь о деструкторе.


EP>Ну и что, что её видно? Допустим, опять-таки, есть глобальный объект, даже несколько, в разных TU, порядок вызова этих "видимых" конструкций определён?


У меня нет привычки сначала добавлять в программу глобальные переменные, а потом уже думать, как их надо инициализировать. Если меня по каким-то причинам не устраивают возможные способы инициализации глобальной переменной, то вместо глобальной переменной я буду использовать что-то другое. Вопрос в том, насколько сложным будет это "что-то другое". Отсутствие необходимости заботиться о точном времени уничтожения объекта делает мой выбор более свободным.

EP>А мне свекла не нравится А если серьёзно, то я не увидел сильных аргументов против.


Ну значит пора сворачить дискуссию. Пусть каждый останется при своём мнении.
Re[26]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 19.10.12 10:39
Оценка:
Здравствуйте, Ku-ku, Вы писали:

KK>Ну то есть безопасность вызова не является критерием и ориентируемся мы исключительно на то, вызван ли деструктор из-за раскрутки стека. Позиция понятна.


да, именно так. deferred — своего рода scope(success) выполняемый перед деструктором.

KK>>>И ситуация с конструкторами совсем не та же самая, так как у меня в распоряжении есть штатные приёмы для откладывания момента инициализации и размещения её там, где я могу поймать все вылетающие исключения. Прямого контроля над уничтожением объекта с static или thread storage duration у меня нет.

EP>>Например, какие есть штатные приёмы для глобальных объектов? auto_ptr, optional, и т.п.?
KK>Объекты, чьё время жизни регулируется классами auto_ptr и optional, имеют dynamic storage duration. Я имел в виду создание функции наподобие
KK>
File& file()
KK>{
KK>    static File f(getFilename());
KK>    return f;
KK>}


трик известный, вот только вопрос был про глобальный объект.

KK>Ещё можно создать объект, не связанный с каким-либо файлом и потом открыть файл через какой-нибудь open.


то есть это вариант с некидающим конструктором, и явным вызовом кидающего действия? можно также явно вызывать deferred..

EP>>Ну вот смотри, есть итераторы, есть различные view чего-нибудь, да хоть обычные указатели, то есть это что-то позволяющие взаимодействовать с неким третьим объектом когда он жив, но при этом вызывающие UB/расстрел памяти/segfault если вдруг объект мёртв.

EP>>И что, из-за того, что есть всякие corner-case'ы, они же не становятся неюзабельными?
KK>Неюзабельными не становятся. Зато становятся error prone в какой-то степени.

Согласен, error-prone момент присутствует. Также как и у кидающих конструкторов глобальных объектов.

KK>У нас есть два подхода: засовывать в деструкторы отложенные операции, не являющиеся очисткой ресурсов, или не засовывать.


Отложенные операции засовываются в деструкторы и сейчас. Выполняются всегда. Иногда фейлятся, иногда нет. Если фейлятся, то исключения глотаются + максимум скромная запись в log.

KK>Оба подхода можно сравнить на предмет читаемости, предрасположенности к провоцированию совершения ошибок и простоты поддержки. Для меня, с учётом моих субъективных качеств, первый подход вчистую проигрывает второму по пунктам 1 и 3 и как минимум не лучше по пункту 2.


ИМХО, когда исключения только появились, они тоже сливали по пунктам 1 и 3 коду без них, как минимум потому, что далеко не все сразу научились ими пользоваться (к сожалению, многие не умеют это делать и по сей день).

KK>Но да, каких-то фатальных недостатков, делающих первый подход абсолютно неюзабельным, я не вижу.



Фатальных недостатков я тоже не вижу. Мне интересно узнать, в первую очередь, именно о них.

KK>Кстати, с исключениями можно было бы и по-другому извратиться. Например, обрабатывать исключения прямо в деструкторе с помощью заранее сохранённого в объекте обработчика исключений.

KK>
void errorHandler()
...
KK>void foo(const wchar_t* filename)
KK>{
KK>    File(filename, errorHandler);
KK>}

KK>Или вытаскивать наружу исключения не с помощью раскрутки стека, а через передачу наверх std::exception_ptr
KK>void foo(const wchar_t* filename, std::exception_ptr& exception)
KK>{
KK> File(filename, exception);
KK>}[/ccode]

То есть появляется два параллельных механизма обработки "исключений" (обычный и errorHandler/exception_ptr), работающих по разному, каждое из которых требует разных действий со стороны пользоватля? Причём вариант с явным errorHandler/exception_ptr требует лишних действий на всём пути от throw до catch.

EP>>>>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.

KK>>>А что делать с общими бессбойными операциями? Дублировать? Не так уж и привлекательно.
EP>>Зачем дублировать? Пусть тот который с исключениями, наследует того который без исключений.
KK>Не пойму в чём прелесть такого финта ушами.

это ответ на вопрос про дублирование.

EP>>>>2. Может точно также реализовать класс который позволяет контролировать кидаются исключения из deferred или нет.

KK>>>И какую же версию будет звать деструктор: бросающую или небросающую?
EP>>Версия одна, бросает или нет — зависит от флага.
KK>Ну то есть при наличии этих наворотов забот у программиста меньше не становится, теперь он должен помнить о дополнительном состоянии объекта.

он должен помнить об этом состоянии, только в случае если ему приспичит реализовать класс поддерживающий несколько вариантов обработки ошибок, где вариант выбирается в runtime. (это можно автоматизировать макросами)

EP>>Также как и в ostream — достаточно ведь exceptions установить, а методы остаются теме же.

KK>ostream — это настоящая помойка и пример того, как делать не нужно.

Ну так я согласен. Это же не я предложил класс поддерживающий два варианта обработки ошибок

KK>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.

ostream делает именно это.

KK>>>Нет. SRP не подразумевает, что задача не может состоять из подзадач.

EP>>Ну так и тут есть задача финализации объекта, которая состоит из подзадач — deferred и release.
KK>Финализация объекта — это не задача. Если бы произвольную сумму действий можно было назвать задачей, то SRP не имел бы смысла.

При поддержки со стороны компилятора, когда deferred вызывается не из деструктора — SRP нарушается?

EP>>А мне свекла не нравится А если серьёзно, то я не увидел сильных аргументов против.

KK>Ну значит пора сворачить дискуссию. Пусть каждый останется при своём мнении.

Ну так ты же тоже согласен, что каких-то фатальных недостатоков нет.
Не фатальные — есть (хотя и не маловажные, и несомненно требующие внимания).
Re[10]: 1) Родовая травма: исключеиния в деструкторах
От: enji  
Дата: 19.10.12 14:54
Оценка:
Здравствуйте, koandrew, Вы писали:

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


E>> Ну дык если надо что-то неубиваемое — создаем отдельный вотчдог, который перезапускает рабочий процесс, если тот перестал подавать признаки жизни.


K>Ага, а для контроля этого вотчдога — ещё один вотчдог Видел я такие конструкции в продакшене — довольно унылое зрелище...


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

В простом случае одного вотчдога хватает — обычно вотчдог очень прост и мы полагаем, что зависнуть он не может Так например firebird работает.
Re[2]: C++ и RAII
От: dr.Chaos Россия Украшения HandMade
Дата: 19.10.12 17:56
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>Что-то, Егор, я не улавливаю сути вопроса. Ты хочешь, что бы мы совместными усилиями провели четкую границу применимости RAII?


Нет он просто проникся монадой Maibe и понял что обработка ошибок с помощью исключений ущербна. Только ФВП, только продолжения, только хардкор.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re[2]: C++ и RAII
От: dr.Chaos Россия Украшения HandMade
Дата: 19.10.12 18:01
Оценка:
Здравствуйте, andyp, Вы писали:

A>2. Когда попадаем в деструктор не совсем ясно, то ли мы раскручаем стек из-за исключения, связанного с нашим ресурсом, то ли все нормально (ну те нет исключения), то ли исключение вызвано чем-то другим (не нашим ресурсом). Конечно в с++11 есть current_exception(), но проверять и отлавливать свои исключения...

И добавьте сюда, что у некоторых компиляторов unhandle_exception не потокобеопасный.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re[27]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 19.10.12 20:40
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>трик известный, вот только вопрос был про глобальный объект.


Так мне вроде никто не запрещал вместо глобального объекта использовать что-нибудь другое

EP>>>Ну вот смотри, есть итераторы, есть различные view чего-нибудь, да хоть обычные указатели, то есть это что-то позволяющие взаимодействовать с неким третьим объектом когда он жив, но при этом вызывающие UB/расстрел памяти/segfault если вдруг объект мёртв.

EP>>>И что, из-за того, что есть всякие corner-case'ы, они же не становятся неюзабельными?
KK>>Неюзабельными не становятся. Зато становятся error prone в какой-то степени.
EP>Согласен, error-prone момент присутствует. Также как и у кидающих конструкторов глобальных объектов.

По-моему, вероятность забыть про кидающие деструкторы гораздо выше. В большинстве случаев чтобы произвести нетривиальную инициализацию, надо сначала получить аргументы инициализации. Вычислить подходящие аргументы до входа в main ещё надо постараться. Уже только одно это будет заставлять задумываться об альтернативе инициализации до main.

KK>>У нас есть два подхода: засовывать в деструкторы отложенные операции, не являющиеся очисткой ресурсов, или не засовывать.

EP>Отложенные операции засовываются в деструкторы и сейчас. Выполняются всегда. Иногда фейлятся, иногда нет. Если фейлятся, то исключения глотаются + максимум скромная запись в log.

Я так не делаю. Ну может только за исключением обёрток над каким-нибудь кривым API.

KK>>Оба подхода можно сравнить на предмет читаемости, предрасположенности к провоцированию совершения ошибок и простоты поддержки. Для меня, с учётом моих субъективных качеств, первый подход вчистую проигрывает второму по пунктам 1 и 3 и как минимум не лучше по пункту 2.

EP>ИМХО, когда исключения только появились, они тоже сливали по пунктам 1 и 3 коду без них, как минимум потому, что далеко не все сразу научились ими пользоваться (к сожалению, многие не умеют это делать и по сей день).

Ты веришь, что твою любимую методику ждёт неминуемый успех? Что ж, мечтать не вредно

EP>>>>>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.

KK>>>>А что делать с общими бессбойными операциями? Дублировать? Не так уж и привлекательно.
EP>>>Зачем дублировать? Пусть тот который с исключениями, наследует того который без исключений.
KK>>Не пойму в чём прелесть такого финта ушами.
EP>это ответ на вопрос про дублирование.

Я так и не понял зачем надо два класса вместо одного.

KK>>ostream — это настоящая помойка и пример того, как делать не нужно.

EP>Ну так я согласен. Это же не я предложил класс поддерживающий два варианта обработки ошибок

Использование флагов — это не единственный способ. Можно предоставить две функции, как например делается в случае basic_stream_socket::connect из Boost.

KK>>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.

EP>ostream делает именно это.

Но через заднее место.

KK>>>>Нет. SRP не подразумевает, что задача не может состоять из подзадач.

EP>>>Ну так и тут есть задача финализации объекта, которая состоит из подзадач — deferred и release.
KK>>Финализация объекта — это не задача. Если бы произвольную сумму действий можно было назвать задачей, то SRP не имел бы смысла.
EP>При поддержки со стороны компилятора, когда deferred вызывается не из деструктора — SRP нарушается?

Вроде, не нарушается. AFAIK, SRP распространяется на функции, классы, модули. А тут к чему его применять?

EP>>>А мне свекла не нравится А если серьёзно, то я не увидел сильных аргументов против.

KK>>Ну значит пора сворачить дискуссию. Пусть каждый останется при своём мнении.
EP>Ну так ты же тоже согласен, что каких-то фатальных недостатоков нет.

У ручного управления ресурсами, например, их тоже нет. Но это как-то не очень мотивирует к применению такого подхода при возможности использовать более удобную альтернативу вроде RAII.
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: Ночной Смотрящий Россия  
Дата: 19.10.12 21:26
Оценка: +1 :)
Здравствуйте, Erop, Вы писали:

E>Конструктор от имени файла будет у нас документ создавать.

E>Конструктор по умолчанию, будет создавать пустой документ
E>Деструктор будет записывать изменения, если они были.

E>Вот тут нас и ждёт засада. Предположим, что в деструкторе что-то пошло не так, файл оказался недоступен по записи, или его вообще ещё не назначили этому документу, или мы вообще решили перед записью уточнять надо ли записать изменения.

E>Вот тут-то у нас и возникнет проблема. Во-первых, нам придётся всё это спросить не выходя из деструктора, то есть, засунуть в данные кусок интерфейса, либо нагородить каких-то колбэков.
E>Ну это ещё пол-беды. Главная родовая травма деструкторов С++, это то, что их нельзя тормознуть.
E>То есть, если пользователь в алерт-боксе, нажмёт не "Сохранить" или "не сохранять", а "отменить", то мы вообще не сможем никак этого поддержать в нашей прекрасной RAII-архитектуре

Тут проблема не в RAII, а в грубейшем нарушении SRP.
Re[3]: C++ и RAII
От: Ночной Смотрящий Россия  
Дата: 19.10.12 21:30
Оценка:
Здравствуйте, Erop, Вы писали:

E>Просто меня тут поддостал кое-кто из боссов


А с чего ты решил, что это кто то конкретный? Я так проглядел выборочно последние твои баны — вроде везде по делу. Может проблемы в себе поискать?
Re[4]: C++ и RAII
От: Erop Россия  
Дата: 19.10.12 21:42
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>А с чего ты решил, что это кто то конкретный?

С переписки...

НС>Я так проглядел выборочно последние твои баны — вроде везде по делу.

Например, мне трудно считать, что это по делу, а не от того, что нечего ответить

НС> Может проблемы в себе поискать?

Причины того, что поддостал? Ну да, у меня обострённое чувство справедливости. Думаешь это плохо?

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

В целом я просто проверил местную публику на вшивость и сделал для себя выводы, стоит так уж хотеть тут присутствовать, или пустое это всё. Все могут посмотреть на результаты моих скормных тестов и тоже сделать свои выводы...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: C++ и RAII
От: Ночной Смотрящий Россия  
Дата: 19.10.12 21:54
Оценка: :)
Здравствуйте, Erop, Вы писали:

НС>>Я так проглядел выборочно последние твои баны — вроде везде по делу.

E>Например, мне трудно считать, что это по делу, а не от того, что нечего ответить

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

E>Все могут посмотреть на результаты моих скормных тестов и тоже сделать свои выводы...


Только не факт, что выводы будут такими, какие тебе хочется.
Re[6]: C++ и RAII
От: Erop Россия  
Дата: 19.10.12 22:00
Оценка: :)
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Т.е. ты намеренно занялся провокацией, о чем ниже признался, а когда тебя за провокацию забанили пытаешься выдать себя за невинную овечку? Какая то запредельная степень нарциссизма.


В смысле провокацией? Мне правда не ясно в чём суть претензий Влада к другим, когда он сам занят немерлением...

НС>Только не факт, что выводы будут такими, какие тебе хочется.

Мне всё равно какие будут у кого выводы.

Мне задали вопрос, я объяснил своё поведение. Тот, кому я объяснял, походу меня понял. Остальные могут решать что им нравится. Мне это не важно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[28]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 19.10.12 22:39
Оценка:
Здравствуйте, Ku-ku, Вы писали:

EP>>трик известный, вот только вопрос был про глобальный объект.

KK>Так мне вроде никто не запрещал вместо глобального объекта использовать что-нибудь другое

EP>>Например, какие есть штатные приёмы для глобальных объектов? auto_ptr, optional, и т.п.?

я к тому, что конструкторы объектов могут кидать, в том числе и глобальных, но это не делает кидающие конструкторы неюзабельными.


KK>>>Оба подхода можно сравнить на предмет читаемости, предрасположенности к провоцированию совершения ошибок и простоты поддержки. Для меня, с учётом моих субъективных качеств, первый подход вчистую проигрывает второму по пунктам 1 и 3 и как минимум не лучше по пункту 2.

EP>>ИМХО, когда исключения только появились, они тоже сливали по пунктам 1 и 3 коду без них, как минимум потому, что далеко не все сразу научились ими пользоваться (к сожалению, многие не умеют это делать и по сей день).
KK>Ты веришь, что твою любимую методику ждёт неминуемый успех? Что ж, мечтать не вредно

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

EP>>>>>>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.

KK>>>>>А что делать с общими бессбойными операциями? Дублировать? Не так уж и привлекательно.
EP>>>>Зачем дублировать? Пусть тот который с исключениями, наследует того который без исключений.
KK>>>Не пойму в чём прелесть такого финта ушами.
EP>>это ответ на вопрос про дублирование.
KK>Я так и не понял зачем надо два класса вместо одного.

имхо, ситуации, в которых требуются методы выполняющие одинаковые действия, но отличающиеся throw/return_error_code, нужны одновременно для одного объекта — редки. То есть обычно нужны либо exceptions, либо error-code.

EP>>>>>>>>Если flush кидает, то скорей всего и конструктор кидает, да и write тоже кидает — ловить придётся в любом случае.

KK>>>>>>>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.
EP>>>>>>1. ИМХО, лучше иметь два разных класса — один с исключениями, другой без.
EP>>>>>>2. Может точно также реализовать класс который позволяет контролировать кидаются исключения из deferred или нет.
KK>>>>>И какую же версию будет звать деструктор: бросающую или небросающую?
EP>>>>Также как и в ostream — достаточно ведь exceptions установить, а методы остаются теме же.
KK>>>ostream — это настоящая помойка и пример того, как делать не нужно.
EP>>Ну так я согласен. Это же не я предложил класс поддерживающий два варианта обработки ошибок
KK>Использование флагов — это не единственный способ. Можно предоставить две функции, как например делается в случае basic_stream_socket::connect из Boost.
KK>>>Это зависит от интерфейса File. Иногда одни и те же операции реализуют в двух вариантах: с использованием исключений для сообщения об ошибках и с использованием кодов ошибок.
EP>>ostream делает именно это.
KK>Но через заднее место.

((ostream -> помойка) && (ostream использует флаги для exceptions)) -> всё что использует флаги теперь ацтой, и так делать не нужно?

KK>>>>>Нет. SRP не подразумевает, что задача не может состоять из подзадач.

EP>>>>Ну так и тут есть задача финализации объекта, которая состоит из подзадач — deferred и release.
KK>>>Финализация объекта — это не задача. Если бы произвольную сумму действий можно было назвать задачей, то SRP не имел бы смысла.
EP>>При поддержки со стороны компилятора, когда deferred вызывается не из деструктора — SRP нарушается?
KK>Вроде, не нарушается. AFAIK, SRP распространяется на функции, классы, модули. А тут к чему его применять?

Ну а к чему ты применяешь SRP в случае текущей реализации? К авто-генерированному деструктору?
В чём существенная разница, между текущей реализацией и решением основанным на поддержке компилятора, которая делает делает одно не SRP-шным, а второе SRP-шным?
Только exception specification??

EP>>>>А мне свекла не нравится А если серьёзно, то я не увидел сильных аргументов против.

KK>>>Ну значит пора сворачить дискуссию. Пусть каждый останется при своём мнении.
EP>>Ну так ты же тоже согласен, что каких-то фатальных недостатоков нет.
KK>У ручного управления ресурсами, например, их тоже нет. Но это как-то не очень мотивирует к применению такого подхода при возможности использовать более удобную альтернативу вроде RAII.

1. надо ставить flush руками
2. не надо ставить flush руками

что более удобное
Re[29]: 1) Родовая травма: исключеиния в деструкторах
От: Ku-ku  
Дата: 20.10.12 10:20
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>я к тому, что конструкторы объектов могут кидать, в том числе и глобальных, но это не делает кидающие конструкторы неюзабельными.


А я к тому, что бросающие деструкторы — error prone, причём в большей степени, чем конструкторы.

EP>Нет, я всего лишь показал, что "читаемость" и "простота поддержки", в этом случае являются вопросом "existance practice", и не являются каким-либо фундаментальным фейлом самого механизма.


Это твоё мнение, и я с ним не согласен.

EP>((ostream -> помойка) && (ostream использует флаги для exceptions)) -> всё что использует флаги теперь ацтой, и так делать не нужно?


ostream — потому и помойка, что берёт на себя кучу совершенно разных обязанностей, которые можно и следовало бы разделить по отдельным классам и функциям. Наличие у объекта состояний, которые не обусловлены изначально возлагаемой на него задачей, — это именно отстой. Любая функция, инкапсулирующая какое-то действие над объектом, должна или документировать в каком состоянии она оставляет флаг, или заниматься сохранением флага перед началом работы и восстановлением его значения по завершении. Первый подход создаёт геморрой для вызывающей стороны, а второй не удобен для того, кто реализует функцию. Да и вообще непонятно считать ли изменение флага логической модификацией объекта. Если какая-то операция изменяет флаг, но в остальном никак не меняет состояние объекта, то она мутирующая или нет?

Забота об исключениях — это отдельная задача. Если она не является единственной задачей объекта, то согласно SRP объект не должен браться за её решение.

EP>Ну а к чему ты применяешь SRP в случае текущей реализации? К авто-генерированному деструктору?


Да. Деструктор — часть интерфейса.

EP>В чём существенная разница, между текущей реализацией и решением основанным на поддержке компилятора, которая делает делает одно не SRP-шным, а второе SRP-шным?


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

EP>>>Ну так ты же тоже согласен, что каких-то фатальных недостатоков нет.

KK>>У ручного управления ресурсами, например, их тоже нет. Но это как-то не очень мотивирует к применению такого подхода при возможности использовать более удобную альтернативу вроде RAII.

EP>1. надо ставить flush руками

EP>2. не надо ставить flush руками

EP>что более удобное


Для меня — ставить flush руками. Потому что альтернатива — неочевидный error prone код. Неявные действия хороши далеко не всегда, преобразования типов тому показательный пример.
Re[13]: 1) Родовая травма: исключеиния в деструкторах
От: Ночной Смотрящий Россия  
Дата: 20.10.12 10:54
Оценка:
Здравствуйте, landerhigh, Вы писали:

E>>Кстати, а как при таком подходе гарантировать освобождение критических ресурсов? terminate их же не освободит?


L>Каких ресурсов? Их уже твой забытый поток покромсал в салат.


А как же смартпоинтеры? Или написать на С++ программу, гарантированно не расстреливающую память, невозможно?
Re[14]: 1) Родовая травма: исключеиния в деструкторах
От: landerhigh Пират  
Дата: 20.10.12 11:50
Оценка: :)
Здравствуйте, Ночной Смотрящий, Вы писали:

L>>Каких ресурсов? Их уже твой забытый поток покромсал в салат.


НС>А как же смартпоинтеры? Или написать на С++ программу, гарантированно не расстреливающую память, невозможно?


Да, да, невозможно. Мы все умрем и только в переплавку.

Разговор вообще не об этом.
www.blinnov.com
Re[30]: 1) Родовая травма: исключеиния в деструкторах
От: Evgeny.Panasyuk Россия  
Дата: 20.10.12 12:45
Оценка:
Здравствуйте, Ku-ku, Вы писали:

EP>>я к тому, что конструкторы объектов могут кидать, в том числе и глобальных, но это не делает кидающие конструкторы неюзабельными.

KK>А я к тому, что бросающие деструкторы — error prone, причём в большей степени, чем конструкторы.

оба error-prone, деструкторы в большей степени. у меня даже caution есть.

EP>>Нет, я всего лишь показал, что "читаемость" и "простота поддержки", в этом случае являются вопросом "existance practice", и не являются каким-либо фундаментальным фейлом самого механизма.

KK>Это твоё мнение, и я с ним не согласен.

народ требует именно это
Автор: Erop
Дата: 13.10.12
, раз требует, значит для них это наверное будет читаемым

KK>ostream — потому и помойка, что берёт на себя кучу совершенно разных обязанностей, которые можно и следовало бы разделить по отдельным классам и функциям. Наличие у объекта состояний, которые не обусловлены изначально возлагаемой на него задачей, — это именно отстой. Любая функция, инкапсулирующая какое-то действие над объектом, должна или документировать в каком состоянии она оставляет флаг, или заниматься сохранением флага перед началом работы и восстановлением его значения по завершении. Первый подход создаёт геморрой для вызывающей стороны, а второй не удобен для того, кто реализует функцию. Да и вообще непонятно считать ли изменение флага логической модификацией объекта. Если какая-то операция изменяет флаг, но в остальном никак не меняет состояние объекта, то она мутирующая или нет?


Ну это же тебе нужны объекты, позволяющие отменить deferred? Я наоборот считаю, что лучше без этого геморроя. Например, я не видел жалоб на flush в fclose.

KK>Забота об исключениях — это отдельная задача. Если она не является единственной задачей объекта, то согласно SRP объект не должен браться за её решение.

EP>>Ну а к чему ты применяешь SRP в случае текущей реализации? К авто-генерированному деструктору?
KK>Да. Деструктор — часть интерфейса.
EP>>В чём существенная разница, между текущей реализацией и решением основанным на поддержке компилятора, которая делает делает одно не SRP-шным, а второе SRP-шным?
KK>Разница в возможности выполнять две операции по отдельности без применения трюков.

Основной смысл deferred в том, чтобы как раз убрать необходимость в вызове отдельного flush.
Ок, это получилось. Теперь ты задаёшь вопрос — как же их выполнить по-отдельности.. — зачем??

KK>Которая кстати не отменяет наличие общей проблемы у обоих решений.


Какой?

EP>>>>Ну так ты же тоже согласен, что каких-то фатальных недостатоков нет.

KK>>>У ручного управления ресурсами, например, их тоже нет. Но это как-то не очень мотивирует к применению такого подхода при возможности использовать более удобную альтернативу вроде RAII.
EP>>1. надо ставить flush руками
EP>>2. не надо ставить flush руками
EP>>что более удобное
KK>Для меня — ставить flush руками. Потому что альтернатива — неочевидный error prone код. Неявные действия хороши далеко не всегда, преобразования типов тому показательный пример.

Про преобразования типов не спорю — я бы предпочёл ключевое слово implicit, где explicit — это default.
Потеря контроля в любом виде, очевидно имеет как плюсы так и минусы.
Любая новая фича, позволяющая потерять частичку контроля, всегда встречается OMG дискуссиями:

http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Scott-Andrei-and-Herb-Ask-Us-Anything#time=42m42s

1. Деструктор? — OMG, неизвестно когда вызывается
2. Перегрузка функций? — OMG, неизвестно что вызывается
3. Перегрузка операторов? — OMG, неизвестно что вызывается
4. Вызов виртуальной функции? — OMG, неизвестно что вызывается
5. Шаблоны? — OMG, неизвестно какие типы используются, что вызывается, и вообще uber-wunderwaffe!
6. Исключения? — OMG, может выстрелить исподтишка!
7. auto? — OMG, неизвестно какие типы используются!!
8. lambda? — в общем случае даже тип не произнести, OMG!!!
9. Polymorphic lambda? — OMG, OMG!!!!
Re[6]: C++ и RAII
От: Трололоша  
Дата: 21.10.12 03:51
Оценка: :)
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Т.е. ты намеренно занялся провокацией, о чем ниже признался, а когда тебя за провокацию забанили пытаешься выдать себя за невинную овечку? Какая то запредельная степень нарциссизма.


Ну вот ты неподалёку совершил прямое оскорбление, назвав меня вором на ровном месте а забанили меня. Это адекватно?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Да, йа зелёный тролль!
Re[3]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ночной Смотрящий Россия  
Дата: 24.10.12 20:39
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>С RAII писать код готовый к исключениям получится проще, чем без него. Например, в C#, Java, Python — RAII нет


RAII это паттерн, а вовсе не автоматический вызов деструктора при выходе из блока, и он вполне реализуем в перечисленных тобой языках. А конкретно в C# есть еще и конструкция using, которая в основном для RAII и предназначена.
Re[4]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 25.10.12 00:29
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

EP>>С RAII писать код готовый к исключениям получится проще, чем без него. Например, в C#, Java, Python — RAII нет

НС>RAII это паттерн, а вовсе не автоматический вызов деструктора при выходе из блока, и он вполне реализуем в перечисленных тобой языках. А конкретно в C# есть еще и конструкция using, которая в основном для RAII и предназначена.

EP>С RAII писать код готовый к исключениям получится проще, чем без него. Например, в C#, Java, Python — RAII нет, но зато есть различные workarounds на эту тему.

Открой ссылку, там и using, и try-with-resouces, и with.

Вопрос, как эти костыли помогают справляться с транзитивностью глубоко-вложенных в объекты ресурсов?
Вот где-то в глубине композиции добавляется новый ресурс, и теперь все классы хоть напрямую, хоть как-то косвенно включающие этот ресурс сами становятся ресурсами. То есть теперь все их использования нужно оборачивать в эти самые юсинги.

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization#The_dispose_pattern

The compositional properties of RAII, however, differ significantly from scope bound resources in that it allows for full encapsulation of the resources behind an abstraction. With the dispose pattern for resource management, "being a resource" becomes a property that is transitive to composition. That is, any object that is composed using a resource that requires resource management effectively itself becomes a resource that requires resource management.

Re[5]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ночной Смотрящий Россия  
Дата: 25.10.12 14:36
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Открой ссылку, там и using, и try-with-resouces, и with.


Я открывал ссылку. И эта ссылка никак не доказывает то, что RAII использовать в перечисленных языках невозможно.

EP>Вот где-то в глубине композиции добавляется новый ресурс, и теперь все классы хоть напрямую, хоть как-то косвенно включающие этот ресурс сами становятся ресурсами. То есть теперь все их использования нужно оборачивать в эти самые юсинги.


А не надо ценные ресурсы заворачивать в глубину иерархии. А если ресурс не настолько ценный, что ему непременно нужно мгновенное освобождение, то GC совместо с вещами типа CriticalFinalizer вполне справляются с любыми иерархиями.
Ну и, в любом случае, подобные моменты никак не доказывают, что RAII использовать в перечисленных языках невозможно.
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: Eye of Hell Россия eyeofhell.habr.ru
Дата: 25.10.12 15:08
Оценка:
Это известная фича. В темном энтерпрайзе лечится двумя способами:

1. Если есть много легаси кода без исключений — мы просто отказываемся от исключений. Кода ненамного больше, в деструкторе ничего в принципе выкинуться не может. Хрестоматийный пример — Qt.
2. Если у нас мало легаси кода и много джедаев — делим исключения на две группы ожидаемых / неожиданных, оборачиваем все и не кидаем ожидаемые исключения в деструкторах. Если в деструкторе выкинулось неожиданное исключение (не которое неожиданное для программиста, а которое unchecked в терминологии джавы) — то падаем, как и полагается по спецификации. Примеров не знаю, но доходили слухи что так можно делать без серьезных последствий .
Re[2]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Eye of Hell Россия eyeofhell.habr.ru
Дата: 25.10.12 15:13
Оценка:
Это тоже известная проблема.

Использование настолько низкоуровневых примитивов не рекомендуется. Рекомендуется использовать высокоуровневые объекты, которые сами себе смарт поинтеры и создаются не через new, а на стеке. См. как это реализовано в Qt.
Re[6]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 25.10.12 15:35
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

EP>>Открой ссылку, там и using, и try-with-resouces, и with.

НС>Я открывал ссылку. И эта ссылка никак не доказывает то, что RAII использовать в перечисленных языках невозможно.

Если открывал ссылку которую я дал, то зачем мне рассказываешь про эти юсинги, которые по этой ссылке и находятся?

А конкретно в C# есть еще и конструкция using, которая в основном для RAII и предназначена.


EP>>Вот где-то в глубине композиции добавляется новый ресурс, и теперь все классы хоть напрямую, хоть как-то косвенно включающие этот ресурс сами становятся ресурсами. То есть теперь все их использования нужно оборачивать в эти самые юсинги.

НС>А не надо ценные ресурсы заворачивать в глубину иерархии.

В C# это нельзя нормально сделать, поэтому не надо это делать

НС>Ну и, в любом случае, подобные моменты никак не доказывают, что RAII использовать в перечисленных языках невозможно.


Вот как автор RAII описывает его:

The "resource acquisition is initialization" technique (TC++PL3 section 14.4). The basic idea is to represent a resource by a local object, so that the local object's destructor will release the resource. That way, the programmer cannot forget to release the resource.

TC++PL 14.4:

The technique for managing resources using local objects is usually referred to as ‘‘resource acquisition is initialization.’’ This is a general technique that relies on the properties of constructors and destructors and their interaction with exception handling.


И как реализовать его в перечисленных языках, опираясь на определение от автора?
Re[7]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ночной Смотрящий Россия  
Дата: 25.10.12 19:51
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Если открывал ссылку которую я дал, то зачем мне рассказываешь про эти юсинги, которые по этой ссылке и находятся?


Затем что ты сам то ли ссылку не читал, то ли не понял что там написано.

EP>В C# это нельзя нормально сделать, поэтому не надо это делать


Почему нельзя? Можно.

EP>И как реализовать его в перечисленных языках, опираясь на определение от автора?


Заменяем деструктор на dispose и получаем примерно тоже самое.
Re[8]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Jack128  
Дата: 25.10.12 20:49
Оценка: +3
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Заменяем деструктор на dispose и получаем примерно тоже самое.


а толку то? В чем ценность раии в плюсах? в том, что деструктор автоматом вызывается для объектов на стеке. в чем _относительная_ ценность IDisposable в шарпе? в том, что есть синтаксический сахар для вызова IDiposable.Dispose(). В чем полная бесполезность RAII для какого нить языка где нет пользовательского метода, который компилятор бы детерминировано вызывал? В том, что "деструктор" мне все равно ручкамивызвать нужно!
Re[8]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 25.10.12 21:58
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

EP>>Если открывал ссылку которую я дал, то зачем мне рассказываешь про эти юсинги, которые по этой ссылке и находятся?

НС>Затем что ты сам то ли ссылку не читал, то ли не понял что там написано.

Я же написал "workarounds". они и в подмётки не годятся RAII.

EP>>В C# это нельзя нормально сделать, поэтому не надо это делать

НС>Почему нельзя? Можно.

Что можно? Справится с транзитивностью глубоко-вложенных в объекты ресурсов?

EP>>И как реализовать его в перечисленных языках, опираясь на определение от автора?

НС>Заменяем деструктор на dispose и получаем примерно тоже самое.

Неужели?
http://msdn.microsoft.com/ru-ru/library/system.idisposable.dispose.aspx

When implementing this method, ensure that all held resources are freed by propagating the call through the containment hierarchy. For example, if an object A allocates an object B, and object B allocates an object C, then A's Dispose implementation must call Dispose on B, which must in turn call Dispose on C. An object must also call the Dispose method of its base class if the base class implements IDisposable.


То есть рукопашка по вызову dispose по всей иерархии наследования, по всей иерархии композиции, рукопашное проставление using, это примерно тоже самое?
С тем же успехом можно сказать что ручной вызов fopen/fclose это примерно тоже самое

В то время как деструкторы вызываются автоматически, и для вложенных объектов, и для родителей, для глобальных, и для стековых переменных.
Re[2]: 1) Родовая травма: исключеиния в деструкторах
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.12 08:51
Оценка: :))
Здравствуйте, Erop, Вы писали:

E>Ну это ещё пол-беды. Главная родовая травма деструкторов С++, это то, что их нельзя тормознуть.

E>То есть, если пользователь в алерт-боксе, нажмёт не "Сохранить" или "не сохранять", а "отменить", то мы вообще не сможем никак этого поддержать в нашей прекрасной RAII-архитектуре

Опаньки ! Сиплюсник оказывается сам не знает что такое RAII

Вобщем тред специльно как по заказу — демонстрирует сколько разных мнений бывает у сиплюсников по поводу простецкой феньки.
Re[9]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.12 15:19
Оценка: +1 -3
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>В то время как деструкторы вызываются автоматически, и для вложенных объектов, и для родителей, для глобальных, и для стековых переменных.


Надо определиться, какой код ты собираешься вложить в деструктор. Если освобождение памяти, то в дотнете это не нужно.
А если в деструкторе ты хочешь чего то разрушать или отпускать, то это уже гнусь, т.к. деструктор внезапно может кинуть исключение.
Само по себе это не создаёт проблемы, а вотреализация исключений в С++ скажем не благоприятствует такой тактике.

Что интересно, если ты отказываешься от кода который может кинуть исключение в деструкторе, то приходится вводить метод навроде Dispose. Опаньки !

то есть деструкторы годятся для
1. мелких фенек привязаных к скопу переменной, типа сброс флажка
2. управления ресурсами когда этот код никогда не может кинуть исключение
3. управления памятью

Итого, преимуществ перед менеджед IDisposable около нуля.
Re[10]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 26.10.12 19:25
Оценка: +1
Здравствуйте, Ikemefula, Вы писали:

EP>>В то время как деструкторы вызываются автоматически, и для вложенных объектов, и для родителей, для глобальных, и для стековых переменных.

I>Надо определиться, какой код ты собираешься вложить в деструктор. Если освобождение памяти, то в дотнете это не нужно.
I>А если в деструкторе ты хочешь чего то разрушать или отпускать, то это уже гнусь, т.к. деструктор внезапно может кинуть исключение.

Herb Sutter: "letting go of a resource" must never fail.
Большинство API не кидает исключений на RELEASE Примеры: память, файлы, lock'и и т.п.

I>Само по себе это не создаёт проблемы, а вотреализация исключений в С++ скажем не благоприятствует такой тактике.


В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.
Я предпочту std::terminate, чем проглатывание первого исключения

I>Что интересно, если ты отказываешься от кода который может кинуть исключение в деструкторе, то приходится вводить метод навроде Dispose. Опаньки !


1. Ни Dispose, а Deferred. Release всё-равно находится в деструкторе, так как по сути своей не кидает
2. Если тебе нужен Deferred в C# — у тебя будут рукопашные и Deferred и Dispose
3. Я уже в этой теме показывал
Автор: Evgeny.Panasyuk
Дата: 13.10.12
, вариант автоматизации вызова Deferred в C++

I>то есть деструкторы годятся для

I>1. мелких фенек привязаных к скопу переменной, типа сброс флажка

Ты про ScopeGuard?

I>2. управления ресурсами когда этот код никогда не может кинуть исключение


Когда RELEASE не может кинуть. Да он в большинстве случаев не кидает.
Кстати, можешь привести пример освобождение ресурсов, которое фейлится?

I>3. управления памятью


I>Итого, преимуществ перед менеджед IDisposable около нуля.


Ты как-то ловко ушёл от вопроса об транзитивности
Re[11]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.12 21:12
Оценка: -1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

I>>Надо определиться, какой код ты собираешься вложить в деструктор. Если освобождение памяти, то в дотнете это не нужно.

I>>А если в деструкторе ты хочешь чего то разрушать или отпускать, то это уже гнусь, т.к. деструктор внезапно может кинуть исключение.

EP>Herb Sutter: "letting go of a resource" must never fail.

EP>Большинство API не кидает исключений на RELEASE Примеры: память, файлы, lock'и и т.п.

Большинство, но не всё. И уж точно доп. логику в деструктор не всунешь, то есть, те же самые приседания.
Если нужна такая логика — надо заводить специальный метод.

EP>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.

EP>Я предпочту std::terminate, чем проглатывание первого исключения

Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.

I>>Что интересно, если ты отказываешься от кода который может кинуть исключение в деструкторе, то приходится вводить метод навроде Dispose. Опаньки !


EP>1. Ни Dispose, а Deferred. Release всё-равно находится в деструкторе, так как по сути своей не кидает

EP>2. Если тебе нужен Deferred в C# — у тебя будут рукопашные и Deferred и Dispose
EP>3. Я уже в этой теме показывал
Автор: Evgeny.Panasyuk
Дата: 13.10.12
, вариант автоматизации вызова Deferred в C++


"Currently library is implemented on top of platform-specific implementation of uncaught_exception_count function"

Вобще ты сэмулировал диспоз

I>>2. управления ресурсами когда этот код никогда не может кинуть исключение


EP>Когда RELEASE не может кинуть. Да он в большинстве случаев не кидает.


В чьем коде, в твоем или в том в котором мне приходилось ковыряться ?

I>>Итого, преимуществ перед менеджед IDisposable около нуля.


EP>Ты как-то ловко ушёл от вопроса об транзитивности


Покажи пример гигансткой вложенности ресурсов.
Re[12]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 28.10.12 22:17
Оценка:
Здравствуйте, Ikemefula, Вы писали:

EP>>Herb Sutter: "letting go of a resource" must never fail.

EP>>Большинство API не кидает исключений на RELEASE Примеры: память, файлы, lock'и и т.п.
I>Большинство, но не всё. И уж точно доп. логику в деструктор не всунешь, то есть, те же самые приседания.
I>Если нужна такая логика — надо заводить специальный метод.

точно также как и в C#

EP>>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.

EP>>Я предпочту std::terminate, чем проглатывание первого исключения
I>Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.

как именно?

EP>>3. Я уже в этой теме показывал
Автор: Evgeny.Panasyuk
Дата: 13.10.12
, вариант автоматизации вызова Deferred в C++

I>"Currently library is implemented on top of platform-specific implementation of uncaught_exception_count function"
I>Вобще ты сэмулировал диспоз

А при чём тут dispose? Освобождение ресурсов так и остаётся в деструкторе — которые автоматом вызываются.
Эта либа помогает автоматизировать вызов deferred.
В то время как в C# нет ни автоматических вызовов dispose ни deferred

I>>>2. управления ресурсами когда этот код никогда не может кинуть исключение

EP>>Когда RELEASE не может кинуть. Да он в большинстве случаев не кидает.
I>В чьем коде, в твоем или в том в котором мне приходилось ковыряться ?

В том, который я видел.
С каким кодом ты работал, я не знаю, по всей видимости там были кидающие деструкторы. Точно также как есть код с кидающим Dispose, который нужно обкостыливать

I>>>Итого, преимуществ перед менеджед IDisposable около нуля.

EP>>Ты как-то ловко ушёл от вопроса об транзитивности
I>Покажи пример гигансткой вложенности ресурсов.

А дело даже ни в том, что ресурсов может быть много — дело в том, что добавление одного малюсенького ресурса внизу иерархии композиции, превращает всех прямых и косвенных пользователей этого класса в ресурсы(если они ими уже не были) — что означает рукопашное распространение вызовов dispose, и проставление using'ов.
Будет ещё веселей если продукт это не конечное приложение, а библиотека/компонент.
Re[13]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 29.10.12 08:46
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

I>>Большинство, но не всё. И уж точно доп. логику в деструктор не всунешь, то есть, те же самые приседания.

I>>Если нужна такая логика — надо заводить специальный метод.

EP>точно также как и в C#


ЧТД.

EP>>>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.

EP>>>Я предпочту std::terminate, чем проглатывание первого исключения
I>>Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.

EP>как именно?


Завести специальную функцию.

EP>А при чём тут dispose? Освобождение ресурсов так и остаётся в деструкторе — которые автоматом вызываются.

EP>Эта либа помогает автоматизировать вызов deferred.
EP>В то время как в C# нет ни автоматических вызовов dispose ни deferred

Нет и не нужно. Решать вопрос нужно дизайном, а не распихивать по уголкам которые дергаются неявно.

I>>Покажи пример гигансткой вложенности ресурсов.


EP>А дело даже ни в том, что ресурсов может быть много — дело в том, что добавление одного малюсенького ресурса внизу иерархии композиции, превращает всех прямых и косвенных пользователей этого класса в ресурсы(если они ими уже не были) — что означает рукопашное распространение вызовов dispose, и проставление using'ов.

EP>Будет ещё веселей если продукт это не конечное приложение, а библиотека/компонент.

Покажи пример.
Re[14]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 30.10.12 02:15
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Здравствуйте, Evgeny.Panasyuk, Вы писали:


I>>>Большинство, но не всё. И уж точно доп. логику в деструктор не всунешь, то есть, те же самые приседания.

I>>>Если нужна такая логика — надо заводить специальный метод.
EP>>точно также как и в C#
I>ЧТД.

Я не знаю, что там ТД — но в C++ это не единственное место где есть какое-то сходство с C#, например и там и там есть while

I>>>>>А если в деструкторе ты хочешь чего то разрушать или отпускать, то это уже гнусь, т.к. деструктор внезапно может кинуть исключение.

I>>>>>Само по себе это не создаёт проблемы, а вотреализация исключений в С++ скажем не благоприятствует такой тактике.
EP>>>>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.
EP>>>>Я предпочту std::terminate, чем проглатывание первого исключения
I>>>Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.
EP>>как именно?
I>Завести специальную функцию.

То есть в C# отдельная функция используемая для того, чтобы обойти проблему кидающего Dispose — это нормально, а в C++

деструкторы и исключения с++ ничем не пофиксишь.

WTF?

EP>>А при чём тут dispose? Освобождение ресурсов так и остаётся в деструкторе — которые автоматом вызываются.

EP>>Эта либа помогает автоматизировать вызов deferred.
EP>>В то время как в C# нет ни автоматических вызовов dispose ни deferred
I>Нет и не нужно. Решать вопрос нужно дизайном, а не распихивать по уголкам которые дергаются неявно.

Дизайн?
А например finilize не напрягает?
Может лучше сразу, на C? — там неявных мест поменьше будет

I>>>Покажи пример гигансткой вложенности ресурсов.

EP>>А дело даже ни в том, что ресурсов может быть много — дело в том, что добавление одного малюсенького ресурса внизу иерархии композиции, превращает всех прямых и косвенных пользователей этого класса в ресурсы(если они ими уже не были) — что означает рукопашное распространение вызовов dispose, и проставление using'ов.
EP>>Будет ещё веселей если продукт это не конечное приложение, а библиотека/компонент.
I>Покажи пример.

Пример:
1. C++ vs C#
А теперь раскомментируем одну строчку:
2. C++ vs С#
Транзитивность в действии такой вот он — дизайн
Re[15]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.12 11:53
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

I>>>>Большинство, но не всё. И уж точно доп. логику в деструктор не всунешь, то есть, те же самые приседания.

I>>>>Если нужна такая логика — надо заводить специальный метод.
EP>>>точно также как и в C#
I>>ЧТД.

EP>Я не знаю, что там ТД — но в C++ это не единственное место где есть какое-то сходство с C#, например и там и там есть while


Наверное выделеную строку не ты писал. Если так, то нет никаких претензий.

EP>То есть в C# отдельная функция используемая для того, чтобы обойти проблему кидающего Dispose — это нормально, а в C++

EP>

EP>деструкторы и исключения с++ ничем не пофиксишь.

EP>WTF?

Правильно. Сравни свою либу c написанием функции (от силы в десяток строчек за минуты 2 с перерывом на RSDN) .

I>>Нет и не нужно. Решать вопрос нужно дизайном, а не распихивать по уголкам которые дергаются неявно.


EP>Дизайн?

EP>А например finilize не напрягает?

Абсолютно не напрягает. Что там вообще должно напрягать ? Открой уже ужасы менеджед разработки

EP>Пример:

EP>1. C++ vs C#
EP>А теперь раскомментируем одну строчку:
EP>2. C++ vs С#
EP>Транзитивность в действии такой вот он — дизайн

Это синтетика. С таким же успехом я покажу тебе код, который бросает явно исключение в деструкторе и программа будет складываться в этот момент.
Re[16]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 31.10.12 17:42
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>>>>>Большинство, но не всё. И уж точно доп. логику в деструктор не всунешь, то есть, те же самые приседания.

I>>>>>Если нужна такая логика — надо заводить специальный метод.
I>EP>>>точно также как и в C#
I>>>ЧТД.
EP>>Я не знаю, что там ТД — но в C++ это не единственное место где есть какое-то сходство с C#, например и там и там есть while
I>Наверное выделеную строку не ты писал. Если так, то нет никаких претензий.

А что не так? Если нужен кидающий deferred метод он будет и там и там.
В то время как nothrow Release в C++ получается намного лучше

EP>>То есть в C# отдельная функция используемая для того, чтобы обойти проблему кидающего Dispose — это нормально, а в C++

EP>>

EP>>деструкторы и исключения с++ ничем не пофиксишь.

EP>>WTF?
I>Правильно. Сравни свою либу c написанием функции (от силы в десяток строчек за минуты 2 с перерывом на RSDN) .

А причём тут моя либа в этом контексте?
Ты говоришь, что исключения и деструкторы никак не пофиксить, а Dispose — где точно такая же ситуация — предлагаешь обходить с помощью отдельной функции

I>>>Нет и не нужно. Решать вопрос нужно дизайном, а не распихивать по уголкам которые дергаются неявно.

EP>>Дизайн?
EP>>А например finilize не напрягает?
I>Абсолютно не напрягает. Что там вообще должно напрягать ? Открой уже ужасы менеджед разработки

Ну это ты же подчёркнутое писал? То есть если в случае RAII, раз его нет в C#, то это что-то распиханное по уголкам и дёргающееся неявно — а в случае finilize, который есть, всё пучком?

EP>>Пример:

EP>>1. C++ vs C#
EP>>А теперь раскомментируем одну строчку:
EP>>2. C++ vs С#
EP>>Транзитивность в действии такой вот он — дизайн
I>Это синтетика.

Добавление private члена в один из классов является синтетикой?
А, ну да, раз это не разруливается нормально в C# — то это синтетика

I>С таким же успехом я покажу тебе код, который бросает явно исключение в деструкторе и программа будет складываться в этот момент.


Я тебе уже показал код с кидающим Dispose
Автор: Evgeny.Panasyuk
Дата: 26.10.12
, и я уже писал, что std::terminate в этом случае лучше чем проглатывание первого исключения
Re[11]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Erop Россия  
Дата: 04.11.12 08:44
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Кстати, можешь привести пример освобождение ресурсов, которое фейлится?


Удаление временного файла...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[17]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 04.11.12 09:01
Оценка: -1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

I>>Наверное выделеную строку не ты писал. Если так, то нет никаких претензий.


EP>А что не так? Если нужен кидающий deferred метод он будет и там и там.

EP>В то время как nothrow Release в C++ получается намного лучше

nothrow как то непопулярен

EP>А причём тут моя либа в этом контексте?

EP>Ты говоришь, что исключения и деструкторы никак не пофиксить, а Dispose — где точно такая же ситуация — предлагаешь обходить с помощью отдельной функции

При том, что это твоя либа это хрупкий код, а у меня просто одна функция в 10 строчек кода.

EP>Ну это ты же подчёркнутое писал? То есть если в случае RAII, раз его нет в C#, то это что-то распиханное по уголкам и дёргающееся неявно — а в случае finilize, который есть, всё пучком?


Конечно. finalize это просто страховка. Это ни разу не аналог деструктора. Управление ресурсами должно всегда и везде делаться явно ! Вот когда можно будет это полностью переложить на компилятор,тогда можно будет рассуждать про деструкторы.

EP>Добавление private члена в один из классов является синтетикой?


Именно. Приведи задачу связаную с ресурсами, а порожняк гнать не нужно.

I>>С таким же успехом я покажу тебе код, который бросает явно исключение в деструкторе и программа будет складываться в этот момент.


EP>Я тебе уже показал код с кидающим Dispose
Автор: Evgeny.Panasyuk
Дата: 26.10.12
, и я уже писал, что std::terminate в этом случае лучше чем проглатывание первого исключения


Ты еще десять раз напиши, может сам за это время поймешь.
Re[18]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 05.11.12 11:15
Оценка: -1
Здравствуйте, Ikemefula, Вы писали:

I>>>Наверное выделеную строку не ты писал. Если так, то нет никаких претензий.

EP>>А что не так? Если нужен кидающий deferred метод он будет и там и там.
EP>>В то время как nothrow Release в C++ получается намного лучше
I>nothrow как то непопулярен

От тебя я так и не увидел примера кидающего release
Автор: Evgeny.Panasyuk
Дата: 26.10.12
:

Кстати, можешь привести пример освобождение ресурсов, которое фейлится?



EP>>А причём тут моя либа в этом контексте?

EP>>Ты говоришь, что исключения и деструкторы никак не пофиксить, а Dispose — где точно такая же ситуация — предлагаешь обходить с помощью отдельной функции
I>При том, что это твоя либа это хрупкий код, а у меня просто одна функция в 10 строчек кода.

Ещё раз, при чём тут моя либа? Забудь про неё.
Насколько я понял, кидающий Dispose ты предлагаешь фиксить отдельным методом. А почему-то кидающие деструкторы у тебя при этом

ничем не пофиксишь



EP>>Ну это ты же подчёркнутое писал? То есть если в случае RAII, раз его нет в C#, то это что-то распиханное по уголкам и дёргающееся неявно — а в случае finilize, который есть, всё пучком?

I>Конечно. finalize это просто страховка. Это ни разу не аналог деструктора.

А я нигде не говорил что это аналог деструктора

I>Управление ресурсами должно всегда и везде делаться явно !


О как. А память это ресурс?

EP>>Добавление private члена в один из классов является синтетикой?

I>Именно. Приведи задачу связаную с ресурсами, а порожняк гнать не нужно.

Я не увидел здесь от тебя ни примера кидающего release, ни примера как фиксится кидающий Dispose (причём так, как нельзя пофиксить кидающий деструктор), вообще ни строчки кода — только "порожняк"

I>>>С таким же успехом я покажу тебе код, который бросает явно исключение в деструкторе и программа будет складываться в этот момент.


EP>>Я тебе уже показал код с кидающим Dispose
Автор: Evgeny.Panasyuk
Дата: 26.10.12
, и я уже писал, что std::terminate в этом случае лучше чем проглатывание первого исключения

I>Ты еще десять раз напиши, может сам за это время поймешь.

ты вроде как ничего по делу не возразил
Автор: Ikemefula
Дата: 29.10.12
:

EP>>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.
EP>>Я предпочту std::terminate, чем проглатывание первого исключения
I>Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.

только "порожняк"
Re[19]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 05.11.12 14:03
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>От тебя я так и не увидел примера кидающего release
Автор: Evgeny.Panasyuk
Дата: 26.10.12
:

EP>

EP>Кстати, можешь привести пример освобождение ресурсов, которое фейлится?

EP>

Тебе принципиально что бы я привел пример или хватит тех что уже были перечислены в треде ?

EP>>>А причём тут моя либа в этом контексте?

EP>>>Ты говоришь, что исключения и деструкторы никак не пофиксить, а Dispose — где точно такая же ситуация — предлагаешь обходить с помощью отдельной функции
I>>При том, что это твоя либа это хрупкий код, а у меня просто одна функция в 10 строчек кода.

EP>Ещё раз, при чём тут моя либа? Забудь про неё.


Ты предлагаешь сравнивать конкретное решение с теоретически идеальным ? Ай маладец.

EP>Насколько я понял, кидающий Dispose ты предлагаешь фиксить отдельным методом. А почему-то кидающие деструкторы у тебя при этом

EP>

EP>ничем не пофиксишь

EP>

Проблемы мягко говоря разной сложности. Приведи прямо здесь __весь__ необходимый код что бы твой пример заработал и тебе все станет ясно.

EP>>>Ну это ты же подчёркнутое писал? То есть если в случае RAII, раз его нет в C#, то это что-то распиханное по уголкам и дёргающееся неявно — а в случае finilize, который есть, всё пучком?

I>>Конечно. finalize это просто страховка. Это ни разу не аналог деструктора.

EP>А я нигде не говорил что это аналог деструктора


Тогда зачем ты сравниваешь разные вещи ? Исключения в диспозе и финалайзере ни разу не фатальны и вобщем даже не критичны, часто нужен просто сам факт, было исключение или нет и большей частью их можно игнорировать. Сюрприз !!!

I>>Управление ресурсами должно всегда и везде делаться явно !


EP>О как. А память это ресурс?


В дотнете это не ресурс, в с++ решай сам.

EP>Я не увидел здесь от тебя ни примера кидающего release,


Тебе принципиально что бы я привел пример или хватит тех что уже были перечислены в треде ?

>ни примера как фиксится кидающий Dispose (причём так, как нельзя пофиксить кидающий деструктор), вообще ни строчки кода — только "порожняк"


Во первых, исключение во время диспоза не всегда важно, главное сам факт, в этом случае можно просто вот так
            using (var r1 = new Resource()) { 
            using (var r2 = new Resource()) { 
            using (var r3 = new Resource())
            {
                    
            }}}


Во вторых, если исключение таки важно и надо освободить все ресурсы и во что бы то ни стало нужно универсальное решение, то
                var _ = new{x = new Resource("1"),y = new Resource("2"),z = new Resource("3") };
                try
                {
                    _.x.Operation();
                    _.y.Operation();
                    _.z.Operation();
                }
                finally
                {
                    Bundle.TryDisposeAll(_);
                }

Или такое
                Bundle.Using(new { x = new Resource("1"), y = new Resource("2"), z = new Resource("3")}, _=>
                {
                    _.x.Operation();
                    _.y.Operation();
                    _.z.Operation();
                });


обработка исключений
            try
            {
              ...
            }
            catch (ResourceBundleException ex)
            {
                foreach (var e in ex)
                {
                    Trace.WriteLine(e.Message);
                }
            }


и сам код
    public class ResourceBundleException : Exception, IEnumerable<Exception>
    {
        private List<Exception> _exceptions = new List<Exception>();
        public ResourceBundleException(Exception ex) { _exceptions.Add(ex); }
        public IEnumerator<Exception> GetEnumerator() { return _exceptions.GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
        internal void Add(Exception ex) { _exceptions.Add(ex); }
    }

    static class Bundle
    {
        public static void Using<T>(T o, Action<T> action )
        {
            try { action(o); } finally { TryDisposeAll(o);}
        }

        public static void TryDisposeAll(object o)
        {
            ResourceBundleException bundleException = null;

            foreach (var item in o.GetType().GetProperties())
            {
                var value = item.GetMethod.Invoke(o, null) as IDisposable;

                if (value == null)
                    continue;
                try
                {
                    value.Dispose();
                }
                catch (Exception ex)
                {
                    if (bundleException == null)
                        bundleException = new ResourceBundleException(ex);
                    else
                        bundleException.Add(ex);
                }
            }
            if (bundleException != null)
                throw bundleException;
        }
    }


EP>ты вроде как ничего по делу не возразил
Автор: Ikemefula
Дата: 29.10.12
:

EP>

EP>>>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.
EP>>>Я предпочту std::terminate, чем проглатывание первого исключения
I>>Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.

EP>только "порожняк"

Осталось еще 9. Продолжай.
Re[20]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 06.11.12 11:09
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Во вторых, если исключение таки важно и надо освободить все ресурсы и во что бы то ни стало нужно универсальное решение, то

I>
I>                var _ = new{x = new Resource("1"),y = new Resource("2"),z = new Resource("3") };
I>                try
I>                {
I>                    _.x.Operation();
I>                    _.y.Operation();
I>                    _.z.Operation();
I>                }
I>                finally
I>                {
I>                    Bundle.TryDisposeAll(_);
I>                }
I>


Шота на меня вчера затмение нашло. Вот
                using(var _ = Bundle.Create(new { x = new Resource("1"), y = new Resource("2"), z = new Resource("3")}))
                {
                    _.Ctx.x.Operation();
                    _.Ctx.y.Operation();
                    _.Ctx.z.Operation();
                }


И добавляется функция Create + класс
        public static Bundle<T> Create<T>(T o) where T : class 
        {
            return new Bundle<T>(o);
        }

    public class Bundle<T> : IDisposable where T : class 
    {
        private T _t;

        public T Ctx { get { return _t; } }

        public Bundle(T t)
        {
            _t = t;
        }

        public void Dispose()
        {
            if (_t == null)
                return;
            Bundle.TryDisposeAll(_t);
            _t = null;
            GC.SuppressFinalize(this);
        }

        ~Bundle()
        {
            Dispose();
        }
    }
Re[20]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 07.11.12 01:28
Оценка:
Здравствуйте, Ikemefula, Вы писали:

EP>>От тебя я так и не увидел примера кидающего release
Автор: Evgeny.Panasyuk
Дата: 26.10.12
:

EP>>

EP>>Кстати, можешь привести пример освобождение ресурсов, которое фейлится?

EP>>
I>Тебе принципиально что бы я привел пример или хватит тех что уже были перечислены в треде ?

Ну вот судя по http://www.rsdn.ru/forum/flame.comp/4943382.1
Автор: Ikemefula
Дата: 26.10.12

то есть деструкторы годятся для
1. мелких фенек привязаных к скопу переменной, типа сброс флажка
2. управления ресурсами когда этот код никогда не может кинуть исключение
3. управления памятью
Итого, преимуществ перед менеджед IDisposable около нуля.

Ты предполагаешь, что большая часть ресурсов, может фейлится на release.
Я хочу увидеть примеры таких ресурсов — раз их большинство, то у тебя должно быть хотя бы несколько примеров, а не просто отсылка в тред.

EP>>>>А причём тут моя либа в этом контексте?

EP>>>>Ты говоришь, что исключения и деструкторы никак не пофиксить, а Dispose — где точно такая же ситуация — предлагаешь обходить с помощью отдельной функции
I>>>При том, что это твоя либа это хрупкий код, а у меня просто одна функция в 10 строчек кода.
EP>>Ещё раз, при чём тут моя либа? Забудь про неё.
I>Ты предлагаешь сравнивать конкретное решение с теоретически идеальным ? Ай маладец.

До этого сообщения — ты не показывал ни одного "конкретного" решения — "Ай маладец"

EP>>Насколько я понял, кидающий Dispose ты предлагаешь фиксить отдельным методом. А почему-то кидающие деструкторы у тебя при этом

EP>>

EP>>ничем не пофиксишь

EP>>
I>Проблемы мягко говоря разной сложности. Приведи прямо здесь __весь__ необходимый код что бы твой пример заработал и тебе все станет ясно.

какой конкретно "мой" пример?

EP>>>>Ну это ты же подчёркнутое писал? То есть если в случае RAII, раз его нет в C#, то это что-то распиханное по уголкам и дёргающееся неявно — а в случае finilize, который есть, всё пучком?

I>>>Конечно. finalize это просто страховка. Это ни разу не аналог деструктора.
EP>>А я нигде не говорил что это аналог деструктора
I>Тогда зачем ты сравниваешь разные вещи?

я их сравниваю в контексте твоего высказывания:

что-то распиханное по уголкам и дёргающееся неявно



I>Исключения в диспозе и финалайзере ни разу не фатальны и вобщем даже не критичны, часто нужен просто сам факт, было исключение или нет и большей частью их можно игнорировать. Сюрприз !!!


I>>>Управление ресурсами должно всегда и везде делаться явно !

EP>>О как. А память это ресурс?
I>В дотнете это не ресурс, в с++ решай сам.

А ну ок — раз среда управляет одним ресурсом неявно, то это уже не ресурс. А если не помогает управлять каким-либо другим ресурсом — то уже вдруг:

Управление ресурсами должно всегда и везде делаться явно !

железно!

>>ни примера как фиксится кидающий Dispose (причём так, как нельзя пофиксить кидающий деструктор), вообще ни строчки кода — только "порожняк"

I>Во первых, исключение во время диспоза не всегда важно, главное сам факт, в этом случае можно просто вот так

Не, я всё понимаю, в казуальных свистелках проглатывание исключения допустимо.
Но почему, блин, проглатывание первых, и выпускание последнего, а не наоборот? Чаще ведь важно именно первое, оно может и является причиной остальных

I>
I>            using (var r1 = new Resource()) { 
I>            using (var r2 = new Resource()) { 
I>            using (var r3 = new Resource())
I>            {
                    
I>            }}}
I>


А зачем несколько юсингов? Почему не один?

I>Во вторых, если исключение таки важно и надо освободить все ресурсы и во что бы то ни стало нужно универсальное решение, то

...
I>Или такое
...
I>обработка исключений
...
I>и сам код
...

Только это не фикс Dispose+using, даже с учётом версии из следующего сообщения — это фактически отказ от них в пользу библиотечной логики
И для надёжности тут желательно вообще вместо IDisposable, использовать кастомный интерфейс — большинство кода ведь не рассчитано на кидающий Dispose. И люди видя IDisposable — могут не правильно понять

Подобную библиотечную логику и в C++ можно реализовать, и даже в C (естественно с некоторыми ограничениями)
Например так:
struct Flushable
{
    string str;
    void use()
    {
        cout << str;
    }
    void flush()
    {
        throw runtime_error("!");
    }
};

int main()
{
    try
    {
        Flushable w{"w"},t{"t"},f{"f"};
        flush_on_exit(tie(w,t,f),[&]()
        {
            w.use();
            t.use();
            f.use();
            throw runtime_error("?");
        });
    }
    catch(const exception &exp)
    {
        print_exception(exp);
    }
}

Или если вляпаться в кучу, то можно так:
struct Flushable: IFlushable
{
    string str;
    Flushable(string str_):str(str_){}
    void use()
    {
        cout << str;
    }
    void flush()
    {
        throw runtime_error("!");
    }
    ~Flushable()
    {
        cout << "~";
    }
};

int main()
{
    try
    {
        using_{[]()
        {
            auto w=new(garbage) Flushable{"w"};
            auto t=new(garbage) Flushable{"t"};
            auto f=new(garbage) Flushable{"f"};
            w->use();
            t->use();
            f->use();
            using_{[]()
            {
                auto n=new(garbage) Flushable{"?"};
                n->use();
            }};
        }};
    }
    catch(const exception &exp)
    {
        print_exception(exp);
    }
}

или даже так
struct Flushable: FlushableBase
{
    string str;
    Flushable(string str_):str(str_){}
    void use()
    {
        cout << str;
    }
    void flush()
    {
        throw runtime_error("!");
    }
    ~Flushable()
    {
        cout << "~";
    }
};

int main()
{
    try
    {
        Flushable w{"w"};
        Flushable t{"t"};
        Flushable f{"f"};
        using_{[&]()
        {
            w.use();
            t.use();
            f.use();
            Flushable n{"?"};
            using_{[&]()
            {
                n.use();
            }};
        }};
    }
    catch(const exception &exp)
    {
        print_exception(exp);
    }
}



EP>>ты вроде как ничего по делу не возразил
Автор: Ikemefula
Дата: 29.10.12
:

EP>>

EP>>>>В C++ реализация исключений из деструкторов, лучше чем в C# из Dispose.
EP>>>>Я предпочту std::terminate, чем проглатывание первого исключения
I>>>Известная проблема. Фиксится около 5 минут. а вот деструкторы и исключения с++ ничем не пофиксишь.

EP>>только "порожняк"
I>Осталось еще 9. Продолжай.

можешь все разом израсходовать
Re[21]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: -MyXa- Россия  
Дата: 07.11.12 04:32
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>                using(var _ = Bundle.Create(new { x = new Resource("1"), y = new Resource("2"), z = new Resource("3")}))


В какой же, интересно знать, версии C#-а компилируется выражение Bundle.Create(new...), при том, что "public class Bundle<T>"?

I>И добавляется функция Create + класс

I>
I>        public void Dispose()
I>        {
I>            if (_t == null)
I>                return;
I>            Bundle.TryDisposeAll(_t);
I>            _t = null;
I>            GC.SuppressFinalize(this);
I>        }

I>        ~Bundle()
I>        {
I>            Dispose();
I>        }
I>    }
I>


Наверное, в той же версии, в которой компилируется "Bundle.TryDisposeAll"... там точно <T> не нужно?

А ещё мне не понятна вот эта идея —

I>обработка исключений

I>[ccode]
I> try
I> {
I> ...
I> }
I> catch (ResourceBundleException ex)
I> {
I> foreach (var e in ex)
I> {
I> Trace.WriteLine(e.Message);
I> }
I> }

Это, вообще, куда?
Если не поможет, будем действовать током... 600 Вольт (C)
Re[22]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Jack128  
Дата: 07.11.12 05:47
Оценка:
Здравствуйте, -MyXa-, Вы писали:

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


MX>
I>>                using(var _ = Bundle.Create(new { x = new Resource("1"), y = new Resource("2"), z = new Resource("3")}))
MX>


MX>В какой же, интересно знать, версии C#-а компилируется выражение Bundle.Create(new...), при том, что "public class Bundle<T>"?


I>>И добавляется функция Create + класс

I>>
I>>        public void Dispose()
I>>        {
I>>            if (_t == null)
I>>                return;
I>>            Bundle.TryDisposeAll(_t);
I>>            _t = null;
I>>            GC.SuppressFinalize(this);
I>>        }

I>>        ~Bundle()
I>>        {
I>>            Dispose();
I>>        }
I>>    }
I>>


MX>Наверное, в той же версии, в которой компилируется "Bundle.TryDisposeAll"... там точно <T> не нужно?


class Bundle 
{
  public static Bundle<T> Create<T>(T o) { return new Bundle<T>(o); }
  public void TryDisposeAll(object o) { ... }
}


MX>А ещё мне не понятна вот эта идея —


I>>обработка исключений

I>>[ccode]
I>> try
I>> {
I>> ...
I>> }
I>> catch (ResourceBundleException ex)
I>> {
I>> foreach (var e in ex)
I>> {
I>> Trace.WriteLine(e.Message);
I>> }
I>> }

MX>Это, вообще, куда?


using (var a = new Resource())
using (var b = new Resource())
{}

если в b.Dispose() возникнет исключение, а потом и в a.Dispose() то исключение из b — мы потеряем. подход с Bundle — позволяет сохранить это исключение и обработать его каким нить образом.
Re[21]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Jack128  
Дата: 07.11.12 05:51
Оценка: 2 (1)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

I>>
I>>            using (var r1 = new Resource()) { 
I>>            using (var r2 = new Resource()) { 
I>>            using (var r3 = new Resource())
I>>            {
                    
I>>            }}}
I>>


EP>А зачем несколько юсингов? Почему не один?


твой код требует, чтобы тип всех ресурсов был одинаков. а если у мя первый ресурс файл, а второй — сетевое подключение — то швах.
можно так писать:

using (var r1 = new Resource()) // нет лишних фигурных скобок.
using (var r2 = new Resource())
using (var r3 = new Resource())
{
                    
}

студия такое форматирование из коробки поддерживает.
Re[22]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Evgeny.Panasyuk Россия  
Дата: 07.11.12 06:56
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, Evgeny.Panasyuk, Вы писали:


I>>>
I>>>            using (var r1 = new Resource()) { 
I>>>            using (var r2 = new Resource()) { 
I>>>            using (var r3 = new Resource())
I>>>            {
                    
I>>>            }}}
I>>>


EP>>А зачем несколько юсингов? Почему не один?

J>твой код требует, чтобы тип всех ресурсов был одинаков. а если у мя первый ресурс файл, а второй — сетевое подключение — то швах.

Теперь ясно. (но в примере выше ресурсы одинаковые..)
Получается в Java этот костылик немного мощней:

try
(
    java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
    java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
)
{
    // ...
}

И getSuppressed есть.
ИМХО — chaining исключений — самое бескомпромиссное решение. Да и правильно сделали, что выпускают именно первое исключение —
Re[22]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 07.11.12 09:47
Оценка:
Здравствуйте, -MyXa-, Вы писали:

MX>
I>>                using(var _ = Bundle.Create(new { x = new Resource("1"), y = new Resource("2"), z = new Resource("3")}))
MX>


MX>В какой же, интересно знать, версии C#-а компилируется выражение Bundle.Create(new...), при том, что "public class Bundle<T>"?


А ты внимательно посмотри, но вообще этот вопрос для форума C#, задай там.

I>>И добавляется функция Create + класс

I>>
I>>        public void Dispose()
I>>        {
I>>            if (_t == null)
I>>                return;
I>>            Bundle.TryDisposeAll(_t);
I>>            _t = null;
I>>            GC.SuppressFinalize(this);
I>>        }

I>>        ~Bundle()
I>>        {
I>>            Dispose();
I>>        }
I>>    }
I>>


MX>Наверное, в той же версии, в которой компилируется "Bundle.TryDisposeAll"... там точно <T> не нужно?


Точно.

MX>А ещё мне не понятна вот эта идея —


I>>обработка исключений

I>>[ccode]
I>> try
I>> {
I>> ...
I>> }
I>> catch (ResourceBundleException ex)
I>> {
I>> foreach (var e in ex)
I>> {
I>> Trace.WriteLine(e.Message);
I>> }
I>> }

MX>Это, вообще, куда?


Это вообще обработка того, что бросается в using. все мой код нужен для того, что бы обрабатывать множественные исключения в Dispose. Случай в дотнет вырожденый, т.к. обычно достаточно одного факта исключений, а не самих исключений.
Re[21]: 2) Будем бдительны: pair<shared_ptr<T1>, shared_ptr<T1>>
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 07.11.12 10:15
Оценка: -2
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Ну вот судя по http://www.rsdn.ru/forum/flame.comp/4943382.1
Автор: Ikemefula
Дата: 26.10.12

EP>

EP>то есть деструкторы годятся для
EP>1. мелких фенек привязаных к скопу переменной, типа сброс флажка
EP>2. управления ресурсами когда этот код никогда не может кинуть исключение
EP>3. управления памятью
EP>Итого, преимуществ перед менеджед IDisposable около нуля.

EP>Ты предполагаешь, что большая часть ресурсов, может фейлится на release.
EP>Я хочу увидеть примеры таких ресурсов — раз их большинство, то у тебя должно быть хотя бы несколько примеров, а не просто отсылка в тред.

Я видел целую кучу разных проектов писаных в разных странах разными людьми, где в деструкторах летели исключения.
Если ты работаешь только со своим собственным кодом, это вобщем ничего не доказывает.

I>>Проблемы мягко говоря разной сложности. Приведи прямо здесь __весь__ необходимый код что бы твой пример заработал и тебе все станет ясно.


EP>какой конкретно "мой" пример?


Тот который ты показал в качестве "решения" для C++. Если не можешь следить за контекстом, иди лучше песочницу.

EP>я их сравниваю в контексте твоего высказывания:

EP>

EP>что-то распиханное по уголкам и дёргающееся неявно

EP>

Ты выдернул кусочек фразы. Там было про дизайн, а не финализаторы. Но я не уверен, может для тебя это одно и то же.

EP>А ну ок — раз среда управляет одним ресурсом неявно, то это уже не ресурс. А если не помогает управлять каким-либо другим ресурсом — то уже вдруг:


Еще раз — в дотнете память это не ресурс. Это тебе удобно назвать его ресурсом, дурацкая привычка из С++. Никакого бонуса это не дает, в отличие от скажем файлов.

EP>Не, я всё понимаю, в казуальных свистелках проглатывание исключения допустимо.


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

EP>Но почему, блин, проглатывание первых, и выпускание последнего, а не наоборот? Чаще ведь важно именно первое, оно может и является причиной остальных


Если тебе важны исключения, пиши код который гарантирует правильное возникновение, распространение и обработку. Дотнет этому нисколько не препятствует, например есть такая вещь как try-finally.

I>>
I>>            using (var r1 = new Resource()) { 
I>>            using (var r2 = new Resource()) { 
I>>            using (var r3 = new Resource())
I>>            {
                    
I>>            }}}
I>>


EP>А зачем несколько юсингов? Почему не один?


В скобках можно объявить только один тип. твой один все равно развернется в несколько штук.


EP>Только это не фикс Dispose+using, даже с учётом версии из следующего сообщения — это фактически отказ от них в пользу библиотечной логики

EP>И для надёжности тут желательно вообще вместо IDisposable, использовать кастомный интерфейс — большинство кода ведь не рассчитано на кидающий Dispose. И люди видя IDisposable — могут не правильно понять

Да не гони пургу, не нужно здесь вводить новый интерфейс. любой, кому нужны все исключения вместе взятые, сможет написать легальный код. Отличие от моего — мой универсальный. конкретный же код пишется очень просто.
то есть, еще раз, мой код это просто сахар для демонстрации.
Я вобще дговоря описал и для using, но ты похоже не читатель.

В дотнете нет никакой проблемы обработать все исключения. Вообще никакой.
В с++ ты фиксишь корявый плюсовый рантайм. В дотнете можно просто забить и ничего фатального не случится. В С++ у тебя просто нет возможности внятно использовать исключения. По этой причине вагоны проектов на плюсах вообще не используют исключение, запрет при чем административный.

EP>Подобную библиотечную логику и в C++ можно реализовать, и даже в C (естественно с некоторыми ограничениями)


О чем и речь — начинается изобретение дотнет фремворка.

I>>Осталось еще 9. Продолжай.


EP>можешь все разом израсходовать


Идейка в том, что бы ты раз за разом повторяя одно и то же наконец то понял, о чем речь. Так что валяй, еще 9 раз осталось.

Вобщем пока что от тебя ровно те же аргументы, что и от любого другого сиплюсника, только ты дотнет вообще не знаешь. Ну и на всякий — лет примерно 8-10 назад я писал точно такие же аргументы, как у тебя, когда был сиплюсником. Так шта...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.