Информация об изменениях

Сообщение Re: Exception safe T Stack::pop() ? от 19.08.2018 20:39

Изменено 20.08.2018 5:45 N. I.

Re: Exception safe T Stack::pop() ?
Максим Рогожин:

МР>Можно ли сделать exception-safe T Stack::pop()?


Можно.

МР>Например, так:


Так будет пооптимальнее:

template<class T>
    T Stack<T>::Pop() noexcept(std::is_nothrow_move_constructible_v<T>)
{
    if (vused_ == 0)
        internal_program_error("pop from empty stack");

    try
    {
        return std::move_if_noexcept(v_[--vused_]);
    }
    catch (...)
    {
        ++vused_;
        throw;
    }
}

С вызовом деструктора будет примерно так:

template <class F>
    class scope_exit_wrapper
{
public:
    scope_exit_wrapper(F &&f) :
        m_f(std::forward<F>(f)) {}
    ~scope_exit_wrapper()
    {
        m_f();
    }
    scope_exit_wrapper(scope_exit_wrapper const &) = delete;
private:
    F m_f;
};

template <class F>
    class scope_success_wrapper
{
public:
    scope_success_wrapper(F &&f) :
        m_f(std::forward<F>(f)),
        m_uncaught_exceptions(std::uncaught_exceptions())
    {}
    ~scope_success_wrapper() noexcept(false)
    {
        if (std::uncaught_exceptions() == m_uncaught_exceptions)
            m_f();
    }
    scope_success_wrapper(scope_success_wrapper const &) = delete;
private:
    F m_f;
    int m_uncaught_exceptions;
};

struct scope_exit_wrapper_arg_t {};
struct scope_success_wrapper_arg_t {};

template <class F>
    scope_exit_wrapper<F> operator <<(scope_exit_wrapper_arg_t, F &&f)
        { return {std::forward<F>(f)}; }
template <class F>
    scope_success_wrapper<F> operator <<(scope_success_wrapper_arg_t, F &&f)
        { return {std::forward<F>(f)}; }

#define PP_CONCAT_IMPL(x1, x2) x1##x2
#define PP_CONCAT(x1, x2) PP_CONCAT_IMPL(x1, x2)

#define SCOPE_EXIT(...)                                                   [[maybe_unused]] auto &&PP_CONCAT(SCOPE_SUCCESS_VAR_, __LINE__) =         scope_exit_wrapper_arg_t() << [__VA_ARGS__]() noexcept -> void

#define SCOPE_SUCCESS(...)                                                [[maybe_unused]] auto &&PP_CONCAT(SCOPE_SUCCESS_VAR_, __LINE__) =         scope_success_wrapper_arg_t() << [__VA_ARGS__]() -> void

template<class T>
    T Stack<T>::Pop() noexcept(std::is_nothrow_move_constructible_v<T>)
{
    if (vused_ == 0)
        internal_program_error("pop from empty stack");

    if constexpr (std::is_nothrow_move_constructible_v<T>)
    {
        SCOPE_EXIT([&]{ v_[vused_].~T(); });
        return std::move(v_[--vused_]);
    }
    else
    {
        SCOPE_SUCCESS([&]{ v_[--vused_].~T(); });
        return v_[vused_ - 1];
    }
}
Re: Exception safe T Stack::pop() ?
Максим Рогожин:

МР>Можно ли сделать exception-safe T Stack::pop()?


Можно.

МР>Например, так:


Так будет пооптимальнее:

template<class T>
    T Stack<T>::Pop() noexcept(std::is_nothrow_move_constructible_v<T>)
{
    if (vused_ == 0)
        internal_program_error("pop from empty stack");

    try
    {
        return std::move_if_noexcept(v_[--vused_]);
    }
    catch (...)
    {
        ++vused_;
        throw;
    }
}

С вызовом деструктора будет примерно так:

template <class F>
    class scope_exit_wrapper
{
public:
    scope_exit_wrapper(F &&f) :
        m_f(std::forward<F>(f)) {}
    ~scope_exit_wrapper()
    {
        m_f();
    }
    scope_exit_wrapper(scope_exit_wrapper const &) = delete;
private:
    F m_f;
};

template <class F>
    class scope_success_wrapper
{
public:
    scope_success_wrapper(F &&f) :
        m_f(std::forward<F>(f)),
        m_uncaught_exceptions(std::uncaught_exceptions())
    {}
    ~scope_success_wrapper() noexcept(false)
    {
        if (std::uncaught_exceptions() == m_uncaught_exceptions)
            m_f();
    }
    scope_success_wrapper(scope_success_wrapper const &) = delete;
private:
    F m_f;
    int m_uncaught_exceptions;
};

struct scope_exit_wrapper_arg_t {};
struct scope_success_wrapper_arg_t {};

template <class F>
    scope_exit_wrapper<F> operator <<(scope_exit_wrapper_arg_t, F &&f)
        { return {std::forward<F>(f)}; }
template <class F>
    scope_success_wrapper<F> operator <<(scope_success_wrapper_arg_t, F &&f)
        { return {std::forward<F>(f)}; }

#define PP_CONCAT_IMPL(x1, x2) x1##x2
#define PP_CONCAT(x1, x2) PP_CONCAT_IMPL(x1, x2)

#define SCOPE_EXIT(...)                                                   [[maybe_unused]] auto &&PP_CONCAT(SCOPE_SUCCESS_VAR_, __LINE__) =         scope_exit_wrapper_arg_t() << [__VA_ARGS__]() noexcept -> void

#define SCOPE_SUCCESS(...)                                                [[maybe_unused]] auto &&PP_CONCAT(SCOPE_SUCCESS_VAR_, __LINE__) =         scope_success_wrapper_arg_t() << [__VA_ARGS__]() -> void

template<class T>
    T Stack<T>::Pop() noexcept(std::is_nothrow_move_constructible_v<T>)
{
    if (vused_ == 0)
        internal_program_error("pop from empty stack");

    if constexpr (std::is_nothrow_move_constructible_v<T>)
    {
        SCOPE_EXIT(&){ v_[vused_].~T(); };
        return std::move(v_[--vused_]);
    }
    else
    {
        SCOPE_SUCCESS(&){ v_[--vused_].~T(); };
        return v_[vused_ - 1];
    }
}