diff --git a/qrenderdoc/Code/pyrenderdoc/pyconversion.h b/qrenderdoc/Code/pyrenderdoc/pyconversion.h index 7b51e9ad9..e46fe603f 100644 --- a/qrenderdoc/Code/pyrenderdoc/pyconversion.h +++ b/qrenderdoc/Code/pyrenderdoc/pyconversion.h @@ -65,11 +65,55 @@ struct TypeConversion return NULL; T *pyCopy = new T(in); - return SWIG_InternalNewPointerObj((void *)pyCopy, type_info, 0); + return SWIG_InternalNewPointerObj((void *)pyCopy, type_info, SWIG_BUILTIN_INIT); } }; -// specialisations for integers +// 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 Convert(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 *Convert(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 *Convert(Opaque *in) { return Convert((const Opaque *&)in); } +}; + +// specialisations for basic types template <> struct TypeConversion { @@ -148,6 +192,44 @@ struct TypeConversion static PyObject *Convert(const uint64_t &in) { return PyLong_FromUnsignedLongLong(in); } }; +template <> +struct TypeConversion +{ + static int Convert(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 *Convert(const float &in) { return PyFloat_FromDouble(in); } +}; + +template <> +struct TypeConversion +{ + static int Convert(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 *Convert(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 @@ -345,4 +427,97 @@ template PyObject *Convert(const T &in) { return TypeConversion::Convert(in); -} \ No newline at end of file +} + +template +T get_return(const char *funcname, PyObject *result, bool &failflag) +{ + T val = T(); + + int res = Convert(result, val); + + if(!SWIG_IsOK(res)) + { + failflag = true; + + PyErr_Format(PyExc_TypeError, "Expected a '%s' for return value of callback in %s", + TypeName(), funcname); + } + + Py_XDECREF(result); + + return val; +} + +template <> +void get_return(const char *funcname, PyObject *result, bool &failflag) +{ + Py_XDECREF(result); +} + +template +struct varfunc +{ + varfunc(const char *funcname, paramTypes... params) + { + args = PyTuple_New(sizeof...(paramTypes)); + + currentarg = 0; + + using expand_type = int[]; + (void)expand_type{0, (push_arg(funcname, params), 0)...}; + } + + template + void push_arg(const char *funcname, const T &arg) + { + if(!args) + return; + + PyObject *obj = Convert(arg); + + if(!obj) + { + Py_DecRef(args); + args = NULL; + + PyErr_Format(PyExc_TypeError, "Expected a '%s' for arg %d of callback in %s", + TypeName::type>(), currentarg + 1, funcname); + + return; + } + + PyTuple_SetItem(args, currentarg++, obj); + } + + ~varfunc() { Py_XDECREF(args); } + rettype call(const char *funcname, PyObject *func, bool &failflag) + { + if(!func || func == Py_None || !PyCallable_Check(func) || !args) + { + failflag = true; + return rettype(); + } + + PyObject *result = PyObject_Call(func, args, 0); + + if(result == NULL) + failflag = true; + + Py_DECREF(args); + + return get_return(funcname, result, failflag); + } + + int currentarg = 0; + PyObject *args; +}; + +template +funcType ConvertFunc(const char *funcname, PyObject *func, bool &failflag) +{ + return [funcname, func, &failflag](auto... param) { + varfunc f(funcname, param...); + return f.call(funcname, func, failflag); + }; +} diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 5e5a66681..d9a592f82 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -220,6 +220,16 @@ $result = Convert($1); } +%typemap(in, fragment="pyconvert") std::function { + PyObject *func = $input; + failed$argnum = false; + $1 = ConvertFunc<$1_ltype>("$symname", func, failed$argnum); +} + +%typemap(argout) std::function (bool failed) { + if(failed) SWIG_fail; +} + // ignore some operators SWIG doesn't have to worry about %ignore rdctype::array::operator=; %ignore rdctype::array::operator[]; diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 11fba49b1..cc91bdc6c 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -93,7 +93,7 @@ win32 { SOURCES += $$CMAKE_DIR/qrenderdoc/renderdoc.py.c CONFIG += warn_off - CONFIG += c++11 + CONFIG += c++14 QMAKE_CFLAGS_WARN_OFF -= -w QMAKE_CXXFLAGS_WARN_OFF -= -w