// 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.

# inout_ptr

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

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

	template <class Pointer, class Smart, class... Args>
	inout_ptr_t<Smart, Pointer, Args...> inout_ptr(Smart& s, Args&&... args) noexcept;

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

----
----

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


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

	template <class Pointer, class Smart>
	class inout_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

- Mandates: `pointer` be http://eel.is/c++draft/concept.moveconstructible[MoveConstructible].

### Static Member Functions

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

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

NOTE: An unspecified value allows an implementation and subsequent program-defined specializations to pick an option which fits an implementation's purpose. ``ztd::out_ptr::inout_ptr_t``s implementation behaves as if `s->release()` is called in the destructor/`reset`, specifically to avoid the problem raised by <<../caveats.adoc#caveat.order, Order of Evaluation>> issues in {cpp}.

- Remarks: if an implementation calls `smart.release()` in this function, then it shall not call `smart.release()` in the `reset` function of this type. The `pointer` member type may contain additional state, members and/or semantics that is used in subsequent calls to this trait type's `reset` and `get` calls.

- Throws: any exception by the evaluation of the 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 in the destructor.

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

- Returns: `addressof(p)`.

- Remarks: If the program specializes this trait, then the `get` 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;`

- Constraints: Either `std::is_pointer_v<Smart>` is `true` or the expression `smart.release()` is well-formed.

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

- Effects: Equivalent to:
* `smart = Smart( static_cast<SP>(p), std::forward<Args>(args)... );` if `std::is_pointer_v<Smart>` is true,
* `=smart.release(); smart.reset( static_cast<SP>(p), std::forward<Args>(args)... );` if the expression `smart.reset(static_cast<SP>(p), std::forward<Args>(args)...)` is well-formed,
* otherwise, `smart.release(); *s = Smart( static_cast<SP>(p), std::forward<Args>(args)... );`.

- Remarks: if an implementation calls `smart.release()` in this function, then it shall not call `smart.release()` in the `construct` function of this type.


[[ref.inout_ptr.class]]
---- `ztd::out_ptr::inout_ptr_t`

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

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

		inout_ptr_t& operator=(inout_ptr_t&&) noexcept;

		~inout_ptr_t() noexcept;

		operator Pointer const*() noexcept const;

	private:
		using traits_type = inout_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`, 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 impossible to properly release the managed resource from a `std::shared_ptr` or `boost::shared_ptr` given its shared ownership model.


### Constructors

`inout_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 `p` with `inout_ptr_traits<Smart>::construct(s, p, a_args...)`, where `a_args...` are references to the arguments stored in `a`.

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

`inout_ptr_t(inout_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

----out_ptr_t& operator=(inout_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

`~inout_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)`.
