From 80d26d690922d4f4d1d7e622232826210bc3e52d Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 17 Dec 2023 19:16:08 +0300 Subject: [PATCH] Prevent default-construction of function pointers. Default- and value-constructed pointers to functions are not callable, and therefore are not accepted as default-constructible condition function objects and deleters. Closes https://github.com/Lastique/scope/issues/14. --- .../is_nonnull_default_constructible.hpp | 66 +++++++++++++++++++ include/boost/scope/scope_exit.hpp | 6 +- include/boost/scope/unique_resource.hpp | 13 ++-- .../scope_exit_cond_def_cted_fptr.cpp | 25 +++++++ .../unique_resource_del_def_cted_fptr.cpp | 23 +++++++ 5 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 include/boost/scope/detail/is_nonnull_default_constructible.hpp create mode 100644 test/compile_fail/scope_exit_cond_def_cted_fptr.cpp create mode 100644 test/compile_fail/unique_resource_del_def_cted_fptr.cpp diff --git a/include/boost/scope/detail/is_nonnull_default_constructible.hpp b/include/boost/scope/detail/is_nonnull_default_constructible.hpp new file mode 100644 index 0000000..e6e8647 --- /dev/null +++ b/include/boost/scope/detail/is_nonnull_default_constructible.hpp @@ -0,0 +1,66 @@ +/* + * 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 Andrey Semashev + */ +/*! + * \file scope/detail/is_nonnull_default_constructible.hpp + * + * This header contains definition of \c is_nonnull_default_constructible + * and \c is_nothrow_nonnull_default_constructible type traits. The type + * traits are useful for preventing default-construction of pointers to + * functions where a default-constructed function object is expected. + * Without it, default- or value-constructing a pointer to function would + * produce a function object that is not callable. + */ + +#ifndef BOOST_SCOPE_DETAIL_IS_NONNULL_DEFAULT_CONSTRUCTIBLE_HPP_INCLUDED_ +#define BOOST_SCOPE_DETAIL_IS_NONNULL_DEFAULT_CONSTRUCTIBLE_HPP_INCLUDED_ + +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace scope { +namespace detail { + +//! The type trait checks if \c T is not a pointer and is default-constructible +template< typename T > +struct is_nonnull_default_constructible : + public std::is_default_constructible< T > +{ +}; + +template< typename T > +struct is_nonnull_default_constructible< T* > : + public std::false_type +{ +}; + +//! The type trait checks if \c T is not a pointer and is nothrow-default-constructible +template< typename T > +struct is_nothrow_nonnull_default_constructible : + public std::is_nothrow_default_constructible< T > +{ +}; + +template< typename T > +struct is_nothrow_nonnull_default_constructible< T* > : + public std::false_type +{ +}; + +} // namespace detail +} // namespace scope +} // namespace boost + +#include + +#endif // BOOST_SCOPE_DETAIL_IS_NONNULL_DEFAULT_CONSTRUCTIBLE_HPP_INCLUDED_ diff --git a/include/boost/scope/scope_exit.hpp b/include/boost/scope/scope_exit.hpp index 759c0b2..230efe6 100644 --- a/include/boost/scope/scope_exit.hpp +++ b/include/boost/scope/scope_exit.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -275,7 +276,8 @@ class scope_exit /*! * \brief Constructs a scope guard with a given callable action function object. * - * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible. + * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible + * and is not a pointer to function. * * \note The requirement for \c Cond default constructor to be non-throwing is to allow for * the condition function object to be called in case if constructing either function @@ -295,7 +297,7 @@ class scope_exit typename F //! \cond , typename = typename std::enable_if< detail::conjunction< - std::is_nothrow_default_constructible< Cond >, + detail::is_nothrow_nonnull_default_constructible< Cond >, std::is_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, diff --git a/include/boost/scope/unique_resource.hpp b/include/boost/scope/unique_resource.hpp index 039f762..0d43d40 100644 --- a/include/boost/scope/unique_resource.hpp +++ b/include/boost/scope/unique_resource.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -302,10 +303,10 @@ class deleter_holder : public: template< - bool Requires = std::is_default_constructible< internal_deleter_type >::value, + bool Requires = detail::is_nonnull_default_constructible< internal_deleter_type >::value, typename = typename std::enable_if< Requires >::type > - constexpr deleter_holder() noexcept(std::is_nothrow_default_constructible< internal_deleter_type >::value) : + constexpr deleter_holder() noexcept(detail::is_nothrow_nonnull_default_constructible< internal_deleter_type >::value) : deleter_base() { } @@ -1064,7 +1065,8 @@ class unique_resource /*! * \brief Constructs an unallocated unique resource guard. * - * **Requires:** Default \c Resource value can be constructed and \c Deleter is default-constructible. + * **Requires:** Default \c Resource value can be constructed. \c Deleter is default-constructible + * and is not a pointer to function. * * **Effects:** Initializes the \c Resource object with the default resource value. Default-constructs * the \c Deleter object. @@ -1126,7 +1128,8 @@ class unique_resource /*! * \brief Constructs a unique resource guard with the given resource and a default-constructed deleter. * - * **Requires:** \c Resource is constructible from \a res and \c Deleter is default-constructible. + * **Requires:** \c Resource is constructible from \a res. \c Deleter is default-constructible and + * is not a pointer to function. * * **Effects:** Constructs the unique resource object as if by calling * `unique_resource(std::forward< R >(res), Deleter())`. @@ -1139,7 +1142,7 @@ class unique_resource typename R //! \cond , typename = typename std::enable_if< detail::conjunction< - std::is_nothrow_default_constructible< deleter_type >, + detail::is_nothrow_nonnull_default_constructible< deleter_type >, std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< deleter_type >::type >, detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue >::value >::type diff --git a/test/compile_fail/scope_exit_cond_def_cted_fptr.cpp b/test/compile_fail/scope_exit_cond_def_cted_fptr.cpp new file mode 100644 index 0000000..9e7c553 --- /dev/null +++ b/test/compile_fail/scope_exit_cond_def_cted_fptr.cpp @@ -0,0 +1,25 @@ +/* + * 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 Andrey Semashev + */ +/*! + * \file scope_exit_cond_def_cted_fptr.cpp + * \author Andrey Semashev + * + * \brief This file tests that \c scope_exit with a function pointer + * condition function object cannot be default-constructed. + */ + +#include +#include "function_types.hpp" + +int main() +{ + int n = 0; + boost::scope::scope_exit< normal_func, bool (*)() > guard{ normal_func(n) }; + + return 0; +} diff --git a/test/compile_fail/unique_resource_del_def_cted_fptr.cpp b/test/compile_fail/unique_resource_del_def_cted_fptr.cpp new file mode 100644 index 0000000..6dc391f --- /dev/null +++ b/test/compile_fail/unique_resource_del_def_cted_fptr.cpp @@ -0,0 +1,23 @@ +/* + * 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 Andrey Semashev + */ +/*! + * \file unique_resource_del_def_cted_fptr.cpp + * \author Andrey Semashev + * + * \brief This file tests that \c unique_resource with a function pointer + * deleter cannot be default-constructed. + */ + +#include + +int main() +{ + boost::scope::unique_resource< int, void (*)(int) > ur; + + return 0; +}