Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码标准

Next

第 1 章。 Boost.Scope

Andrey Semashev

根据 Boost 软件许可协议 1.0 版发布。(请参阅随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制。)

目录

简介
安装和兼容性
作用域守卫
条件作用域守卫:scope_exitscope_successscope_fail
作用域守卫条件函数
无条件作用域守卫:defer_guard
按引用捕获的注意事项
在运行时设置作用域退出动作
与 Boost.ScopeExit 库的比较
与 C++ 库基础扩展中定义的作用域守卫的比较
唯一资源包装器
资源特征
简化资源特征
与 C++ 库基础扩展中定义的 unique_resource 的比较
参考
头文件 <boost/scope/defer.hpp>
头文件 <boost/scope/error_code_checker.hpp>
头文件 <boost/scope/exception_checker.hpp>
头文件 <boost/scope/fd_deleter.hpp>
头文件 <boost/scope/fd_resource_traits.hpp>
头文件 <boost/scope/scope_exit.hpp>
头文件 <boost/scope/scope_fail.hpp>
头文件 <boost/scope/scope_success.hpp>
头文件 <boost/scope/unique_fd.hpp>
头文件 <boost/scope/unique_resource.hpp>
头文件 <boost/scope/unique_resource_fwd.hpp>
更新日志

Boost.Scope 库是一组实用工具的集合,旨在帮助在离开作用域时执行代码以及自动资源管理。该库包含的组件类似于C++ 库基础扩展,版本 3 技术规范(或简称 TS)中,<experimental/scope> 标准库头文件中指定的组件,最初在 P0052R10 中定义。该库还包含对 TS 的扩展,以提高组件的可用性和效率。

该库提供的组件可以分为两类

  • 一组作用域守卫,允许在作用域守卫被销毁时执行任意代码,
  • 一个通用资源包装器,可在销毁时自动释放拥有的资源。

该库在其命名空间 boost::scope 中定义了其组件。为了简洁起见,本文档中可能会省略命名空间限定;读者应假定未限定的名称(如 scope_exitunique_resource)在 boost::scope 中定义。

作用域守卫允许用户在离开声明作用域守卫的作用域时执行一段代码(一个动作)。根据作用域守卫的不同,动作可以无条件执行,在正常离开作用域时执行,或者由于异常而执行,甚至可以根据用户指定的条件执行。这在各种用例中都可能很有用,下面将举例说明其中一些用例。

表 1.1。 Boost.Scope 作用域守卫的语法概述

C++11

C++17

