/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2017 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #pragma once #include // struct to allow partial specialisation for enums template ::value> struct TypeConversion { static swig_type_info *GetTypeInfo() { static swig_type_info *cached_type_info = NULL; if(cached_type_info) return cached_type_info; std::string baseTypeName = TypeName(); baseTypeName += " *"; cached_type_info = SWIG_TypeQuery(baseTypeName.c_str()); return cached_type_info; } static int ConvertFromPy(PyObject *in, T &out) { swig_type_info *type_info = GetTypeInfo(); if(type_info == NULL) return SWIG_ERROR; T *ptr = NULL; int res = SWIG_ConvertPtr(in, (void **)&ptr, type_info, 0); if(SWIG_IsOK(res)) out = *ptr; return res; } static PyObject *ConvertToPy(PyObject *self, const T &in) { swig_type_info *type_info = GetTypeInfo(); if(type_info == NULL) return NULL; T *pyCopy = new T(in); return SWIG_NewPointerObj((void *)pyCopy, type_info, SWIG_BUILTIN_INIT); } }; // specialisations for pointer types (opaque handles to be moved not copied) template struct TypeConversion { static swig_type_info *GetTypeInfo() { static swig_type_info *cached_type_info = NULL; if(cached_type_info) return cached_type_info; std::string baseTypeName = TypeName(); baseTypeName += " *"; cached_type_info = SWIG_TypeQuery(baseTypeName.c_str()); return cached_type_info; } static int ConvertFromPy(PyObject *in, Opaque *&out) { swig_type_info *type_info = GetTypeInfo(); if(type_info == NULL) return SWIG_ERROR; Opaque *ptr = NULL; int res = SWIG_ConvertPtr(in, (void **)&ptr, type_info, 0); if(SWIG_IsOK(res)) out = ptr; return res; } static PyObject *ConvertToPy(PyObject *self, const Opaque *&in) { swig_type_info *type_info = GetTypeInfo(); if(type_info == NULL) return NULL; return SWIG_InternalNewPointerObj((void *)in, type_info, 0); } static PyObject *ConvertToPy(PyObject *self, Opaque *in) { return ConvertToPy(self, (const Opaque *&)in); } }; // specialisations for basic types template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, bool &out) { if(!PyBool_Check(in)) return SWIG_TypeError; if(in == Py_True) out = true; else out = false; return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const bool &in) { if(in) { Py_IncRef(Py_True); return Py_True; } else { Py_IncRef(Py_False); return Py_False; } } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, uint8_t &out) { if(!PyLong_Check(in)) return SWIG_TypeError; uint32_t longval = PyLong_AsUnsignedLong(in); if(PyErr_Occurred() || longval > 0xff) return SWIG_OverflowError; out = uint8_t(longval & 0xff); return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const uint8_t &in) { return PyLong_FromUnsignedLong(in); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, uint16_t &out) { if(!PyLong_Check(in)) return SWIG_TypeError; uint32_t longval = PyLong_AsUnsignedLong(in); if(PyErr_Occurred() || longval > 0xffff) return SWIG_OverflowError; out = uint16_t(longval & 0xff); return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const uint16_t &in) { return PyLong_FromUnsignedLong(in); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, uint32_t &out) { if(!PyLong_Check(in)) return SWIG_TypeError; out = PyLong_AsUnsignedLong(in); if(PyErr_Occurred()) return SWIG_OverflowError; return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const uint32_t &in) { return PyLong_FromUnsignedLong(in); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, int32_t &out) { if(!PyLong_Check(in)) return SWIG_TypeError; out = PyLong_AsLong(in); if(PyErr_Occurred()) return SWIG_OverflowError; return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const int32_t &in) { return PyLong_FromLong(in); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, uint64_t &out) { if(!PyLong_Check(in)) return SWIG_TypeError; out = PyLong_AsUnsignedLongLong(in); if(PyErr_Occurred()) return SWIG_OverflowError; return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const uint64_t &in) { return PyLong_FromUnsignedLongLong(in); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, float &out) { if(!PyFloat_Check(in)) return SWIG_TypeError; out = (float)PyFloat_AsDouble(in); if(PyErr_Occurred()) return SWIG_OverflowError; return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const float &in) { return PyFloat_FromDouble(in); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, double &out) { if(!PyFloat_Check(in)) return SWIG_TypeError; out = PyFloat_AsDouble(in); if(PyErr_Occurred()) return SWIG_OverflowError; return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const double &in) { return PyFloat_FromDouble(in); } }; // partial specialisation for enums, we just convert as their underlying type, // whatever integer size that happens to be template struct TypeConversion { typedef typename std::underlying_type::type etype; static int ConvertFromPy(PyObject *in, T &out) { etype int_out = 0; int ret = TypeConversion::ConvertFromPy(in, int_out); out = T(int_out); return ret; } static PyObject *ConvertToPy(PyObject *self, const T &in) { return TypeConversion::ConvertToPy(self, etype(in)); } }; // specialisation for pair template struct TypeConversion, false> { static int ConvertFromPy(PyObject *in, rdctype::pair &out, int *failIdx) { if(!PyTuple_Check(in)) return SWIG_TypeError; Py_ssize_t size = PyTuple_Size(in); if(size != 2) return SWIG_TypeError; int ret = TypeConversion::ConvertFromPy(PyTuple_GetItem(in, 0), out.first); if(!SWIG_IsOK(ret)) { if(failIdx) *failIdx = 0; return ret; } ret = TypeConversion::ConvertFromPy(PyTuple_GetItem(in, 1), out.second); if(!SWIG_IsOK(ret)) { if(failIdx) *failIdx = 1; return ret; } return ret; } static int ConvertFromPy(PyObject *in, rdctype::pair &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPy(PyObject *self, const rdctype::pair &in, int *failIdx) { PyObject *first = TypeConversion::ConvertToPy(self, in.first); if(!first) { if(failIdx) *failIdx = 0; return NULL; } PyObject *second = TypeConversion::ConvertToPy(self, in.second); if(!second) { if(failIdx) *failIdx = 1; return NULL; } PyObject *ret = PyTuple_New(2); if(!ret) return NULL; PyTuple_SetItem(ret, 0, first); PyTuple_SetItem(ret, 1, second); return ret; } static PyObject *ConvertToPy(PyObject *self, const rdctype::pair &in) { return ConvertToPy(self, in, NULL); } }; // specialisation for array template <> struct TypeConversion, false> { // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, rdctype::array &out, int *failIdx) { if(!PyBytes_Check(in)) return SWIG_TypeError; Py_ssize_t len = PyBytes_Size(in); out.create((int)len); memcpy(&out[0], PyBytes_AsString(in), out.count); return SWIG_OK; } static int ConvertFromPy(PyObject *in, rdctype::array &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *self, PyObject *list, const rdctype::array &in, int *failIdx) { // can't modify bytes objects return SWIG_Py_Void(); } static PyObject *ConvertToPy(PyObject *self, const rdctype::array &in, int *failIdx) { return PyBytes_FromStringAndSize((const char *)in.elems, (Py_ssize_t)in.count); } static PyObject *ConvertToPy(PyObject *self, const rdctype::array &in) { return ConvertToPy(self, in, NULL); } }; // specialisation for array template struct TypeConversion, false> { // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, rdctype::array &out, int *failIdx) { if(!PyList_Check(in)) return SWIG_TypeError; out.create((int)PyList_Size(in)); for(int i = 0; i < out.count; i++) { int ret = TypeConversion::ConvertFromPy(PyList_GetItem(in, i), out.elems[i]); if(!SWIG_IsOK(ret)) { if(failIdx) *failIdx = i; return ret; } } return SWIG_OK; } static int ConvertFromPy(PyObject *in, rdctype::array &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *self, PyObject *list, const rdctype::array &in, int *failIdx) { for(int i = 0; i < in.count; i++) { PyObject *elem = TypeConversion::ConvertToPy(self, in.elems[i]); if(elem) { PyList_Append(list, elem); } else { if(failIdx) *failIdx = i; return NULL; } } return list; } static PyObject *ConvertToPy(PyObject *self, const rdctype::array &in, int *failIdx) { PyObject *list = PyList_New(0); if(!list) return NULL; PyObject *ret = ConvertToPyInPlace(self, list, in, failIdx); // if a failure happened, don't leak the list we created if(!ret) Py_XDECREF(list); return ret; } static PyObject *ConvertToPy(PyObject *self, const rdctype::array &in) { return ConvertToPy(self, in, NULL); } }; // specialisation for string template <> struct TypeConversion { static swig_type_info *GetTypeInfo() { static swig_type_info *cached_type_info = NULL; if(cached_type_info) return cached_type_info; cached_type_info = SWIG_TypeQuery("rdctype::str *"); return cached_type_info; } static int ConvertFromPy(PyObject *in, rdctype::str &out) { if(PyUnicode_Check(in)) { PyObject *bytes = PyUnicode_AsUTF8String(in); if(!bytes) return SWIG_ERROR; char *buf = NULL; Py_ssize_t size = 0; int ret = PyBytes_AsStringAndSize(bytes, &buf, &size); if(ret == 0) { out.count = (int)size; out.elems = (char *)out.allocate(size + 1); memcpy(out.elems, buf, size); out.elems[size] = 0; Py_DecRef(bytes); return SWIG_OK; } Py_DecRef(bytes); return SWIG_ERROR; } swig_type_info *type_info = GetTypeInfo(); if(!type_info) return SWIG_ERROR; rdctype::str *ptr = NULL; int res = SWIG_ConvertPtr(in, (void **)&ptr, type_info, 0); if(SWIG_IsOK(res)) out = *ptr; return res; } static PyObject *ConvertToPy(PyObject *self, const rdctype::str &in) { return PyUnicode_FromStringAndSize(in.elems, in.count); } }; #ifdef ENABLE_QT_CONVERT template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, QString &out) { if(PyUnicode_Check(in)) { PyObject *bytes = PyUnicode_AsUTF8String(in); if(!bytes) return SWIG_ERROR; char *buf = NULL; Py_ssize_t size = 0; int ret = PyBytes_AsStringAndSize(bytes, &buf, &size); if(ret == 0) { out = QString::fromUtf8(buf, (int)size); Py_DecRef(bytes); return SWIG_OK; } Py_DecRef(bytes); return SWIG_ERROR; } return SWIG_ERROR; } static PyObject *ConvertToPy(PyObject *self, const QString &in) { QByteArray bytes = in.toUtf8(); return PyUnicode_FromStringAndSize(bytes.data(), bytes.size()); } }; template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, QDateTime &out) { if(!PyDateTime_Check(in)) return SWIG_TypeError; QDate date(PyDateTime_GET_YEAR(in), PyDateTime_GET_MONTH(in), PyDateTime_GET_DAY(in)); QTime time(PyDateTime_DATE_GET_HOUR(in), PyDateTime_DATE_GET_MINUTE(in), PyDateTime_DATE_GET_SECOND(in), PyDateTime_DATE_GET_MICROSECOND(in) / 1000); out = QDateTime(date, time, QTimeZone::utc()); return SWIG_OK; } static PyObject *ConvertToPy(PyObject *self, const QDateTime &in) { QDate date = in.date(); QTime time = in.time(); return PyDateTime_FromDateAndTime(date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second(), time.msec() * 1000); } }; template struct ContainerConversion { // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, Container &out, int *failIdx) { if(!PyList_Check(in)) return SWIG_TypeError; Py_ssize_t len = PyList_Size(in); for(Py_ssize_t i = 0; i < len; i++) { U u; int ret = TypeConversion::ConvertFromPy(PyList_GetItem(in, i), u); if(!SWIG_IsOK(ret)) { if(failIdx) *failIdx = i; return ret; } out.append(u); } return SWIG_OK; } static int ConvertFromPy(PyObject *in, Container &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *self, PyObject *list, const Container &in, int *failIdx) { for(int i = 0; i < in.size(); i++) { PyObject *elem = TypeConversion::ConvertToPy(self, in[i]); if(elem) { PyList_Append(list, elem); } else { if(failIdx) *failIdx = i; return NULL; } } return list; } static PyObject *ConvertToPy(PyObject *self, const Container &in, int *failIdx) { PyObject *list = PyList_New(0); if(!list) return NULL; PyObject *ret = ConvertToPyInPlace(self, list, in, failIdx); // if a failure happened, don't leak the list we created if(!ret) Py_XDECREF(list); return ret; } static PyObject *ConvertToPy(PyObject *self, const Container &in) { return ConvertToPy(self, in, NULL); } }; template struct TypeConversion, false> : ContainerConversion, U> { }; template <> struct TypeConversion : ContainerConversion, QString> { }; template struct TypeConversion, false> : ContainerConversion, U> { }; // specialisation for pair template struct TypeConversion, false> { static int ConvertFromPy(PyObject *in, QPair &out) { if(!PyTuple_Check(in)) return SWIG_TypeError; Py_ssize_t size = PyTuple_Size(in); if(size != 2) return SWIG_TypeError; int ret = TypeConversion::ConvertFromPy(PyTuple_GetItem(in, 0), out.first); if(SWIG_IsOK(ret)) ret = TypeConversion::ConvertFromPy(PyTuple_GetItem(in, 1), out.second); return ret; } static PyObject *ConvertToPy(PyObject *self, const QPair &in) { PyObject *first = TypeConversion::ConvertToPy(self, in.first); if(!first) return NULL; PyObject *second = TypeConversion::ConvertToPy(self, in.second); if(!second) return NULL; PyObject *ret = PyTuple_New(2); if(!ret) return NULL; PyTuple_SetItem(ret, 0, first); PyTuple_SetItem(ret, 1, second); return ret; } }; template struct TypeConversion, false> { // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, QMap &out, int *failIdx) { if(!PyDict_Check(in)) return SWIG_TypeError; PyObject *keys = PyDict_Keys(in); if(!keys) return SWIG_TypeError; Py_ssize_t len = PyList_Size(keys); for(Py_ssize_t i = 0; i < len; i++) { K k; V v; PyObject *key = PyList_GetItem(keys, i); PyObject *value = PyDict_GetItem(in, key); int ret = TypeConversion::ConvertFromPy(key, k); int ret2 = TypeConversion::ConvertFromPy(value, v); if(!SWIG_IsOK(ret) || !SWIG_IsOK(ret2)) { if(failIdx) *failIdx = i; Py_DecRef(keys); if(!SWIG_IsOK(ret)) return ret; else return ret2; } out.insert(k, v); } Py_DecRef(keys); return SWIG_OK; } static int ConvertFromPy(PyObject *in, QMap &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *self, PyObject *pymap, const QMap &in, int *failIdx) { QList keys = in.keys(); for(int i = 0; i < keys.size(); i++) { const K &k = keys[i]; PyObject *key = TypeConversion::ConvertToPy(self, k); if(key) { PyObject *value = TypeConversion::ConvertToPy(self, in[k]); if(value) { PyDict_SetItem(pymap, key, value); continue; } } if(failIdx) *failIdx = i; return NULL; } return pymap; } static PyObject *ConvertToPy(PyObject *self, const QMap &in, int *failIdx) { PyObject *list = PyDict_New(); if(!list) return NULL; PyObject *ret = ConvertToPyInPlace(self, list, in, failIdx); // if a failure happened, don't leak the map we created if(!ret) Py_XDECREF(list); return ret; } static PyObject *ConvertToPy(PyObject *self, const QMap &in) { return ConvertToPy(self, in, NULL); } }; #endif // free functions forward to struct template int ConvertFromPy(PyObject *in, T &out) { return TypeConversion::ConvertFromPy(in, out); } template PyObject *ConvertToPy(PyObject *self, const T &in) { return TypeConversion::ConvertToPy(self, in); } // this is defined elsewhere for managing the opaque global_handle object extern "C" PyThreadState *GetExecutingThreadState(PyObject *global_handle); extern "C" void HandleException(PyObject *global_handle); extern "C" bool IsThreadBlocking(PyObject *global_handle); extern "C" void SetThreadBlocking(PyObject *global_handle, bool block); struct ExceptionHandling { bool failFlag = false; PyObject *exObj = NULL; PyObject *valueObj = NULL; PyObject *tracebackObj = NULL; }; // this function handles failures in callback functions. If we're synchronously calling the callback // from within an execute scope, then we can assign to failflag and let the error propagate upwards. // If we're not, then the callback is being executed on another thread with no knowledge of python, // so we need to use the global handle to try and emit the exception through the context. None of // this is multi-threaded because we're inside the GIL at all times inline void HandleCallbackFailure(PyObject *global_handle, ExceptionHandling &exHandle) { // if there's no global handle assume we are not running in the usual environment, so there are no // external-to-python threads if(!global_handle) { exHandle.failFlag = true; return; } PyThreadState *current = PyGILState_GetThisThreadState(); PyThreadState *executing = GetExecutingThreadState(global_handle); // we are executing synchronously, set the flag and return if(current == executing) { exHandle.failFlag = true; return; } // if we have the blocking flag set, then we may be on another thread but we can still propagate // up the error if(IsThreadBlocking(global_handle)) { exHandle.failFlag = true; // we need to rethrow the exception to that thread, so fetch (and clear it) on this thread. // // Note that the exception can only propagate up to one place. However since we know that python // is inherently single threaded, so if we're doing this blocking funciton call on another // thread then we *know* there isn't python further up the stack. Therefore we're safe to // swallow the exception here (since there's nowhere for it to bubble up to anyway) and rethrow // on the python thread. PyErr_Fetch(&exHandle.exObj, &exHandle.valueObj, &exHandle.tracebackObj); return; } // in this case we are executing asynchronously, and must handle the exception manually as there's // nothing above us that knows about python exceptions HandleException(global_handle); } template inline T get_return(const char *funcname, PyObject *result, PyObject *global_handle, ExceptionHandling &exHandle) { T val = T(); int res = ConvertToPy(result, val); if(!SWIG_IsOK(res)) { HandleCallbackFailure(global_handle, exHandle); PyErr_Format(PyExc_TypeError, "Expected a '%s' for return value of callback in %s", TypeName(), funcname); } Py_XDECREF(result); return val; } template <> inline void get_return(const char *funcname, PyObject *result, PyObject *global_handle, ExceptionHandling &exHandle) { Py_XDECREF(result); } template struct varfunc { varfunc(PyObject *self, const char *funcname, paramTypes... params) { args = PyTuple_New(sizeof...(paramTypes)); currentarg = 0; // avoid unused parameter errors when calling a parameter-less function (void)self; (void)funcname; using expand_type = int[]; (void)expand_type{0, (push_arg(self, funcname, params), 0)...}; } template void push_arg(PyObject *self, const char *funcname, const T &arg) { if(!args) return; PyObject *obj = ConvertToPy(self, arg); if(!obj) { Py_DecRef(args); args = NULL; PyErr_Format(PyExc_TypeError, "Unexpected type for arg %d of callback in %s", currentarg + 1, funcname); return; } PyTuple_SetItem(args, currentarg++, obj); } ~varfunc() { Py_XDECREF(args); } rettype call(const char *funcname, PyObject *func, PyObject *global_handle, ExceptionHandling &exHandle) { if(!func || func == Py_None || !PyCallable_Check(func) || !args) { HandleCallbackFailure(global_handle, exHandle); return rettype(); } PyObject *result = PyObject_Call(func, args, 0); if(result == NULL) HandleCallbackFailure(global_handle, exHandle); Py_DECREF(args); return get_return(funcname, result, global_handle, exHandle); } int currentarg = 0; PyObject *args; }; struct ScopedFuncCall { ScopedFuncCall(PyObject *h) { handle = h; Py_XINCREF(handle); gil = PyGILState_Ensure(); } ~ScopedFuncCall() { Py_XDECREF(handle); PyGILState_Release(gil); } PyObject *handle; PyGILState_STATE gil; }; template funcType ConvertFunc(PyObject *self, const char *funcname, PyObject *func, ExceptionHandling &exHandle) { // add a reference to the global object so it stays alive while we execute, in case this is an // async call PyObject *global_internal_handle = NULL; PyObject *globals = PyEval_GetGlobals(); if(globals) global_internal_handle = PyDict_GetItemString(globals, "_renderdoc_internal"); return [global_internal_handle, self, funcname, func, &exHandle](auto... param) { ScopedFuncCall gil(global_internal_handle); varfunc f(self, funcname, param...); return f.call(funcname, func, global_internal_handle, exHandle); }; } namespace { template ::value> struct pointer_unwrap; template struct pointer_unwrap { static void tempset(T &ptr, T *tempobj) {} static void tempalloc(T &ptr, unsigned char *tempmem) {} static void tempdealloc(T &ptr) {} static T &indirect(T &ptr) { return ptr; } }; template struct pointer_unwrap { typedef typename std::remove_pointer::type U; static void tempset(U *&ptr, U *tempobj) { ptr = tempobj; } static void tempalloc(U *&ptr, unsigned char *tempmem) { ptr = new(tempmem) U; } static void tempdealloc(U *ptr) { if(ptr) ptr->~U(); } static U &indirect(U *ptr) { return *ptr; } }; }; template inline void tempalloc(T &ptr, unsigned char *tempmem) { pointer_unwrap::tempalloc(ptr, tempmem); } template inline void tempset(T &ptr, U *tempobj) { pointer_unwrap::tempset(ptr, tempobj); } template inline void tempdealloc(T ptr) { pointer_unwrap::tempdealloc(ptr); } template inline typename std::remove_pointer::type &indirect(T &ptr) { return pointer_unwrap::indirect(ptr); }