Boost C++ 库

...世界上最受推崇、设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

第 1 章。Boost.Scope - Boost C++ 函数库
Next

第 1 章。Boost.Scope

Andrey Semashev

根据 Boost 软件许可证 1.0 分发。(请参阅随附的 LICENSE_1_0.txt 文件或访问 https://boost.ac.cn/LICENSE_1_0.txt 获取副本)。

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.SmartPtr 中的 boost::scoped_ptr)的泛化。智能指针适用于管理由指针表示的资源(例如,动态分配内存中的对象),而唯一资源包装器可以与更多种类的资源类型一起使用,例如整数(例如 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