mirror of
https://github.com/boostorg/scope.git
synced 2026-01-19 04:42:10 +00:00
1625 lines
53 KiB
C++
1625 lines
53 KiB
C++
/*
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* https://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
* Copyright (c) 2023-2024 Andrey Semashev
|
|
*/
|
|
/*!
|
|
* \file unique_resource.cpp
|
|
* \author Andrey Semashev
|
|
*
|
|
* \brief This file contains tests for \c unique_resource.
|
|
*/
|
|
|
|
#include <boost/scope/unique_resource.hpp>
|
|
#include <boost/core/lightweight_test.hpp>
|
|
#include <boost/core/lightweight_test_trait.hpp>
|
|
#include <boost/config.hpp>
|
|
#include <ostream>
|
|
#include <utility>
|
|
#include <stdexcept>
|
|
|
|
template< typename Resource >
|
|
struct empty_resource_deleter
|
|
{
|
|
void operator() (Resource const&) const noexcept
|
|
{
|
|
}
|
|
};
|
|
|
|
template< typename Resource >
|
|
inline void copy_resource(Resource const& from, Resource& to)
|
|
{
|
|
to = from;
|
|
}
|
|
|
|
template< typename Resource >
|
|
class checking_resource_deleter
|
|
{
|
|
private:
|
|
Resource* m_deleted;
|
|
int& m_n;
|
|
|
|
public:
|
|
explicit checking_resource_deleter(int& n) noexcept :
|
|
m_deleted(nullptr),
|
|
m_n(n)
|
|
{
|
|
}
|
|
|
|
explicit checking_resource_deleter(Resource& deleted, int& n) noexcept :
|
|
m_deleted(&deleted),
|
|
m_n(n)
|
|
{
|
|
}
|
|
|
|
checking_resource_deleter(checking_resource_deleter&& that) noexcept :
|
|
m_deleted(that.m_deleted),
|
|
m_n(that.m_n)
|
|
{
|
|
}
|
|
|
|
checking_resource_deleter(checking_resource_deleter const& that) noexcept :
|
|
m_deleted(that.m_deleted),
|
|
m_n(that.m_n)
|
|
{
|
|
}
|
|
|
|
// Make sure the deleter is move and copy-assignable
|
|
checking_resource_deleter& operator= (checking_resource_deleter&& that) noexcept
|
|
{
|
|
m_deleted = that.m_deleted;
|
|
return *this;
|
|
}
|
|
|
|
checking_resource_deleter& operator= (checking_resource_deleter const& that) noexcept
|
|
{
|
|
m_deleted = that.m_deleted;
|
|
return *this;
|
|
}
|
|
|
|
Resource* get_deleted() const noexcept
|
|
{
|
|
return m_deleted;
|
|
}
|
|
|
|
void operator() (Resource const& res) const noexcept
|
|
{
|
|
if (m_deleted)
|
|
copy_resource(res, *m_deleted);
|
|
++m_n;
|
|
}
|
|
};
|
|
|
|
int g_n = 0, g_res1 = 0, g_res2 = 0;
|
|
|
|
void check_int()
|
|
{
|
|
{
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int > > ur;
|
|
BOOST_TEST_EQ(ur.get(), 0);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
}
|
|
|
|
int n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur{ boost::scope::default_resource, checking_resource_deleter< int >(n) };
|
|
BOOST_TEST_EQ(ur.get(), 0);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
|
|
{
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int > > ur{ 10 };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST(!!ur);
|
|
}
|
|
|
|
int deleted_res1 = -1;
|
|
n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur{ 0, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 0);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 0);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST_EQ(ur.get_deleter().get_deleted(), &deleted_res1);
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
ur.release();
|
|
BOOST_TEST(!ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(deleted_res1, -1);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST_EQ(noexcept(ur.reset()), noexcept(std::declval< checking_resource_deleter< int >& >()(10)));
|
|
ur.reset();
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
ur.reset(20);
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
deleted_res1 = -1;
|
|
BOOST_TEST_EQ(ur.get(), 20);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, 20);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur1{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur2{ std::move(ur1) };
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
int deleted_res2 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur1{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur2{ 20, checking_resource_deleter< int >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(ur2.get(), 20);
|
|
BOOST_TEST(ur2.allocated());
|
|
ur2 = std::move(ur1);
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, -1);
|
|
BOOST_TEST_EQ(deleted_res2, 20);
|
|
deleted_res2 = -1;
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
BOOST_TEST_EQ(deleted_res2, -1);
|
|
|
|
{
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int > > ur1;
|
|
BOOST_TEST_EQ(ur1.get(), 0);
|
|
BOOST_TEST(!ur1.allocated());
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int > > ur2{ 10, empty_resource_deleter< int >() };
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur2.get(), 0);
|
|
BOOST_TEST(!ur2.allocated());
|
|
}
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
deleted_res2 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur1{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST_EQ(ur1.get_deleter().get_deleted(), &deleted_res1);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int > > ur2{ 20, checking_resource_deleter< int >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(ur2.get(), 20);
|
|
BOOST_TEST_EQ(ur2.get_deleter().get_deleted(), &deleted_res2);
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(ur1.get(), 20);
|
|
BOOST_TEST_EQ(ur1.get_deleter().get_deleted(), &deleted_res2);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST_EQ(ur2.get_deleter().get_deleted(), &deleted_res1);
|
|
BOOST_TEST(ur2.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
BOOST_TEST_EQ(deleted_res2, 20);
|
|
|
|
struct local
|
|
{
|
|
static void raw_func_deleter(int)
|
|
{
|
|
++g_n;
|
|
}
|
|
|
|
static void raw_func_deleter1(int res)
|
|
{
|
|
g_res1 = res;
|
|
}
|
|
|
|
static void raw_func_deleter2(int res)
|
|
{
|
|
g_res2 = res;
|
|
}
|
|
};
|
|
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, void (&)(int) > ur(10, local::raw_func_deleter);
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST(!noexcept(ur.reset()));
|
|
}
|
|
BOOST_TEST_EQ(g_n, 1);
|
|
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, void (&)(int) > ur1(10, local::raw_func_deleter);
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, void (&)(int) > ur2(std::move(ur1));
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(g_n, 1);
|
|
|
|
g_res1 = 0;
|
|
g_res2 = 0;
|
|
{
|
|
boost::scope::unique_resource< int, void (&)(int) > ur1(10, local::raw_func_deleter1);
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, void (&)(int) > ur2(20, local::raw_func_deleter2);
|
|
BOOST_TEST_EQ(ur2.get(), 20);
|
|
BOOST_TEST(ur2.allocated());
|
|
ur2 = std::move(ur1);
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(g_res1, 10);
|
|
BOOST_TEST_EQ(g_res2, 20);
|
|
|
|
g_res1 = 0;
|
|
g_res2 = 0;
|
|
{
|
|
boost::scope::unique_resource< int, void (&)(int) > ur1(10, local::raw_func_deleter1);
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, void (&)(int) > ur2(20, local::raw_func_deleter2);
|
|
BOOST_TEST_EQ(ur2.get(), 20);
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(ur1.get(), 20);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
}
|
|
BOOST_TEST_EQ(g_res1, 10);
|
|
BOOST_TEST_EQ(g_res2, 20);
|
|
}
|
|
|
|
|
|
struct struct_resource
|
|
{
|
|
int value;
|
|
|
|
struct_resource(int v = 0) noexcept :
|
|
value(v)
|
|
{
|
|
}
|
|
|
|
friend bool operator== (struct_resource const& left, struct_resource const& right) noexcept
|
|
{
|
|
return left.value == right.value;
|
|
}
|
|
|
|
friend bool operator!= (struct_resource const& left, struct_resource const& right) noexcept
|
|
{
|
|
return !(left == right);
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& strm, struct_resource const& res)
|
|
{
|
|
strm << "{ " << res.value << " }";
|
|
return strm;
|
|
}
|
|
};
|
|
|
|
void check_struct()
|
|
{
|
|
{
|
|
boost::scope::unique_resource< struct_resource, empty_resource_deleter< struct_resource > > ur;
|
|
BOOST_TEST_EQ(ur.get(), struct_resource{});
|
|
BOOST_TEST(!ur.allocated());
|
|
}
|
|
|
|
int n = 0;
|
|
struct_resource deleted_res1{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur{ struct_resource{ 10 }, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur{ struct_resource{ 10 }, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur.allocated());
|
|
ur.reset(20);
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
deleted_res1 = struct_resource{ -1 };
|
|
BOOST_TEST_EQ(ur.get(), struct_resource{ 20 });
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 20 });
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur1{ struct_resource{ 10 }, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur2{ std::move(ur1) };
|
|
BOOST_TEST_EQ(ur2.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
struct_resource deleted_res2{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur1{ struct_resource{ 10 }, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur2{ struct_resource{ 20 }, checking_resource_deleter< struct_resource >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(ur2.get(), struct_resource{ 20 });
|
|
BOOST_TEST(ur2.allocated());
|
|
ur2 = std::move(ur1);
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ -1 });
|
|
BOOST_TEST_EQ(deleted_res2, struct_resource{ 20 });
|
|
deleted_res2 = struct_resource{ -1 };
|
|
BOOST_TEST_EQ(ur2.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
BOOST_TEST_EQ(deleted_res2, struct_resource{ -1 });
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
deleted_res2 = struct_resource{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur1{ struct_resource{ 10 }, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), struct_resource{ 10 });
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< struct_resource, checking_resource_deleter< struct_resource > > ur2{ struct_resource{ 20 }, checking_resource_deleter< struct_resource >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(ur2.get(), struct_resource{ 20 });
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(ur1.get(), struct_resource{ 20 });
|
|
BOOST_TEST_EQ(ur1.get_deleter().get_deleted(), &deleted_res2);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur2.get(), struct_resource{ 10 });
|
|
BOOST_TEST_EQ(ur2.get_deleter().get_deleted(), &deleted_res1);
|
|
BOOST_TEST(ur2.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
BOOST_TEST_EQ(deleted_res2, struct_resource{ 20 });
|
|
}
|
|
|
|
|
|
void check_ptr()
|
|
{
|
|
{
|
|
boost::scope::unique_resource< struct_resource*, empty_resource_deleter< struct_resource* > > ur;
|
|
BOOST_TEST_EQ(ur.get(), static_cast< struct_resource* >(nullptr));
|
|
BOOST_TEST(!ur.allocated());
|
|
}
|
|
|
|
int n = 0;
|
|
struct_resource res1{ 10 };
|
|
struct_resource* deleted_res1 = nullptr;
|
|
{
|
|
boost::scope::unique_resource< struct_resource*, checking_resource_deleter< struct_resource* > > ur{ &res1, checking_resource_deleter< struct_resource* >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), &res1);
|
|
BOOST_TEST_EQ(ur.get()->value, 10);
|
|
BOOST_TEST_EQ(ur->value, 10);
|
|
BOOST_TEST_EQ((*ur).value, 10);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, &res1);
|
|
}
|
|
|
|
|
|
void check_ref()
|
|
{
|
|
int n = 0;
|
|
struct_resource res1{ 10 };
|
|
struct_resource deleted_res1{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur{ res1, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), res1);
|
|
BOOST_TEST_EQ(&ur.get(), &res1);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, res1);
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
struct_resource res2{ 20 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur{ res1, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(&ur.get(), &res1);
|
|
BOOST_TEST(ur.allocated());
|
|
ur.reset(res2);
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, res1);
|
|
deleted_res1 = struct_resource{ -1 };
|
|
BOOST_TEST_EQ(&ur.get(), &res2);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, res2);
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur1{ res1, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(&ur1.get(), &res1);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur2{ std::move(ur1) };
|
|
BOOST_TEST_EQ(&ur2.get(), &res1);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, res1);
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
struct_resource deleted_res2{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur1{ res1, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(&ur1.get(), &res1);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur2{ res2, checking_resource_deleter< struct_resource >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(&ur2.get(), &res2);
|
|
BOOST_TEST(ur2.allocated());
|
|
ur2 = std::move(ur1);
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ -1 });
|
|
BOOST_TEST_EQ(deleted_res2, struct_resource{ 20 });
|
|
deleted_res2 = struct_resource{ -1 };
|
|
BOOST_TEST_EQ(&ur2.get(), &res1);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
BOOST_TEST_EQ(deleted_res2, struct_resource{ -1 });
|
|
|
|
n = 0;
|
|
deleted_res1 = struct_resource{ -1 };
|
|
deleted_res2 = struct_resource{ -1 };
|
|
{
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur1{ res1, checking_resource_deleter< struct_resource >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(&ur1.get(), &res1);
|
|
BOOST_TEST(ur1.allocated());
|
|
struct_resource expected_res2{ 20 };
|
|
boost::scope::unique_resource< struct_resource&, checking_resource_deleter< struct_resource > > ur2{ res2, checking_resource_deleter< struct_resource >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(&ur2.get(), &res2);
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(&ur1.get(), &res2);
|
|
BOOST_TEST_EQ(ur1.get_deleter().get_deleted(), &deleted_res2);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(&ur2.get(), &res1);
|
|
BOOST_TEST_EQ(ur2.get_deleter().get_deleted(), &deleted_res1);
|
|
BOOST_TEST(ur2.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, struct_resource{ 10 });
|
|
BOOST_TEST_EQ(deleted_res2, struct_resource{ 20 });
|
|
}
|
|
|
|
|
|
class throw_on_move_resource
|
|
{
|
|
private:
|
|
int m_value;
|
|
|
|
public:
|
|
explicit throw_on_move_resource(int v = 0) noexcept :
|
|
m_value(v)
|
|
{
|
|
}
|
|
|
|
throw_on_move_resource(throw_on_move_resource const&) = default;
|
|
throw_on_move_resource& operator= (throw_on_move_resource const&) = default;
|
|
|
|
throw_on_move_resource(throw_on_move_resource&&)
|
|
{
|
|
throw std::runtime_error("throw_on_move_resource move ctor");
|
|
}
|
|
throw_on_move_resource& operator= (throw_on_move_resource&&)
|
|
{
|
|
throw std::runtime_error("throw_on_move_resource move assignment");
|
|
}
|
|
|
|
int get() const noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
bool operator== (throw_on_move_resource const& that) const noexcept
|
|
{
|
|
return m_value == that.m_value;
|
|
}
|
|
|
|
bool operator!= (throw_on_move_resource const& that) const noexcept
|
|
{
|
|
return !operator==(that);
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& strm, throw_on_move_resource const& res)
|
|
{
|
|
strm << "{ " << res.m_value << " }";
|
|
return strm;
|
|
}
|
|
};
|
|
|
|
class throw_on_copy_resource
|
|
{
|
|
private:
|
|
int m_value;
|
|
mutable bool m_throw;
|
|
|
|
public:
|
|
explicit throw_on_copy_resource(int v = 0, bool do_throw = true) noexcept :
|
|
m_value(v),
|
|
m_throw(do_throw)
|
|
{
|
|
}
|
|
|
|
throw_on_copy_resource(throw_on_copy_resource const& that) :
|
|
m_value(that.m_value),
|
|
m_throw(that.m_throw)
|
|
{
|
|
if (m_throw)
|
|
throw std::runtime_error("throw_on_copy_resource copy ctor");
|
|
}
|
|
throw_on_copy_resource& operator= (throw_on_copy_resource const& that)
|
|
{
|
|
m_value = that.m_value;
|
|
m_throw = that.m_throw;
|
|
if (m_throw)
|
|
throw std::runtime_error("throw_on_copy_resource copy assignment");
|
|
return *this;
|
|
}
|
|
|
|
throw_on_copy_resource(throw_on_copy_resource&&) = delete;
|
|
throw_on_copy_resource& operator= (throw_on_copy_resource&&) = delete;
|
|
|
|
int get() const noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
void set_throw(bool do_throw) const noexcept
|
|
{
|
|
m_throw = do_throw;
|
|
}
|
|
|
|
bool operator== (throw_on_copy_resource const& that) const noexcept
|
|
{
|
|
return m_value == that.m_value;
|
|
}
|
|
|
|
bool operator!= (throw_on_copy_resource const& that) const noexcept
|
|
{
|
|
return !operator==(that);
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& strm, throw_on_copy_resource const& res)
|
|
{
|
|
strm << "{ " << res.m_value << " }";
|
|
return strm;
|
|
}
|
|
};
|
|
|
|
void check_throw_resource()
|
|
{
|
|
int n = 0;
|
|
try
|
|
{
|
|
boost::scope::unique_resource< throw_on_move_resource, checking_resource_deleter< throw_on_move_resource > > ur{ throw_on_move_resource{ 10 }, checking_resource_deleter< throw_on_move_resource >(n) };
|
|
BOOST_TEST_EQ(ur.get().get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_ERROR("An exception is not expected to be thrown by throw_on_move_resource (copy ctor should be used)");
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
|
|
n = 0;
|
|
try
|
|
{
|
|
boost::scope::unique_resource< throw_on_copy_resource, checking_resource_deleter< throw_on_copy_resource > > ur{ throw_on_copy_resource{ 10 }, checking_resource_deleter< throw_on_copy_resource >(n) };
|
|
BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_resource");
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
|
|
n = 0;
|
|
try
|
|
{
|
|
boost::scope::unique_resource< throw_on_copy_resource, checking_resource_deleter< throw_on_copy_resource > > ur1{ throw_on_copy_resource{ 10, false }, checking_resource_deleter< throw_on_copy_resource >(n) };
|
|
ur1.get().set_throw(true);
|
|
try
|
|
{
|
|
boost::scope::unique_resource< throw_on_copy_resource, checking_resource_deleter< throw_on_copy_resource > > ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_resource");
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST_EQ(n, 0);
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
|
|
n = 0;
|
|
try
|
|
{
|
|
boost::scope::unique_resource< throw_on_copy_resource, checking_resource_deleter< throw_on_copy_resource > > ur1{ throw_on_copy_resource{ 10, false }, checking_resource_deleter< throw_on_copy_resource >(n) };
|
|
ur1.get().set_throw(true);
|
|
try
|
|
{
|
|
boost::scope::unique_resource< throw_on_copy_resource, checking_resource_deleter< throw_on_copy_resource > > ur2{ throw_on_copy_resource{ 20, false }, checking_resource_deleter< throw_on_copy_resource >(n) };
|
|
ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_resource");
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST_EQ(n, 1);
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
}
|
|
|
|
class moveable_resource
|
|
{
|
|
private:
|
|
int m_value;
|
|
|
|
public:
|
|
explicit moveable_resource(int value = 0) noexcept :
|
|
m_value(value)
|
|
{
|
|
}
|
|
|
|
moveable_resource(moveable_resource&& that) noexcept :
|
|
m_value(that.m_value)
|
|
{
|
|
that.m_value = -1;
|
|
}
|
|
|
|
moveable_resource& operator= (moveable_resource&& that) noexcept
|
|
{
|
|
m_value = that.m_value;
|
|
that.m_value = -1;
|
|
return *this;
|
|
}
|
|
|
|
moveable_resource(moveable_resource const&) = delete;
|
|
moveable_resource& operator= (moveable_resource const&) = delete;
|
|
|
|
int get() const noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
bool operator== (moveable_resource const& that) const noexcept
|
|
{
|
|
return m_value == that.m_value;
|
|
}
|
|
|
|
bool operator!= (moveable_resource const& that) const noexcept
|
|
{
|
|
return !operator==(that);
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& strm, moveable_resource const& res)
|
|
{
|
|
strm << "{ " << res.m_value << " }";
|
|
return strm;
|
|
}
|
|
|
|
friend void copy_resource(moveable_resource const& from, moveable_resource& to)
|
|
{
|
|
to.m_value = from.m_value;
|
|
}
|
|
};
|
|
|
|
class copyable_resource
|
|
{
|
|
private:
|
|
int m_value;
|
|
|
|
public:
|
|
explicit copyable_resource(int value = 0) noexcept :
|
|
m_value(value)
|
|
{
|
|
}
|
|
|
|
copyable_resource(copyable_resource const& that) noexcept :
|
|
m_value(that.m_value)
|
|
{
|
|
}
|
|
|
|
copyable_resource& operator= (copyable_resource const& that) noexcept
|
|
{
|
|
m_value = that.m_value;
|
|
return *this;
|
|
}
|
|
|
|
int get() const noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
bool operator== (copyable_resource const& that) const noexcept
|
|
{
|
|
return m_value == that.m_value;
|
|
}
|
|
|
|
bool operator!= (copyable_resource const& that) const noexcept
|
|
{
|
|
return !operator==(that);
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& strm, copyable_resource const& res)
|
|
{
|
|
strm << "{ " << res.m_value << " }";
|
|
return strm;
|
|
}
|
|
};
|
|
|
|
template< typename Resource >
|
|
class throwing_resource_deleter :
|
|
public checking_resource_deleter< Resource >
|
|
{
|
|
private:
|
|
using base_type = checking_resource_deleter< Resource >;
|
|
|
|
private:
|
|
mutable bool m_throw;
|
|
|
|
public:
|
|
explicit throwing_resource_deleter(int& n, bool do_throw = false) noexcept :
|
|
base_type(n),
|
|
m_throw(do_throw)
|
|
{
|
|
}
|
|
|
|
explicit throwing_resource_deleter(Resource& deleted, int& n, bool do_throw = false) noexcept :
|
|
base_type(deleted, n),
|
|
m_throw(do_throw)
|
|
{
|
|
}
|
|
|
|
throwing_resource_deleter(throwing_resource_deleter const& that) :
|
|
base_type(static_cast< base_type const& >(that)),
|
|
m_throw(that.m_throw)
|
|
{
|
|
if (m_throw)
|
|
throw std::runtime_error("throwing_resource_deleter copy ctor");
|
|
}
|
|
|
|
throwing_resource_deleter& operator=(throwing_resource_deleter const& that)
|
|
{
|
|
*static_cast< base_type* >(this) = static_cast< base_type const& >(that);
|
|
m_throw = that.m_throw;
|
|
if (m_throw)
|
|
throw std::runtime_error("throwing_resource_deleter copy assignment");
|
|
return *this;
|
|
}
|
|
|
|
void set_throw(bool do_throw) const noexcept
|
|
{
|
|
m_throw = do_throw;
|
|
}
|
|
};
|
|
|
|
template< typename Resource >
|
|
using default_resource_traits = void;
|
|
|
|
template< typename Resource >
|
|
struct wrapped_int_resource_traits
|
|
{
|
|
static Resource make_default() noexcept
|
|
{
|
|
return Resource(-1);
|
|
}
|
|
|
|
static bool is_allocated(Resource const& res) noexcept
|
|
{
|
|
return res.get() >= 0;
|
|
}
|
|
};
|
|
|
|
template< template< typename > class Traits >
|
|
void check_throw_deleter()
|
|
{
|
|
int n = 0;
|
|
{
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< copyable_resource, throwing_resource_deleter< copyable_resource >, Traits< copyable_resource > >;
|
|
unique_resource_t ur1{ boost::scope::default_resource, throwing_resource_deleter< copyable_resource >(n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST_EQ(n, 0);
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
}
|
|
|
|
n = 0;
|
|
{
|
|
copyable_resource deleted_res1;
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< copyable_resource, throwing_resource_deleter< copyable_resource >, Traits< copyable_resource > >;
|
|
unique_resource_t ur1{ copyable_resource{ 10 }, throwing_resource_deleter< copyable_resource >(deleted_res1, n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST_EQ(n, 0);
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, copyable_resource{ 10 });
|
|
}
|
|
|
|
n = 0;
|
|
{
|
|
copyable_resource deleted_res1, deleted_res2;
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< copyable_resource, throwing_resource_deleter< copyable_resource >, Traits< copyable_resource > >;
|
|
unique_resource_t ur1{ copyable_resource{ 10 }, throwing_resource_deleter< copyable_resource >(deleted_res1, n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2{ copyable_resource{ 20 }, throwing_resource_deleter< copyable_resource >(deleted_res2, n) };
|
|
ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res2, copyable_resource{ 20 });
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, copyable_resource{ 10 });
|
|
}
|
|
|
|
n = 0;
|
|
{
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< moveable_resource, throwing_resource_deleter< moveable_resource >, Traits< moveable_resource > >;
|
|
unique_resource_t ur1{ boost::scope::default_resource, throwing_resource_deleter< moveable_resource >(n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
BOOST_TEST_EQ(n, 0);
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
}
|
|
|
|
n = 0;
|
|
{
|
|
moveable_resource deleted_res1;
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< moveable_resource, throwing_resource_deleter< moveable_resource >, Traits< moveable_resource > >;
|
|
unique_resource_t ur1{ moveable_resource{ 10 }, throwing_resource_deleter< moveable_resource >(deleted_res1, n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
// The resource was moved from ur1, but the deleter could not have been moved. Then the resource was moved back to ur1.
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur1.get(), moveable_resource{ 10 });
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, moveable_resource{ 10 });
|
|
}
|
|
|
|
n = 0;
|
|
{
|
|
moveable_resource deleted_res1, deleted_res2;
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< moveable_resource, throwing_resource_deleter< moveable_resource >, Traits< moveable_resource > >;
|
|
unique_resource_t ur1{ moveable_resource{ 10 }, throwing_resource_deleter< moveable_resource >(deleted_res1, n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2{ moveable_resource{ 20 }, throwing_resource_deleter< moveable_resource >(deleted_res2, n) };
|
|
ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
// First, ur2 was reset - the deleter was called on the resource 20. Then the deleter could not have been moved from ur1,
|
|
// leaving ur1 intact.
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res2, moveable_resource{ 20 });
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, moveable_resource{ 10 });
|
|
BOOST_TEST_EQ(deleted_res2, moveable_resource{ 20 });
|
|
}
|
|
}
|
|
|
|
// Specially-crafted resource type that is:
|
|
// * nothrow move-constructible
|
|
// * NOT-nothrow move-assignable
|
|
// * has data layout which favors placing the "allocated" flag in unique_resource in its tail padding
|
|
class move_constructible_resource
|
|
{
|
|
public:
|
|
// A special tag to construct "default" resource value
|
|
struct default_tag { };
|
|
|
|
private:
|
|
int m_n1;
|
|
signed char m_n2;
|
|
|
|
public:
|
|
constexpr move_constructible_resource() noexcept : m_n1(0), m_n2(0) { }
|
|
|
|
// For compatibility with move_constructible_resource_traits::make_default()
|
|
explicit constexpr move_constructible_resource(default_tag) noexcept : move_constructible_resource() { }
|
|
|
|
explicit move_constructible_resource(int n1, signed char n2 = 0) noexcept : m_n1(n1), m_n2(n2) { }
|
|
|
|
move_constructible_resource(move_constructible_resource&& that) noexcept : m_n1(that.m_n1), m_n2(that.m_n2)
|
|
{
|
|
that.m_n1 = 0;
|
|
that.m_n2 = 0;
|
|
}
|
|
|
|
move_constructible_resource(move_constructible_resource const& that) : m_n1(that.m_n1), m_n2(that.m_n2)
|
|
{
|
|
}
|
|
|
|
move_constructible_resource& operator= (move_constructible_resource&& that) // not noexcept
|
|
{
|
|
m_n1 = that.m_n1;
|
|
m_n2 = that.m_n2;
|
|
that.m_n1 = 0;
|
|
that.m_n2 = 0;
|
|
return *this;
|
|
}
|
|
|
|
move_constructible_resource& operator= (move_constructible_resource const& that)
|
|
{
|
|
m_n1 = that.m_n1;
|
|
m_n2 = that.m_n2;
|
|
return *this;
|
|
}
|
|
|
|
// For compatibility with move_constructible_resource_traits::make_default()
|
|
move_constructible_resource& operator= (default_tag) noexcept
|
|
{
|
|
m_n1 = 0;
|
|
m_n2 = 0;
|
|
return *this;
|
|
}
|
|
|
|
bool operator== (move_constructible_resource const& that) const noexcept
|
|
{
|
|
return m_n1 == that.m_n1 && m_n2 == that.m_n2;
|
|
}
|
|
|
|
bool operator!= (move_constructible_resource const& that) const noexcept
|
|
{
|
|
return !operator==(that);
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& strm, move_constructible_resource const& res)
|
|
{
|
|
strm << "{ " << res.m_n1 << ", " << static_cast< int >(res.m_n2) << " }";
|
|
return strm;
|
|
}
|
|
|
|
friend void copy_resource(move_constructible_resource const& from, move_constructible_resource& to)
|
|
{
|
|
to.m_n1 = from.m_n1;
|
|
to.m_n2 = from.m_n2;
|
|
}
|
|
};
|
|
|
|
//! Resource traits for \c move_constructible_resource
|
|
struct move_constructible_resource_traits
|
|
{
|
|
static move_constructible_resource::default_tag make_default() noexcept { return move_constructible_resource::default_tag{}; }
|
|
static bool is_allocated(move_constructible_resource const& res) noexcept { return res != move_constructible_resource{}; }
|
|
};
|
|
|
|
void check_throw_deleter_move_constructible_resource()
|
|
{
|
|
int n = 0;
|
|
{
|
|
move_constructible_resource deleted_res1;
|
|
try
|
|
{
|
|
using unique_resource_t = boost::scope::unique_resource< move_constructible_resource, throwing_resource_deleter< move_constructible_resource >, move_constructible_resource_traits >;
|
|
unique_resource_t ur1{ move_constructible_resource(10, 5), throwing_resource_deleter< move_constructible_resource >(deleted_res1, n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
// The resource was moved from ur1, but the deleter could not have been moved. Then the resource was moved back to ur1.
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur1.get(), move_constructible_resource(10, 5));
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, move_constructible_resource(10, 5));
|
|
}
|
|
|
|
n = 0;
|
|
{
|
|
move_constructible_resource deleted_res1;
|
|
try
|
|
{
|
|
// No resource traits to force using the "allocated" flag
|
|
using unique_resource_t = boost::scope::unique_resource< move_constructible_resource, throwing_resource_deleter< move_constructible_resource > >;
|
|
unique_resource_t ur1{ move_constructible_resource(10, 0), throwing_resource_deleter< move_constructible_resource >(deleted_res1, n) };
|
|
ur1.get_deleter().set_throw(true);
|
|
try
|
|
{
|
|
unique_resource_t ur2 = std::move(ur1);
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
// The resource was moved from ur1, but the deleter could not have been moved. Then the resource was moved back to ur1.
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur1.get(), move_constructible_resource(10, 0));
|
|
throw;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, move_constructible_resource(10, 0));
|
|
}
|
|
}
|
|
|
|
|
|
void check_deduction()
|
|
{
|
|
struct local
|
|
{
|
|
static void raw_func_deleter(int)
|
|
{
|
|
++g_n;
|
|
}
|
|
|
|
#if !defined(BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES)
|
|
static void raw_func_deleter_noexcept(int) noexcept
|
|
{
|
|
++g_n;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, empty_resource_deleter< int > >;
|
|
boost::scope::unique_resource ur{ 0, empty_resource_deleter< int >() };
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
}
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< struct_resource, empty_resource_deleter< struct_resource > >;
|
|
boost::scope::unique_resource ur{ struct_resource(), empty_resource_deleter< struct_resource >() };
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
}
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, void(*)(int) >;
|
|
boost::scope::unique_resource ur{ 0, local::raw_func_deleter };
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
}
|
|
#if !defined(BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES)
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, void(*)(int) noexcept >;
|
|
boost::scope::unique_resource ur{ 0, local::raw_func_deleter_noexcept };
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
}
|
|
#endif
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, empty_resource_deleter< int > >;
|
|
boost::scope::unique_resource ur1{ 0, empty_resource_deleter< int >() };
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur1), expected_unique_resource_t);
|
|
boost::scope::unique_resource ur2 = std::move(ur1);
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur2), expected_unique_resource_t);
|
|
}
|
|
#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
|
|
|
|
int n = 0, deleted_res = -1;
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, checking_resource_deleter< int > >;
|
|
auto ur = boost::scope::make_unique_resource_checked(10, 0, checking_resource_deleter< int >(deleted_res, n));
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res, 10);
|
|
|
|
n = 0;
|
|
deleted_res = -1;
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, checking_resource_deleter< int > >;
|
|
auto ur = boost::scope::make_unique_resource_checked(0, 0, checking_resource_deleter< int >(deleted_res, n));
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
BOOST_TEST_EQ(ur.get(), 0);
|
|
BOOST_TEST(!ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(deleted_res, -1);
|
|
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, void(*)(int) >;
|
|
auto ur = boost::scope::make_unique_resource_checked(0, 0, local::raw_func_deleter);
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
}
|
|
#if !defined(BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES)
|
|
{
|
|
using expected_unique_resource_t = boost::scope::unique_resource< int, void(*)(int) noexcept >;
|
|
auto ur = boost::scope::make_unique_resource_checked(0, 0, local::raw_func_deleter_noexcept);
|
|
BOOST_TEST_TRAIT_SAME(decltype(ur), expected_unique_resource_t);
|
|
}
|
|
#endif
|
|
|
|
n = 0;
|
|
try
|
|
{
|
|
// Not required to throw, but either way the deleter should not be called
|
|
auto ur = boost::scope::make_unique_resource_checked(throw_on_copy_resource{ 0 }, throw_on_copy_resource{ 0 }, checking_resource_deleter< throw_on_copy_resource >(n));
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
|
|
n = 0;
|
|
try
|
|
{
|
|
auto ur = boost::scope::make_unique_resource_checked(0, 0, throwing_resource_deleter< int >(n, true));
|
|
BOOST_ERROR("An exception is expected to be thrown by throwing_resource_deleter");
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
}
|
|
|
|
struct int_resource_traits
|
|
{
|
|
static int make_default() noexcept
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
static bool is_allocated(int res) noexcept
|
|
{
|
|
return res >= 0;
|
|
}
|
|
};
|
|
|
|
void check_resource_traits()
|
|
{
|
|
{
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int >, int_resource_traits > ur;
|
|
BOOST_TEST_EQ(ur.get(), int_resource_traits::make_default());
|
|
BOOST_TEST(!ur.allocated());
|
|
}
|
|
|
|
int n = 0, deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur{ -10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), -10);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(deleted_res1, -1);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur{ 0, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 0);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST(!!ur);
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 0);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
ur.release();
|
|
BOOST_TEST_EQ(ur.get(), int_resource_traits::make_default());
|
|
BOOST_TEST(!ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(deleted_res1, -1);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
ur.reset();
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST_EQ(ur.get(), int_resource_traits::make_default());
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
ur.reset(20);
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
deleted_res1 = -1;
|
|
BOOST_TEST_EQ(ur.get(), 20);
|
|
BOOST_TEST(ur.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, 20);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur1{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur2{ std::move(ur1) };
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST_EQ(ur1.get(), int_resource_traits::make_default());
|
|
BOOST_TEST(!ur1.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
int deleted_res2 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur1{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur2{ 20, checking_resource_deleter< int >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(ur2.get(), 20);
|
|
BOOST_TEST(ur2.allocated());
|
|
ur2 = std::move(ur1);
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
BOOST_TEST_EQ(ur1.get(), int_resource_traits::make_default());
|
|
BOOST_TEST(!ur1.allocated());
|
|
BOOST_TEST_EQ(n, 1);
|
|
BOOST_TEST_EQ(deleted_res1, -1);
|
|
BOOST_TEST_EQ(deleted_res2, 20);
|
|
deleted_res2 = -1;
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
BOOST_TEST_EQ(deleted_res2, -1);
|
|
|
|
{
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int >, int_resource_traits > ur1;
|
|
BOOST_TEST_EQ(ur1.get(), int_resource_traits::make_default());
|
|
BOOST_TEST(!ur1.allocated());
|
|
boost::scope::unique_resource< int, empty_resource_deleter< int >, int_resource_traits > ur2{ 10, empty_resource_deleter< int >() };
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur2.get(), int_resource_traits::make_default());
|
|
BOOST_TEST(!ur2.allocated());
|
|
}
|
|
|
|
n = 0;
|
|
deleted_res1 = -1;
|
|
deleted_res2 = -1;
|
|
{
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur1{ 10, checking_resource_deleter< int >(deleted_res1, n) };
|
|
BOOST_TEST_EQ(ur1.get(), 10);
|
|
BOOST_TEST_EQ(ur1.get_deleter().get_deleted(), &deleted_res1);
|
|
BOOST_TEST(ur1.allocated());
|
|
boost::scope::unique_resource< int, checking_resource_deleter< int >, int_resource_traits > ur2{ 20, checking_resource_deleter< int >(deleted_res2, n) };
|
|
BOOST_TEST_EQ(ur2.get(), 20);
|
|
BOOST_TEST_EQ(ur2.get_deleter().get_deleted(), &deleted_res2);
|
|
BOOST_TEST(ur2.allocated());
|
|
using namespace std;
|
|
swap(ur1, ur2);
|
|
BOOST_TEST_EQ(n, 0);
|
|
BOOST_TEST_EQ(ur1.get(), 20);
|
|
BOOST_TEST_EQ(ur1.get_deleter().get_deleted(), &deleted_res2);
|
|
BOOST_TEST(ur1.allocated());
|
|
BOOST_TEST_EQ(ur2.get(), 10);
|
|
BOOST_TEST_EQ(ur2.get_deleter().get_deleted(), &deleted_res1);
|
|
BOOST_TEST(ur2.allocated());
|
|
}
|
|
BOOST_TEST_EQ(n, 2);
|
|
BOOST_TEST_EQ(deleted_res1, 10);
|
|
BOOST_TEST_EQ(deleted_res2, 20);
|
|
}
|
|
|
|
#if !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
|
|
|
|
struct global_deleter_int
|
|
{
|
|
void operator() (int) noexcept
|
|
{
|
|
++g_n;
|
|
}
|
|
};
|
|
|
|
struct global_deleter_ptr
|
|
{
|
|
void operator() (int*) noexcept
|
|
{
|
|
++g_n;
|
|
}
|
|
};
|
|
|
|
void check_simple_resource_traits()
|
|
{
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, global_deleter_int, boost::scope::unallocated_resource< -1 > > ur;
|
|
BOOST_TEST_EQ(ur.get(), -1);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
}
|
|
BOOST_TEST_EQ(g_n, 0);
|
|
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, global_deleter_int, boost::scope::unallocated_resource< -1 > > ur{ -1 };
|
|
BOOST_TEST_EQ(ur.get(), -1);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
}
|
|
BOOST_TEST_EQ(g_n, 0);
|
|
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, global_deleter_int, boost::scope::unallocated_resource< -1 > > ur{ 10 };
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST(!!ur);
|
|
}
|
|
BOOST_TEST_EQ(g_n, 1);
|
|
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int, global_deleter_int, boost::scope::unallocated_resource< -1, -2, -3 > > ur;
|
|
BOOST_TEST_EQ(ur.get(), -1);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
|
|
ur.reset(-2);
|
|
BOOST_TEST_EQ(g_n, 0);
|
|
BOOST_TEST_EQ(ur.get(), -2);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
|
|
ur.reset(-3);
|
|
BOOST_TEST_EQ(g_n, 0);
|
|
BOOST_TEST_EQ(ur.get(), -3);
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
|
|
ur.reset(10);
|
|
BOOST_TEST_EQ(g_n, 0);
|
|
BOOST_TEST_EQ(ur.get(), 10);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST(!!ur);
|
|
}
|
|
BOOST_TEST_EQ(g_n, 1);
|
|
|
|
g_n = 0;
|
|
{
|
|
boost::scope::unique_resource< int*, global_deleter_ptr, boost::scope::unallocated_resource< nullptr > > ur;
|
|
BOOST_TEST_EQ(ur.get(), static_cast< int* >(nullptr));
|
|
BOOST_TEST(!ur.allocated());
|
|
BOOST_TEST(!ur);
|
|
}
|
|
BOOST_TEST_EQ(g_n, 0);
|
|
|
|
g_n = 0;
|
|
{
|
|
#if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1920)
|
|
boost::scope::unique_resource< int*, global_deleter_ptr, boost::scope::unallocated_resource< nullptr > > ur{ &g_n };
|
|
#else
|
|
// MSVC 14.1 fails with ICE on check_simple_resource_traits. It's not clear what exactly upsets the compiler,
|
|
// but fiddling with this constructor call seems to allow the compilation to succeed.
|
|
// https://developercommunity.visualstudio.com/t/ICE-when-compiling-NTTP-related-BoostSc/10922599
|
|
boost::scope::unique_resource< int*, global_deleter_ptr, boost::scope::unallocated_resource< nullptr > > ur{ &g_n, global_deleter_ptr() };
|
|
#endif
|
|
BOOST_TEST_EQ(ur.get(), &g_n);
|
|
BOOST_TEST(ur.allocated());
|
|
BOOST_TEST(!!ur);
|
|
}
|
|
BOOST_TEST_EQ(g_n, 1);
|
|
}
|
|
|
|
#endif // !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
|
|
|
|
int main()
|
|
{
|
|
check_int();
|
|
check_struct();
|
|
check_ptr();
|
|
check_ref();
|
|
check_throw_resource();
|
|
check_throw_deleter< default_resource_traits >();
|
|
check_throw_deleter< wrapped_int_resource_traits >();
|
|
check_throw_deleter_move_constructible_resource();
|
|
check_deduction();
|
|
check_resource_traits();
|
|
#if !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
|
|
check_simple_resource_traits();
|
|
#endif
|
|
return boost::report_errors();
|
|
}
|