-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
86 changed files
with
2,438 additions
and
1,434 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# `jss::object_ptr<T>` | ||
|
||
[![Build Status](https://travis-ci.com/anthonywilliams/object_ptr.svg?branch=master)](https://travis-ci.com/anthonywilliams/object_ptr) | ||
|
||
This is an implementation of a class similar | ||
to [`std::experimental::observer_ptr`](https://en.cppreference.com/w/cpp/experimental/observer_ptr) | ||
from the [Library Fundamentals TS v2](http://wg21.link/n4562), but with various improvements | ||
suggested in WG21 email discussions of the feature. | ||
|
||
## Differences to `std::experimental::observer_ptr` | ||
|
||
The most obvious change is the name: `observer_ptr` is a bad name, because it conjures up the idea | ||
of the Observer pattern, but it doesn't really "observe" anything. I believe `object_ptr` is better: | ||
it is a pointer to an object, so doesn't have any array-related functionality such as pointer | ||
arithmetic, but it doesn't tell you anything about ownership. | ||
|
||
The most important change to functionality is that it has **implicit** conversions from raw | ||
pointers, `std::shared_ptr<T>` and `std::unique_ptr<T>`. This facilitates the use of | ||
`jss::object_ptr<T>` as a drop-in replacement for `T*` in function parameters. There is nothing you | ||
can do with a `jss::object_ptr<T>` that you can't do with a `T*`, and in fact there is considerably | ||
less that you can do. The same applies with `std::shared_ptr<T>` and `std::unique_ptr<T>`: you are | ||
reducing functionality, so this is safe, and reducing typing for safe operations is a good thing. | ||
|
||
The other change is the removal of the `release()` member function. An `object_ptr` doesn't own | ||
anything, so it can't release ownership. To clear it, call `reset()`; if you want the wrapped | ||
pointer, call `get()` first. | ||
|
||
## Examples | ||
|
||
~~~cplusplus | ||
#include "object_ptr.hpp" | ||
#include <iostream> | ||
void foo(jss::object_ptr<int> p) { | ||
if(p) { | ||
std::cout << *p; | ||
} else { | ||
std::cout << "(null)"; | ||
} | ||
std::cout << "\n"; | ||
} | ||
int main() { | ||
foo(nullptr); | ||
int x= 42; | ||
foo(&x); | ||
auto sp= std::make_shared<int>(123); | ||
foo(sp); | ||
auto up= std::make_unique<int>(456); | ||
foo(up); | ||
} | ||
~~~ | ||
|
||
The most common expected use case is as a function parameter, as in the example above. It is similar | ||
to `std::string_view` from C++17 and `std::span` from C++20, in that it provides a view on data | ||
managed elsewhere, and we only care about accessing that data, not how it is managed. It is | ||
therefore ideally suited to places where you know the lifetime of the pointee exceeds the lifetime | ||
of the `object_ptr`. Function arguments typically provide this guarantee, but it may also be used as | ||
a class member where the pointee outlives the new class object, such as where the wrapper class | ||
object is created and destroyed within a scope, and the pointee outlives that scope. | ||
|
||
## License | ||
|
||
This code is released under the [Boost Software License](https://www.boost.org/LICENSE_1_0.txt): | ||
|
||
> Boost Software License - Version 1.0 - August 17th, 2003 | ||
> | ||
> Permission is hereby granted, free of charge, to any person or organization | ||
> obtaining a copy of the software and accompanying documentation covered by | ||
> this license (the "Software") to use, reproduce, display, distribute, | ||
> execute, and transmit the Software, and to prepare derivative works of the | ||
> Software, and to permit third-parties to whom the Software is furnished to | ||
> do so, all subject to the following: | ||
> | ||
> The copyright notices in the Software and this entire statement, including | ||
> the above license grant, this restriction and the following disclaimer, | ||
> must be included in all copies of the Software, in whole or in part, and | ||
> all derivative works of the Software, unless such copies or derivative | ||
> works are solely in the form of machine-executable object code generated by | ||
> a source language processor. | ||
> | ||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
> FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT | ||
> SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE | ||
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, | ||
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
> DEALINGS IN THE SOFTWARE. | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
#ifndef JSS_OBJECT_PTR_HPP | ||
#define JSS_OBJECT_PTR_HPP | ||
#include <cstddef> | ||
#include <utility> | ||
#include <functional> | ||
#include <memory> | ||
#include <type_traits> | ||
|
||
namespace jss { | ||
namespace detail { | ||
/// Detect the presence of operator*, operator-> and .get() for the | ||
/// given type | ||
template <typename Ptr, bool= std::is_class<Ptr>::value> | ||
struct has_smart_pointer_ops { | ||
using false_test= char; | ||
template <typename T> struct true_test { false_test dummy[2]; }; | ||
|
||
template <typename T> static false_test test_op_star(...); | ||
template <typename T> | ||
static true_test<decltype(*std::declval<T const &>())> | ||
test_op_star(T *); | ||
|
||
template <typename T> static false_test test_op_arrow(...); | ||
template <typename T> | ||
static true_test<decltype(std::declval<T const &>().operator->())> | ||
test_op_arrow(T *); | ||
|
||
template <typename T> static false_test test_get(...); | ||
template <typename T> | ||
static true_test<decltype(std::declval<T const &>().get())> | ||
test_get(T *); | ||
|
||
static constexpr bool value= | ||
!std::is_same<decltype(test_get<Ptr>(0)), false_test>::value && | ||
!std::is_same< | ||
decltype(test_op_arrow<Ptr>(0)), false_test>::value && | ||
!std::is_same< | ||
decltype(test_op_star<Ptr>(0)), false_test>::value; | ||
}; | ||
|
||
/// Non-class types can't be smart pointers | ||
template <typename Ptr> | ||
struct has_smart_pointer_ops<Ptr, false> : std::false_type {}; | ||
|
||
/// Ensure that the smart pointer operations give consistent return | ||
/// types | ||
template <typename Ptr> | ||
struct smart_pointer_ops_consistent | ||
: std::integral_constant< | ||
bool, | ||
std::is_pointer<decltype( | ||
std::declval<Ptr const &>().get())>::value && | ||
std::is_reference<decltype( | ||
*std::declval<Ptr const &>())>::value && | ||
std::is_pointer<decltype( | ||
std::declval<Ptr const &>().operator->())>::value && | ||
std::is_same< | ||
decltype(std::declval<Ptr const &>().get()), | ||
decltype(std::declval<Ptr const &>(). | ||
operator->())>::value && | ||
std::is_same< | ||
decltype(*std::declval<Ptr const &>().get()), | ||
decltype(*std::declval<Ptr const &>())>::value> {}; | ||
|
||
/// Assume Ptr is a smart pointer if it has the relevant ops and they | ||
/// are consistent | ||
template <typename Ptr, bool= has_smart_pointer_ops<Ptr>::value> | ||
struct is_smart_pointer | ||
: std::integral_constant< | ||
bool, smart_pointer_ops_consistent<Ptr>::value> {}; | ||
|
||
/// If Ptr doesn't have the relevant ops then it can't be a smart | ||
/// pointer | ||
template <typename Ptr> | ||
struct is_smart_pointer<Ptr, false> : std::false_type {}; | ||
|
||
/// Check if Ptr is a smart pointer that holds a pointer convertible to | ||
/// T* | ||
template <typename Ptr, typename T, bool= is_smart_pointer<Ptr>::value> | ||
struct is_convertible_smart_pointer | ||
: std::integral_constant< | ||
bool, std::is_convertible< | ||
decltype(std::declval<Ptr const &>().get()), | ||
T *>::value> {}; | ||
|
||
/// If Ptr isn't a smart pointer then we don't want it | ||
template <typename Ptr, typename T> | ||
struct is_convertible_smart_pointer<Ptr, T, false> : std::false_type {}; | ||
|
||
} | ||
|
||
/// A basic "smart" pointer that points to an individual object it does not | ||
/// own. Unlike a raw pointer, it does not support pointer arithmetic or | ||
/// array operations, and the pointee cannot be accidentally deleted. It | ||
/// supports implicit conversion from any smart pointer that holds a pointer | ||
/// convertible to T* | ||
template <typename T> class object_ptr { | ||
public: | ||
/// Construct a null pointer | ||
constexpr object_ptr() noexcept : ptr(nullptr) {} | ||
/// Construct a null pointer | ||
constexpr object_ptr(std::nullptr_t) noexcept : ptr(nullptr) {} | ||
/// Construct an object_ptr from a raw pointer | ||
constexpr object_ptr(T *ptr_) noexcept : ptr(ptr_) {} | ||
/// Construct an object_ptr from a raw pointer convertible to T*, such | ||
/// as BaseOfT* | ||
template < | ||
typename U, | ||
typename= std::enable_if_t<std::is_convertible<U *, T *>::value>> | ||
constexpr object_ptr(U *ptr_) noexcept : ptr(ptr_) {} | ||
/// Construct an object_ptr from a smart pointer that holds a pointer | ||
/// convertible to T*, | ||
/// such as shared_ptr<T> or unique_ptr<BaseOfT> | ||
template < | ||
typename Ptr, | ||
typename= std::enable_if_t< | ||
detail::is_convertible_smart_pointer<Ptr, T>::value>> | ||
constexpr object_ptr(Ptr const &other) noexcept : ptr(other.get()) {} | ||
|
||
/// Get the raw pointer value | ||
constexpr T *get() const noexcept { | ||
return ptr; | ||
} | ||
|
||
/// Dereference the pointer | ||
constexpr T &operator*() const noexcept { | ||
return *ptr; | ||
} | ||
|
||
/// Dereference the pointer for ptr->m usage | ||
constexpr T *operator->() const noexcept { | ||
return ptr; | ||
} | ||
|
||
/// Allow if(ptr) to test for null | ||
constexpr explicit operator bool() const noexcept { | ||
return ptr != nullptr; | ||
} | ||
|
||
/// Convert to a raw pointer where necessary | ||
constexpr explicit operator T *() const noexcept { | ||
return ptr; | ||
} | ||
|
||
/// !ptr is true iff ptr is null | ||
constexpr bool operator!() const noexcept { | ||
return !ptr; | ||
} | ||
|
||
/// Change the value | ||
void reset(T *ptr_= nullptr) noexcept { | ||
ptr= ptr_; | ||
} | ||
|
||
/// Check for equality | ||
friend constexpr bool | ||
operator==(object_ptr const &lhs, object_ptr const &rhs) noexcept { | ||
return lhs.ptr == rhs.ptr; | ||
} | ||
|
||
/// Check for inequality | ||
friend constexpr bool | ||
operator!=(object_ptr const &lhs, object_ptr const &rhs) noexcept { | ||
return !(lhs == rhs); | ||
} | ||
|
||
/// a<b provides a total order | ||
friend constexpr bool | ||
operator<(object_ptr const &lhs, object_ptr const &rhs) noexcept { | ||
return std::less<void>()(lhs.ptr, rhs.ptr); | ||
} | ||
/// a>b is b<a | ||
friend constexpr bool | ||
operator>(object_ptr const &lhs, object_ptr const &rhs) noexcept { | ||
return rhs < lhs; | ||
} | ||
/// a<=b is !(b<a) | ||
friend constexpr bool | ||
operator<=(object_ptr const &lhs, object_ptr const &rhs) noexcept { | ||
return !(rhs < lhs); | ||
} | ||
/// a<=b is b<=a | ||
friend constexpr bool | ||
operator>=(object_ptr const &lhs, object_ptr const &rhs) noexcept { | ||
return rhs <= lhs; | ||
} | ||
|
||
protected: | ||
/// The stored pointer | ||
T *ptr; | ||
}; | ||
|
||
} | ||
|
||
namespace std { | ||
/// Allow hashing object_ptrs so they can be used as keys in unordered_map | ||
template <typename T> struct hash<jss::object_ptr<T>> { | ||
constexpr size_t operator()(jss::object_ptr<T> const &p) const | ||
noexcept { | ||
return hash<T *>()(p.get()); | ||
} | ||
}; | ||
|
||
/// Do a static_cast with object_ptr | ||
template <typename To, typename From> | ||
typename std::enable_if< | ||
sizeof(decltype(static_cast<To *>(std::declval<From *>()))) != 0, | ||
jss::object_ptr<To>>::type | ||
static_pointer_cast(jss::object_ptr<From> p) { | ||
return static_cast<To *>(p.get()); | ||
} | ||
|
||
/// Do a dynamic_cast with object_ptr | ||
template <typename To, typename From> | ||
typename std::enable_if< | ||
sizeof(decltype(dynamic_cast<To *>(std::declval<From *>()))) != 0, | ||
jss::object_ptr<To>>::type | ||
dynamic_pointer_cast(jss::object_ptr<From> p) { | ||
return dynamic_cast<To *>(p.get()); | ||
} | ||
|
||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.