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