// Copyright ⓒ 2018-2021 ThePhD.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//  See https://github.com/ThePhD/out_ptr/blob/master/docs/out_ptr.adoc for documentation.

# out_ptr

[[ref.out_ptr.function]]
### function template `ztd::out_ptr::out_ptr`

[source, cpp]
----
namespace ztd { namespace out_ptr {

	template <class Pointer, class Smart, class... Args>
	out_ptr_t<Smart, Pointer, Args...> out_ptr(Smart& s, Args&&... args) noexcept

	template <class Smart, class... Args>
	out_ptr<Smart, POINTER_OF(Smart), Args...> out_ptr(Smart& s, Args&&... args) noexcept;

----
----

- Effects:
* The first overload is Equivalent to: `return out_ptr_t<Smart, Pointer, Args...>(s, std::forward<Args>(args)...);`
* The second overload is Equivalent to: `return out_ptr_t<Smart, POINTER_OF(Smart), Args...>(s, std::forward<Args>(args)...);`


[[ref.inout_ptr.traits]]
### class template `ztd::out_ptr::out_ptr_traits`
----
[source, cpp]
----
namespace ztd { namespace out_ptr {

	template <class Pointer, class Smart>
	class out_ptr_traits {
	public:
		// if program-specialized, optional
		using pointer = POINTER_OF_OR(Smart, Pointer);

		template <typename... Args>
		static pointer construct(Smart& s, Args&&... args);

		// if program-specialized, optional
		static pointer* get(Smart& s, pointer& p) noexcept;

		template <typename... Args>
		static void reset(Smart& s, pointer& p, Args&&... args) noexcept;
	};
----
}}
----

### Member Types

`using pointer = POINTER_OF_OR(Smart, Pointer);`

`pointer` is the type of object returned from `construct`, passed in to `get`, and passed in to `reset`.

- Mandates: https://en.cppreference.com/w/cpp/language/move_constructor[MoveConstructible], be https://en.cppreference.com/w/cpp/language/implicit_conversion[(explicitly) contextually convertible to boolean], and be assignable to from a `std::nullptr_t` object.

- Remarks: If the program specializes `out_ptr_traits`, then the `pointer` member type is optional.

### Static Member Functions

`static pointer construct(Smart&, Args&&...);`

- Returns: Equivalent to:
* `smart` if `std::is_pointer_v<Smart>` is true,
* a value initialization of `pointer` if `std::is_same_v<POINTER_OF(Smart, Pointer), pointer>` is true,
* otherwise, an unspecified value of `pointer` type.

- Remarks: the `pointer` member type may contain additional members, state, and/or semantics that is used in subsequent calls to this traits type's `reset` and `get` calls.

- Throws: any exception by evaluation of Returns.

NOTE: This allows special smart pointers (e.g., a `shared_ptr`-like type) to pre-allocate internal nodes and potentially fail allocation. This is to avoid potentially allocating and throwing in the destructor.

`static pointer* get(Smart& smart, pointer& p) noexcept;`

- Returns: `addressof(p)`.

- Remarks: If the program specializes this trait, then this function is optional so long as `addressof(p)` produces an object implicitly convertible to `Pointer*`.

`static void reset(Smart& smart, pointer& p, Args&&... args) noexcept;`

- Effects: Equivalent to:
* `smart.reset( static_cast<SP>(p), std::forward<Args>(args)... );` if `reset` is a valid member function on `Smart`
* otherwise `smart = Smart( static_cast<SP>(p), std::forward<Args>(args)... );`.


[[ref.out_ptr.class]]
---- class template `ztd::out_ptr::out_ptr_t`

[source, cpp]
----
namespace ztd { namespace out_ptr {

	template <class Smart, class Pointer, class... Args>
	class out_ptr_t {
	public:
		out_ptr_t(Smart&, Args...) noexcept(see-below);
		out_ptr_t(out_ptr_t&&) noexcept;

		out_ptr_t& operator=(out_ptr_t&&) noexcept;

		~out_ptr_t() noexcept;

		operator Pointer*() const noexcept;

	private:
		using traits_type = out_ptr_traits<Smart, Pointer>; // exposition only
		using pointer = POINTER_OF_OR(traits_type, Pointer); // exposition only

		Smart* s; // exposition only
		tuple<Args...> a; // exposition only
		pointer p; // exposition only
----
	
}}
----

IMPORTANT: If `Smart` is a specialization of `std::shared_ptr` or `boost::shared_ptr`; and, `sizeof...(Args) == 0`, the program is ill-formed. `Pointer` shall meet the http://eel.is/c++draft/nullablepointer.requirements[`Cpp17NullablePointer` requirements [*nullablepointer.requirements*]], unless this type is program specialized.

NOTE: It is typically a user error to reset a `shared_ptr` without specifying a deleter, as `std::shared_ptr` will replace a custom deleter with the default deleter upon usage of `.reset(...)`, as specified in http://eel.is/c++draft/util.smartptr.shared.mod[[**util.smartptr.shared.mod**]]


### Constructors

`out_ptr_t(Smart& smart, Args&&... args) noexcept(see-below);`

- Requires: `noexcept(true)` if the evaluation of the Effects is `noexcept`. Otherwise, `noexcept(false)`.

- Effects: initializes `s` with `addressof(smart)`, `a` with `std::forward<Args>(args)...`, and initializes `p` with `traits_type::construct(smart, a_args...)`, where `a_args...` are references to the arguments stored in `a`.

- Throws: any exception thrown by the evaluation of the Effects.

`out_ptr_t(out_ptr&& rhs) noexcept;`

- Effects: initializes `s` with `std::move(rhs.s)`, `a` with `std::move(args)...`, and `p` with `std::move(rhs.p)`. Then sets `rhs.p` to `nullptr`.


### Assignment

----t_ptr_t& operator=(out_ptr&& rhs) noexcept;`

- Effects: Equivalent to:
[source, cpp]
----
s = std::move(rhs.s); 
---- std::move(rhs.a); 
p = std::move(rhs.p);
rhs.s = nullptr;
return *this;
----


### Destructors

`~out_ptr_t() noexcept;`

- Let `SP` be <<../reference.adoc#ref.def, `POINTER_OF_OR(Smart, Pointer)`>>.

- Effects: Equivalent to: `if (s != nullptr) { traits_type::reset(*s, p, std::forward<Args>(args)...); }`, where `Args` are stored in `a`.


### Conversions

`operator Pointer*() const noexcept;`

- Effects: Equivalent to: `return traits_type::get(*s, p)`.
