blob: 35e29088b869d348ea41d1cf985fdb883eccf222 [file] [log] [blame]
// Copyright 2023 The Cobalt Authors. All Rights Reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Scopers help you manage ownership of a pointer, helping you easily manage the
// a pointer within a scope, and automatically destroying the pointer at the
// end of a scope. There are two main classes you will use, which correspond
// to the operators new/delete and new[]/delete[].
//
// Example usage (scoped_ptr):
// {
// scoped_ptr<Foo> foo(new Foo("whee"));
// } // foo goes out of scope, releasing the pointer with it.
//
// {
// scoped_ptr<Foo> foo; // No pointer managed.
// foo.reset(new Foo("whee")); // Now a pointer is managed.
// foo.reset(new Foo("whee2")); // Foo("whee") was destroyed.
// foo.reset(new Foo("whee3")); // Foo("whee2") was destroyed.
// foo->Method(); // Foo::Method() called.
// foo.get()->Method(); // Foo::Method() called.
// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer
// // manages a pointer.
// foo.reset(new Foo("whee4")); // foo manages a pointer again.
// foo.reset(); // Foo("whee4") destroyed, foo no longer
// // manages a pointer.
// } // foo wasn't managing a pointer, so nothing was destroyed.
//
// Example usage (scoped_array):
// {
// scoped_array<Foo> foo(new Foo[100]);
// foo.get()->Method(); // Foo::Method on the 0th element.
// foo[10].Method(); // Foo::Method on the 10th element.
// }
//
// These scopers also implement part of the functionality of C++11 unique_ptr
// in that they are "movable but not copyable." You can use the scopers in
// the parameter and return types of functions to signify ownership transfer
// in to and out of a function. When calling a function that has a scoper
// as the argument type, it must be called with the result of an analogous
// scoper's Pass() function or another function that generates a temporary;
// passing by copy will NOT work. Here is an example using scoped_ptr:
//
// void TakesOwnership(scoped_ptr<Foo> arg) {
// // Do something with arg
// }
// scoped_ptr<Foo> CreateFoo() {
// // No need for calling Pass() because we are constructing a temporary
// // for the return value.
// return scoped_ptr<Foo>(new Foo("new"));
// }
// scoped_ptr<Foo> PassThru(scoped_ptr<Foo> arg) {
// return arg.Pass();
// }
//
// {
// scoped_ptr<Foo> ptr(new Foo("yay")); // ptr manages Foo("yay").
// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay").
// scoped_ptr<Foo> ptr2 = CreateFoo(); // ptr2 owns the return Foo.
// scoped_ptr<Foo> ptr3 = // ptr3 now owns what was in ptr2.
// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL.
// }
//
// Notice that if you do not call Pass() when returning from PassThru(), or
// when invoking TakesOwnership(), the code will not compile because scopers
// are not copyable; they only implement move semantics which require calling
// the Pass() function to signify a destructive transfer of state. CreateFoo()
// is different though because we are constructing a temporary on the return
// line and thus can avoid needing to call Pass().
//
// Pass() properly handles upcast in assignment, i.e. you can assign
// scoped_ptr<Child> to scoped_ptr<Parent>:
//
// scoped_ptr<Foo> foo(new Foo());
// scoped_ptr<FooParent> parent = foo.Pass();
//
// PassAs<>() should be used to upcast return value in return statement:
//
// scoped_ptr<Foo> CreateFoo() {
// scoped_ptr<FooChild> result(new FooChild());
// return result.PassAs<Foo>();
// }
//
// Note that PassAs<>() is implemented only for scoped_ptr, but not for
// scoped_array. This is because casting array pointers may not be safe.
#ifndef STARBOARD_COMMON_SCOPED_PTR_H_
#define STARBOARD_COMMON_SCOPED_PTR_H_
// This is an implementation designed to match the anticipated future TR2
// implementation of the scoped_ptr class, and its closely-related brethren,
// scoped_array, scoped_ptr_malloc.
#include <algorithm>
#include <utility>
#include "starboard/common/log.h"
#include "starboard/common/move.h"
#include "starboard/memory.h"
#include "starboard/types.h"
namespace starboard {
// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
// automatically deletes the pointer it holds (if any).
// That is, scoped_ptr<T> owns the T object that it points to.
// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
// Also like T*, scoped_ptr<T> is thread-compatible, and once you
// dereference it, you get the thread safety guarantees of T.
//
// The size of a scoped_ptr is small:
// sizeof(scoped_ptr<C>) == sizeof(C*)
template <class C>
class scoped_ptr {
MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
public:
// The element type
typedef C element_type;
// Constructor. Defaults to initializing with NULL.
// There is no way to create an uninitialized scoped_ptr.
// The input parameter must be allocated with new.
explicit scoped_ptr(C* p = NULL) : ptr_(p) {}
// The GHS compiler always chooses this copy constructor over the next one,
// so disable this to promote the more important and frequently used constr.
#if !defined(COMPILER_GHS)
// Constructor. Allows construction from a scoped_ptr rvalue for a
// convertible type.
template <typename U>
scoped_ptr(scoped_ptr<U> other) // NOLINT(runtime/explicit)
: ptr_(other.release()) {}
#endif
// Constructor. Move constructor for C++03 move emulation of this type.
scoped_ptr(RValue rvalue) // NOLINT(runtime/explicit)
: ptr_(rvalue.object->release()) {}
// Destructor. If there is a C object, delete it.
// We don't need to test ptr_ == NULL because C++ does that for us.
~scoped_ptr() {
enum { type_must_be_complete = sizeof(C) };
delete ptr_;
}
// operator=. Allows assignment from a scoped_ptr rvalue for a convertible
// type.
template <typename U>
scoped_ptr& operator=(scoped_ptr<U> rhs) {
reset(rhs.release());
return *this;
}
// operator=. Move operator= for C++03 move emulation of this type.
scoped_ptr& operator=(RValue rhs) {
swap(*rhs->object);
return *this;
}
// Reset. Deletes the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
void reset(C* p = NULL) {
if (p != ptr_) {
enum { type_must_be_complete = sizeof(C) };
delete ptr_;
ptr_ = p;
}
}
// Accessors to get the owned object.
// operator* and operator-> will SB_DCHECK() if there is no current object.
C& operator*() const {
SB_DCHECK(ptr_ != NULL);
return *ptr_;
}
C* operator->() const {
SB_DCHECK(ptr_ != NULL);
return ptr_;
}
C* get() const { return ptr_; }
// Allow scoped_ptr<C> to be used in boolean expressions, but not
// implicitly convertible to a real bool (which is dangerous).
typedef C* scoped_ptr::*Testable;
operator Testable() const { return ptr_ ? &scoped_ptr::ptr_ : NULL; }
// Comparison operators.
// These return whether two scoped_ptr refer to the same object, not just to
// two different but equal objects.
bool operator==(C* p) const { return ptr_ == p; }
bool operator!=(C* p) const { return ptr_ != p; }
// Swap two scoped pointers.
void swap(scoped_ptr& p2) {
C* tmp = ptr_;
ptr_ = p2.ptr_;
p2.ptr_ = tmp;
}
// Release a pointer.
// The return value is the current pointer held by this object.
// If this object holds a NULL pointer, the return value is NULL.
// After this operation, this object will hold a NULL pointer,
// and will not own the object any more.
C* release() {
C* retVal = ptr_;
ptr_ = NULL;
return retVal;
}
template <typename PassAsType>
scoped_ptr<PassAsType> PassAs() {
return scoped_ptr<PassAsType>(release());
}
private:
C* ptr_;
// Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
// make sense, and if C2 == C, it still doesn't make sense because you should
// never have the same object owned by two different scoped_ptrs.
template <class C2>
bool operator==(scoped_ptr<C2> const& p2) const;
template <class C2>
bool operator!=(scoped_ptr<C2> const& p2) const;
};
// Free functions
template <class C>
void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
p1.swap(p2);
}
template <class C>
bool operator==(C* p1, const scoped_ptr<C>& p2) {
return p1 == p2.get();
}
template <class C>
bool operator!=(C* p1, const scoped_ptr<C>& p2) {
return p1 != p2.get();
}
// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
// with new [] and the destructor deletes objects with delete [].
//
// As with scoped_ptr<C>, a scoped_array<C> either points to an object
// or is NULL. A scoped_array<C> owns the object that it points to.
// scoped_array<T> is thread-compatible, and once you index into it,
// the returned objects have only the thread safety guarantees of T.
//
// Size: sizeof(scoped_array<C>) == sizeof(C*)
template <class C>
class scoped_array {
MOVE_ONLY_TYPE_FOR_CPP_03(scoped_array, RValue)
public:
// The element type
typedef C element_type;
// Constructor. Defaults to initializing with NULL.
// There is no way to create an uninitialized scoped_array.
// The input parameter must be allocated with new [].
explicit scoped_array(C* p = NULL) : array_(p) {}
// Constructor. Move constructor for C++03 move emulation of this type.
scoped_array(RValue rvalue) // NOLINT(runtime/explicit)
: array_(rvalue.object->release()) {}
// Destructor. If there is a C object, delete it.
// We don't need to test ptr_ == NULL because C++ does that for us.
~scoped_array() {
enum { type_must_be_complete = sizeof(C) };
delete[] array_;
}
// operator=. Move operator= for C++03 move emulation of this type.
scoped_array& operator=(RValue rhs) {
swap(*rhs.object);
return *this;
}
// Reset. Deletes the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
void reset(C* p = NULL) {
if (p != array_) {
enum { type_must_be_complete = sizeof(C) };
delete[] array_;
array_ = p;
}
}
// Get one element of the current object.
// Will SB_DCHECK() if there is no current object, or index i is negative.
C& operator[](ptrdiff_t i) const {
SB_DCHECK(i >= 0);
SB_DCHECK(array_ != NULL);
return array_[i];
}
// Get a pointer to the zeroth element of the current object.
// If there is no current object, return NULL.
C* get() const { return array_; }
// Allow scoped_array<C> to be used in boolean expressions, but not
// implicitly convertible to a real bool (which is dangerous).
typedef C* scoped_array::*Testable;
operator Testable() const { return array_ ? &scoped_array::array_ : NULL; }
// Comparison operators.
// These return whether two scoped_array refer to the same object, not just to
// two different but equal objects.
bool operator==(C* p) const { return array_ == p; }
bool operator!=(C* p) const { return array_ != p; }
// Swap two scoped arrays.
void swap(scoped_array& p2) {
C* tmp = array_;
array_ = p2.array_;
p2.array_ = tmp;
}
// Release an array.
// The return value is the current pointer held by this object.
// If this object holds a NULL pointer, the return value is NULL.
// After this operation, this object will hold a NULL pointer,
// and will not own the object any more.
C* release() {
C* retVal = array_;
array_ = NULL;
return retVal;
}
private:
C* array_;
// Forbid comparison of different scoped_array types.
template <class C2>
bool operator==(scoped_array<C2> const& p2) const;
template <class C2>
bool operator!=(scoped_array<C2> const& p2) const;
};
// Free functions
template <class C>
void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
p1.swap(p2);
}
template <class C>
bool operator==(C* p1, const scoped_array<C>& p2) {
return p1 == p2.get();
}
template <class C>
bool operator!=(C* p1, const scoped_array<C>& p2) {
return p1 != p2.get();
}
// This class wraps the memory deallocation function in a class that can be
// passed as a template argument to scoped_ptr_malloc below.
class ScopedPtrMallocFree {
public:
inline void operator()(void* x) const { free(x); }
};
// This class wraps the memory deallocation function in a class that
// can be passed as a template argument to scoped_ptr_malloc below.
class ScopedPtrMallocFreeAligned {
public:
inline void operator()(void* x) const { free(x); }
};
// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
// second template argument, the functor used to free the object.
template <class C, class FreeProc = ScopedPtrMallocFree>
class scoped_ptr_malloc {
MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr_malloc, RValue)
public:
// The element type
typedef C element_type;
// Constructor. Defaults to initializing with NULL.
// There is no way to create an uninitialized scoped_ptr.
// The input parameter must be allocated with an allocator that matches the
// Free functor. For the default Free functor, this is malloc, calloc, or
// realloc.
explicit scoped_ptr_malloc(C* p = NULL) : ptr_(p) {}
// Constructor. Move constructor for C++03 move emulation of this type.
scoped_ptr_malloc(RValue rvalue) // NOLINT(runtime/explicit)
: ptr_(rvalue.object->release()) {}
// Destructor. If there is a C object, call the Free functor.
~scoped_ptr_malloc() { reset(); }
// operator=. Move operator= for C++03 move emulation of this type.
scoped_ptr_malloc& operator=(RValue rhs) {
swap(*rhs.object);
return *this;
}
// Reset. Calls the Free functor on the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
void reset(C* p = NULL) {
if (ptr_ != p) {
FreeProc free_proc;
free_proc(ptr_);
ptr_ = p;
}
}
// Get the current object.
// operator* and operator-> will cause an SB_DCHECK() failure if there is
// no current object.
C& operator*() const {
SB_DCHECK(ptr_ != NULL);
return *ptr_;
}
C* operator->() const {
SB_DCHECK(ptr_ != NULL);
return ptr_;
}
C* get() const { return ptr_; }
// Allow scoped_ptr_malloc<C> to be used in boolean expressions, but not
// implicitly convertible to a real bool (which is dangerous).
typedef C* scoped_ptr_malloc::*Testable;
operator Testable() const { return ptr_ ? &scoped_ptr_malloc::ptr_ : NULL; }
// Comparison operators.
// These return whether a scoped_ptr_malloc and a plain pointer refer
// to the same object, not just to two different but equal objects.
// For compatibility with the boost-derived implementation, these
// take non-const arguments.
bool operator==(C* p) const { return ptr_ == p; }
bool operator!=(C* p) const { return ptr_ != p; }
// Swap two scoped pointers.
void swap(scoped_ptr_malloc& b) {
C* tmp = b.ptr_;
b.ptr_ = ptr_;
ptr_ = tmp;
}
// Release a pointer.
// The return value is the current pointer held by this object.
// If this object holds a NULL pointer, the return value is NULL.
// After this operation, this object will hold a NULL pointer,
// and will not own the object any more.
C* release() {
C* tmp = ptr_;
ptr_ = NULL;
return tmp;
}
private:
C* ptr_;
// no reason to use these: each scoped_ptr_malloc should have its own object
template <class C2, class GP>
bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
template <class C2, class GP>
bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
};
template <class C, class FP>
inline void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
a.swap(b);
}
template <class C, class FP>
inline bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
return p == b.get();
}
template <class C, class FP>
inline bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
return p != b.get();
}
// A function to convert T* into scoped_ptr<T>
// Doing e.g. make_scoped_ptr(new FooBarBaz<type>(arg)) is a shorter notation
// for scoped_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
template <typename T>
scoped_ptr<T> make_scoped_ptr(T* ptr) {
return scoped_ptr<T>(ptr);
}
} // namespace starboard
#endif // STARBOARD_COMMON_SCOPED_PTR_H_