class adder
{
    int x, y;

public:
    // Computes a sum of integers
    int compute()
    {
        // Reset variables on return or exception
        auto cleanup = boost::scope::make_scope_exit([this]
        {
            x = 0;
            y = 0;
        });

        long long int sum = static_cast< long long int >(x) +
            static_cast< long long int >(y);
        if (sum < std::numeric_limits< int >::min() ||
            sum > std::numeric_limits< int >::max())
        {
            throw std::overflow_error("Integer overflow");
        }

        return static_cast< int >(sum);
    }
};
class adder
{
    int x, y;

public:
    // Computes a sum of integers
    int compute()
    {
        // Reset variables on return or exception.
        // Anonymous scope guard.
        BOOST_SCOPE_DEFER [this]
        {
            x = 0;
            y = 0;
        };

        long long int sum = static_cast< long long int >(x) +
            static_cast< long long int >(y);
        if (sum < std::numeric_limits< int >::min() ||
            sum > std::numeric_limits< int >::max())
        {
            throw std::overflow_error("Integer overflow");
        }

        return static_cast< int >(sum);
    }
};
template< typename Object >
class collection
{
    std::set< Object > objects;

public:
    // Adds a new object to the collection
    Object& add_object()
    {
        auto it = objects.emplace();

        // Revert object insertion on exception
        auto cleanup = boost::scope::make_scope_fail([this, it]
        {
            objects.erase(it);
        });

        // Throws on error
        it->on_added(*this);

        return *it;
    }
};
template< typename Object >
class collection
{
    std::set< Object > objects;

public:
    // Adds a new object to the collection
    Object& add_object()
    {
        auto it = objects.emplace();

        // Revert object insertion on exception
        boost::scope::scope_fail cleanup{[this, it]
        {
            objects.erase(it);
        }};

        // Throws on error
        it->on_added(*this);

        return *it;
    }
};
// Writes a list of strings to the file, in CSV format
bool save_as_csv(std::vector< std::string > const& strings,
    std::string const& filename)
{
    std::ofstream file(filename.c_str(),
        std::ios_base::out | std::ios_base::trunc);
    if (!file.is_open())
        return false;

    // Set a scope guard to remove the partially written file
    // in case of error - exception or not
    auto remove_guard = boost::scope::make_scope_exit([&file, &filename]
    {
        file.close(); // close the file to allow remove() to succeed on Windows
        std::remove(filename.c_str());
    });

    bool first = true;
    for (auto const& str : strings)
    {
        if (!first)
            file << ',';
        else
            first = false;

        file << '"' << str << '"';

        if (file.fail())
            return false;
    }

    file << std::endl;
    if (file.fail())
        return false;

    // Commit the operation
    remove_guard.set_active(false);

    return true;
}
// Writes a list of strings to the file, in CSV format
bool save_as_csv(std::vector< std::string > const& strings,
    std::string const& filename)
{
    std::ofstream file(filename.c_str(),
        std::ios_base::out | std::ios_base::trunc);
    if (!file.is_open())
        return false;

    // Set a scope guard to remove the partially written file
    // in case of error - exception or not
    boost::scope::scope_exit remove_guard{[&file, &filename]
    {
        file.close(); // close the file to allow remove() to succeed on Windows
        std::remove(filename.c_str());
    }};

    bool first = true;
    for (auto const& str : strings)
    {
        if (!first)
            file << ',';
        else
            first = false;

        file << '"' << str << '"';

        if (file.fail())
            return false;
    }

    file << std::endl;
    if (file.fail())
        return false;

    // Commit the operation
    remove_guard.set_active(false);

    return true;
}

此库提供的作用域守卫与 Boost.ScopeExit 之间存在一些重叠。Boost.ScopeExit 相比,Boost.Scope 提供了更简单的语法(尤其是在支持 C++17 的编译器上)以及针对特定用例的新功能。Boost.Scope 提供的作用域守卫与 Boost.ScopeExit 之间的详细比较在单独的章节中给出。

Boost.Scope 提供的唯一资源包装器是智能指针(如 std::unique_ptrboost::scoped_ptr,来自 Boost.SmartPtr)的泛化。虽然智能指针适用于管理由指针表示的资源(例如,动态分配内存中的对象),但唯一资源包装器可以用于更多种类的资源类型,例如整数(例如,POSIX 文件描述符)和用户定义的类型。

// Fills the buffer with random bytes from system RNG. Returns 0 on success, otherwise an error code.
int get_random_bytes(unsigned char* bytes, std::size_t size)
{
    // Open RNG device and wrap the returned POSIX file descriptor in unique_resource.
    // This wrapper will automatically close the file descriptor upon destruction by invoking fd_deleter on it.
    boost::scope::unique_resource< int, boost::scope::fd_deleter, boost::scope::fd_resource_traits > fd(open("/dev/urandom", O_RDONLY));

    // fd_resource_traits allows the unique_resource to recognize when open() returns a valid file descriptor
    // and when it fails and returns -1. In the latter case, the constructed unique_resource is unallocated,
    // which we test below.
    if (!fd)
        return errno;

    // Read random bytes until the buffer is filled or an error occurs
    std::size_t read_size = 0u;
    while (read_size < size)
    {
        ssize_t res = read(fd.get(), bytes + read_size, size - read_size);
        if (res < 0)
        {
            int err = errno;
            if (err == EINTR)
                continue;

            return err;
        }

        if (res == 0)
            return ENODATA;

        read_size += res;
    }

    return 0;
}

Next