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

[[examples]]
# Examples

Here are a few examples of the more powerful features of `ztd::out_ptr::out_ptr` and `ztd::out_ptr::inout_ptr`.

[[examples.size_create]]
## Parameters with Size

Sometimes, you are working with a C API that requires you to handle both the pointer and some other parameters. It would be useful if we could take the following:

.out_ptr.createbus.raw
[source,cpp]
----
Interface* obj;
size_t obj_size;
int ret = CreateBusInterface(&obj, &obj_size);
if (ret != 0) {
	throw std::runtime_error("failed to create the bus interface");
}
/* use obj... */
DeleteBusInterface(obj, obj_size);
----

And turn it into something that would be usable with C++ smart pointers. With ztd.out_ptr, interfacing with this is fairly okay to do:

.out_ptr.createbus
[source,cpp]
----
struct SizedDeleter {
    size_t size = 0;
    
    void operator()(Interface* obj) const {
	    DeleteBusInterface(obj, size);
    }
};

using zop = ztd::out_ptr;

size_t& size_ref = p_obj.get_deleter().size;
std::unique_ptr<Interface, SizedDeleter> obj;
int ret = CreateBusInterface(..., zop::out_ptr(obj), &size_ref);
if (ret != 0) {
     throw std::runtime_error("deep sadness");
}
/* use obj ...*/
----

Notice how with the `std::uniqe_ptr` there is now complete safety. If an error is thrown in the space of the `use_obj ...` comment then the unique_ptr's destructor will take care of it. We also get the benefit of having a nice way to push the pointer into the `unique_ptr` without needing to maintain a raw pointer and maybe do it ourselves later, manually.


[[examples.posix_memalign]]
## With posix_memalign

Normally, one could use POSIX's memalign function like so:

.out_ptr.posix_memalign.raw
[source,cpp]
----
std::unique_ptr<Numf, DoFree> upNumf;
/* some work... */
void* pNumf;
int ret = posix_memalign(&pNumf, 64, nNumf);
if (ret != 0) {
     throw std::runtime_error("something went wrong and posix_memalign failed!");
}
upNumf.reset((Numf*)pNumf);
----

.out_ptr.posix_memalign
[source,cpp]
----
using zop = ztd::out_ptr;

std::unique_ptr<Numf, DoFree> upNumf;
/* some work... */
int ret = posix_memalign(zop::out_ptr<void*>(upNumf), 64, nNumf);
if (ret != 0) {
     throw std::runtime_error("something went wrong and posix_memalign failed!");
}
----


## IntrusivePtr with COM and base-class / void** with Casting Support

`ztd::out_ptr::out_ptr` and `ztd::out_ptr::inout_ptr` work with more than just the typical standard pointers. They also know how to interop with legacy codebases which communicate through base class ``IUnknown**`` or ``void**``!

You can add a template parameter `T` in an `ztd::out_ptr::out_ptr` call to cast to a specific type, where `T` can be `HANDLE`, `int*`, `void*` and more:

ifdef::env-github[]
link:../../examples/source/com.intrusive_ptr.cpp[com.intrusive_ptr.cpp]
endif::[]
ifndef::env-github[]
[source,cpp,linenums]
----
include::../../examples/source/com.intrusive_ptr.cpp[lines=8..-1]
----
endif::[]

Notice how an extra parameter gets passed to the function:

ifdef::env-github[]
[source, cpp]
----
	HRESULT cci_result = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER,
----_uuidof(IDispatch), ztd::out_ptr::out_ptr<void*>(dispatch_ptr, false));
----
endif::[]
ifndef::env-github[]
[source,cpp,linenums]
----
include::../../examples/source/com.intrusive_ptr.cpp[lines=34..35]
----
endif::[]

What this does is forward the `false` to ``intrusive_ptr``'s `reset(T* ptr, bool addRef)` call. You can invoke any `.reset()` with additional parameters so that you can work with any pointer type that takes `.reset(new_ptr, args...)`. Pass in as many `args` as you like to make the call work out!


## A `pthread` example

And for a Linux example, observe how you can pick up the ``new``ly created value after the `pthread` exits:

ifdef::env-github[]
link:../../examples/source/pthread.cpp[pthread.cpp]
endif::[]
ifndef::env-github[]
[source,cpp,linenums]
----
include::../../examples/source/pthread.cpp[lines=8..-1]
----
endif::[]


## Prevents errors with `shared_ptr` resetting the deleter.

If you have ever used any `shared_ptr` similar to the one in `boost` or the `std`, you'll know that if you call `my_shared_ptr.reset(some_raw_pointer)`, it will erase the old deleter and automatically place in the `std::default_delete<T>` (or similar). ztd.out_ptr prevents that from happening by giving a strong, compile-time warning when you forget:

ifdef::env-github[]
link:../../examples/source/std.shared_ptr.cpp[std.shared_ptr.cpp]
endif::[]
ifndef::env-github[]
[source,cpp,linenums]
----
include::../../examples/source/std.shared_ptr.cpp[lines=8..-1]
----
endif::[]

This prevents you from making mistakes in your code and having strange non-delete bugs later on with your smart pointers.


## Customizing for your own pointer type

There is a whole section dedicated to customization, with its own example. See the <<customization.adoc#customization, customization documentation>> for more details.
