// Copyright (c) 2015 - 2022 kio@little-bat.de
// BSD-2-Clause license
// https://opensource.org/licenses/BSD-2-Clause

#pragma once
#include <memory>

enum EnumTRY { TRY };

template <class T> inline T* nv (T volatile* p) { return const_cast<T*>(p); }
template <class T> inline T* nv (T volatile& p) { return const_cast<T*>(&p); }

template <class T> inline T* nv (const std::shared_ptr<volatile T> p)
{
	return const_cast<T*>(p.get());
}


template <class T> class NVPtr
{
	// While alive, NVPtr locks a volatile object and provides non-volatile access
	// the object must implement functions lock() and unlock() and, if TRY is used, also try_lock().

	T* ptr;

public:
	NVPtr (T volatile* p)			: ptr(nv(p))  { if (ptr) ptr->lock(); }
	NVPtr (T volatile* p, EnumTRY)	: ptr(p && nv(p)->try_lock() ? nv(p) : nullptr) {}
	~NVPtr () { if (ptr) ptr->unlock(); }

	NVPtr (const NVPtr&) = delete;
	NVPtr (NVPtr&& q) noexcept : ptr(q.ptr) { q.ptr = nullptr; }
	NVPtr& operator= (NVPtr&& q) noexcept { std::swap(ptr,q.ptr); }

	T* operator-> () const noexcept { return ptr; }

	operator bool () const { return ptr; }
};


// helper:
// usage:  nvptr(my_vol_obj_ptr)->call_non_vol_member_function();
//
template <class T> NVPtr<T> nvptr (T volatile* o) { return NVPtr<T>(o); }
template <class T> NVPtr<T> nvptr (T volatile* o, EnumTRY) { return NVPtr<T>(o,TRY); }

template <class T> NVPtr<T> nvptr (T volatile& o) { return NVPtr<T>(&o); }
template <class T> NVPtr<T> nvptr (T volatile& o, EnumTRY) { return NVPtr<T>(&o,TRY); }

template <class T> NVPtr<T> nvptr (const std::shared_ptr<volatile T>& o)
{
	return NVPtr<T>(o.get());
}













