changeset 29:6cc2b7fc7ea2

bigger pydc update: - cleanups and refactoring - python 2 fixes in var conversions (especially w/ respect to int vs long) - fix to pydc.free() which didn't work at all - fix to return python bool as actual bool - test lib covering all conversions (manual verification, though :-/)
author Tassilo Philipp
date Wed, 08 Apr 2020 22:17:43 +0200
parents edbbd467f50a
children baf087cf5971
files python/pydc/README.txt python/pydc/pydcext.c python/pydc/test/Makefile python/pydc/test/types.py
diffstat 4 files changed, 512 insertions(+), 288 deletions(-) [+]
line wrap: on
line diff
--- a/python/pydc/README.txt	Tue Apr 07 21:16:37 2020 +0200
+++ b/python/pydc/README.txt	Wed Apr 08 22:17:43 2020 +0200
@@ -28,6 +28,7 @@
 libhandle = load(libpath)
 funcptr   = find(libhandle, symbolname)
 call(funcptr, signature, ...)
+free(libhandle)
 
 
 SIGNATURE FORMAT
@@ -39,61 +40,34 @@
 
     x is positional parameter-type charcode, y is result-type charcode
 
-  SIG | FROM PYTHON 2                      | FROM PYTHON 3 @@@                  | C/C++                           | TO PYTHON 2                        | TO PYTHON 3 @@@
-  ----+------------------------------------+------------------------------------+---------------------------------+------------------------------------+-----------------------------------
-  'v' |                                    |                                    | void                            |                                    |
-  'B' | PyBool                             | PyBool                             | bool                            | PyBool                             | PyBool
-  'c' | PyInt (range checked)              | PyInt (range checked)              | char                            | PyInt                              | PyInt
-  'C' | PyInt (range checked)              | PyInt (range checked)              | unsigned char                   | PyInt                              | PyInt
-  's' | PyInt (range checked)              | PyInt (range checked)              | short                           | PyInt                              | PyInt
-  'S' | PyInt (range checked)              | PyInt (range checked)              | unsigned short                  | PyInt                              | PyInt
-  'i' | PyInt                              | PyInt                              | int                             | PyInt                              | PyInt
-  'I' | PyInt                              | PyInt                              | unsigned int                    | PyInt                              | PyInt
-  'j' | PyLong                             | PyLong                             | long                            | PyLong                             | PyLong
-  'J' | PyLong                             | PyLong                             | unsigned long                   | PyLong                             | PyLong
-  'l' | PyLongLong                         | PyLongLong                         | long long                       | PyLongLong                         | PyLongLong
-  'L' | PyLongLong                         | PyLongLong                         | unsigned long long              | PyLongLong                         | PyLongLong
-  'f' | PyFloat (cast to single precision) | PyFloat (cast to single precision) | float                           | PyFloat (cast to double precision) | PyFloat (cast to double precision)
-  'd' | PyFloat                            | PyFloat                            | double                          | PyFloat                            | PyFloat
-  'p' | PyUnicode/PyString/PyLong          | PyUnicode/PyBytes/PyLong           | void*                           | Py_ssize_t                         | Py_ssize_t
-  'Z' | PyUnicode/PyString                 | PyUnicode/PyBytes                  | const char* (UTF-8 for unicode) | PyString                           | PyUnicode
+  SIG | FROM PYTHON 2                                        | FROM PYTHON 3                            | C/C++                           | TO PYTHON 2                          | TO PYTHON 3
+  ----+------------------------------------------------------+------------------------------------------+---------------------------------+--------------------------------------+-------------------------------------
+  'v' |                                                      |                                          | void                            | None (Py_None) (e.g. ret of "...)v") | None (Py_None) (e.g. ret of "...)v")
+  'B' | bool (PyBool)                                        | bool (PyBool)#                           | int/bool                        | bool (PyBool)                        | bool (PyBool)@
+  'c' | int (PyLong)%, str (single char)%                    | int (PyLong)%, str (single char)%        | char                            | int (PyInt)                          | int (PyLong)
+  'C' | int (PyLong)%, str (single char)%                    | int (PyLong)%, str (single char)%        | unsigned char                   | int (PyInt)                          | int (PyLong)
+  's' | int (PyInt)%                                         | int (PyLong)%                            | short                           | int (PyInt)                          | int (PyLong)
+  'S' | int (PyInt)%                                         | int (PyLong)%                            | unsigned short                  | int (PyInt)                          | int (PyLong)
+  'i' | int (PyInt)                                          | int (PyLong)                             | int                             | int (PyInt)                          | int (PyLong)
+  'I' | int (PyInt)                                          | int (PyLong)                             | unsigned int                    | int (PyInt)                          | int (PyLong)
+  'j' | int (PyInt)                                          | int (PyLong)                             | long                            | int (PyInt)                          | int (PyLong)
+  'J' | int (PyInt)                                          | int (PyLong)                             | unsigned long                   | int (PyInt)                          | int (PyLong)
+  'l' | int (PyInt), long (PyLong)                           | int (PyLongLong)                         | long long                       | long (PyLong)                        | int (PyLong)
+  'L' | int (PyInt), long (PyLong)                           | int (PyLongLong)                         | unsigned long long              | long (PyLong)                        | int (PyLong)
+  'f' | float (PyFloat)$                                     | float (PyFloat)$                         | float                           | float (PyFloat)^                     | float (PyFloat)^
+  'd' | float (PyFloat)                                      | float (PyFloat)                          | double                          | float (PyFloat)                      | float (PyFloat)
+  'p' | str (PyUnicode/PyString), int (PyInt), long (PyLong) | str (PyUnicode), int (PyLong), (PyBytes) | void*                           | int,long (Py_ssize_t)                | int (Py_ssize_t)
+  'Z' | str (PyUnicode/PyString)                             | str (PyUnicode), (PyBytes)               | const char* (UTF-8 for unicode) | int (PyString)                       | str (PyUnicode)
 
+# converted to 1 if True and 0 otherwise
+@ converted to False if 0 and True otherwise
+% range checked
+$ cast to single precision
+^ cast to double precision
 
 TODO
 ====
 
 - signature suffixes used to indicate calling conventions are not supported yet!
-- not sure if returning 'p' is working, creating PyCObject, check and write test code @@@
 - callback support
 
-
-BUGS
-====
-
-* build on osx/ppc - link error i386 something...  [MacPython 2.4]
-
-  solution:
-  installation of latest python for os x (MacPython 2.5)  
-
-
-EXAMPLE BUILD
-=============
-
-  $ python setup.py install
-  running install
-  running build
-  running build_py
-  creating build
-  creating build/lib.macosx-10.3-fat-2.4
-  copying pydc.py -> build/lib.macosx-10.3-fat-2.4
-  running build_ext
-  building 'pydcext' extension
-  creating build/temp.macosx-10.3-fat-2.4
-  gcc -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -fno-strict-aliasing -Wno-long-double -no-cpp-precomp -mno-fused-madd -fno-common -dynamic -DNDEBUG -g -O3 -I../../../dyncall -I../../../dynload -I/Library/Frameworks/Python.framework/Versions/2.4/include/python2.4 -c pydcext.c -o build/temp.macosx-10.3-fat-2.4/pydcext.o
-  gcc -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -g -bundle -undefined dynamic_lookup build/temp.macosx-10.3-fat-2.4/pydcext.o -L../../../dyncall -L../../../dynload -ldyncall_s -ldynload_s -lstdc++ -o build/lib.macosx-10.3-fat-2.4/pydcext.so
-  /usr/bin/ld: for architecture i386
-  /usr/bin/ld: warning ../../../dyncall/libdyncall_s.a archive's cputype (18, architecture ppc) does not match cputype (7) for specified -arch flag: i386 (can't load from it)
-  /usr/bin/ld: warning ../../../dynload/libdynload_s.a archive's cputype (18, architecture ppc) does not match cputype (7) for specified -arch flag: i386 (can't load from it)
-  running install_lib
-  copying build/lib.macosx-10.3-fat-2.4/pydcext.so -> /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages
-
--- a/python/pydc/pydcext.c	Tue Apr 07 21:16:37 2020 +0200
+++ b/python/pydc/pydcext.c	Wed Apr 08 22:17:43 2020 +0200
@@ -30,10 +30,11 @@
 #  define USE_CAPSULE_API
 #  define DcPyCObject_FromVoidPtr(ptr, dtor)   PyCapsule_New((ptr), NULL, (dtor))
 #  define DcPyCObject_AsVoidPtr(ppobj)         PyCapsule_GetPointer((ppobj), NULL)
-#  define DcPyCObject_SetVoidPtr(ppobj, ptr)   PyCapsule_SetPointer((ppobj), (ptr))  // this might need to call the dtor to behave like PyCObject_SetVoidPtr?
+#  define DcPyCObject_SetVoidPtr(ppobj, ptr)   //@@@ unsure what to do, cannot/shouldn't call this with a null pointer as this wants to call the dtor, so not doing anything: PyCapsule_SetPointer((ppobj), (ptr))  // this might need to call the dtor to behave like PyCObject_SetVoidPtr?
 #endif
 
 #if PY_MAJOR_VERSION >= 3
+#  define EXPECT_LONG_TYPE_STR "an int"
 #  define DcPyString_GET_SIZE PyBytes_GET_SIZE
 #  define DcPyString_Check    PyBytes_Check
 #  define DcPyString_AsString PyBytes_AsString
@@ -41,6 +42,7 @@
 #  define DcPyInt_AsLong      PyLong_AsLong
 #  define DcPyInt_AS_LONG     PyLong_AS_LONG
 #else
+#  define EXPECT_LONG_TYPE_STR "an int or a long"
 #  define DcPyString_GET_SIZE PyString_GET_SIZE
 #  define DcPyString_Check    PyString_Check
 #  define DcPyString_AsString PyString_AsString
@@ -54,13 +56,13 @@
 #if defined(USE_CAPSULE_API)
 void free_library(PyObject* capsule)
 {
-  void* libhandle = PyCapsule_GetPointer(capsule, NULL);
+	void* libhandle = PyCapsule_GetPointer(capsule, NULL);
 #else
 void free_library(void* libhandle)
 {
 #endif
-  if (libhandle != 0)
-    dlFreeLibrary(libhandle);
+	if (libhandle != 0)
+		dlFreeLibrary(libhandle);
 }
 
 
@@ -69,18 +71,18 @@
 static PyObject*
 pydc_load(PyObject* self, PyObject* args)
 {
-  const char* libpath;
-  void* libhandle;
+	const char* libpath;
+	void* libhandle;
 
-  if (!PyArg_ParseTuple(args,"s", &libpath))
-    return PyErr_Format(PyExc_RuntimeError, "libpath argument (string) missing");
+	if (!PyArg_ParseTuple(args,"s", &libpath))
+		return PyErr_Format(PyExc_RuntimeError, "libpath argument (string) missing");
 
-  libhandle = dlLoadLibrary(libpath);
+	libhandle = dlLoadLibrary(libpath);
 
-  if (!libhandle)
-    return PyErr_Format(PyExc_RuntimeError, "dlLoadLibrary('%s') failed", libpath);
+	if (!libhandle)
+		return PyErr_Format(PyExc_RuntimeError, "dlLoadLibrary('%s') failed", libpath);
 
-  return DcPyCObject_FromVoidPtr(libhandle, &free_library);
+	return DcPyCObject_FromVoidPtr(libhandle, &free_library);
 }
 
 /* find function */
@@ -88,23 +90,23 @@
 static PyObject*
 pydc_find(PyObject* self, PyObject* args)
 {
-  PyObject* pcobj;
-  const char* symbol;
-  void* libhandle;
-  void* funcptr;
+	PyObject* pcobj;
+	const char* symbol;
+	void* libhandle;
+	void* funcptr;
 
-  if (!PyArg_ParseTuple(args, "Os", &pcobj, &symbol))
-    return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+	if (!PyArg_ParseTuple(args, "Os", &pcobj, &symbol))
+		return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
 
-  libhandle = DcPyCObject_AsVoidPtr(pcobj);
-  if (!libhandle)
-    return PyErr_Format(PyExc_RuntimeError, "libhandle is null");
+	libhandle = DcPyCObject_AsVoidPtr(pcobj);
+	if (!libhandle)
+		return PyErr_Format(PyExc_RuntimeError, "libhandle is null");
 
-  funcptr = dlFindSymbol(libhandle, symbol);
-  if (!funcptr)
-    return PyErr_Format(PyExc_RuntimeError, "symbol '%s' not found", symbol);
+	funcptr = dlFindSymbol(libhandle, symbol);
+	if (!funcptr)
+		return PyErr_Format(PyExc_RuntimeError, "symbol '%s' not found", symbol);
 
-  return DcPyCObject_FromVoidPtr(funcptr, NULL);
+	return DcPyCObject_FromVoidPtr(funcptr, NULL);
 }
 
 /* free function */
@@ -112,20 +114,20 @@
 static PyObject*
 pydc_free(PyObject* self, PyObject* args)
 {
-  PyObject* pcobj;
-  void* libhandle;
+	PyObject* pcobj;
+	void* libhandle;
 
-  if (!PyArg_ParseTuple(args, "o", &pcobj))
-    return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+	if (!PyArg_ParseTuple(args, "O", &pcobj))
+		return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
 
-  libhandle = DcPyCObject_AsVoidPtr(pcobj);
-  if (!libhandle)
-    return PyErr_Format(PyExc_RuntimeError, "libhandle is NULL");
+	libhandle = DcPyCObject_AsVoidPtr(pcobj);
+	if (!libhandle)
+		return PyErr_Format(PyExc_RuntimeError, "libhandle is NULL");
 
-  dlFreeLibrary(libhandle);
-  DcPyCObject_SetVoidPtr(pcobj, NULL);
+	dlFreeLibrary(libhandle);
+	DcPyCObject_SetVoidPtr(pcobj, NULL);
 
-  Py_RETURN_NONE;
+	Py_RETURN_NONE;
 }
 
 
@@ -134,209 +136,238 @@
 
 DCCallVM* gpCall;
 
+
 /* call function */
 
 static PyObject*
 pydc_call(PyObject* self, PyObject* in_args)
 {
-  PyObject*   pcobj_funcptr;
-  const char* signature;
-  PyObject*   args;
-  int         l;
-  const char* ptr;
-  char        ch;
-  int         pos;
-  void*       pfunc;
+	PyObject    *pcobj_funcptr, *args;
+	const char  *signature, *ptr;
+	char        ch;
+	int         pos, ts;
+	void*       pfunc;
+
+	if (!PyArg_ParseTuple(in_args,"OsO", &pcobj_funcptr, &signature, &args))
+		return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+
+	pfunc = DcPyCObject_AsVoidPtr(pcobj_funcptr);
+	if (!pfunc)
+		return PyErr_Format( PyExc_RuntimeError, "function pointer is NULL" );
 
-  if (!PyArg_ParseTuple(in_args,"OsO", &pcobj_funcptr, &signature, &args))
-    return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+	ptr = signature;
+	pos = 0;
+	ts  = PyTuple_Size(args);
+
+	dcReset(gpCall);
 
-  pfunc = DcPyCObject_AsVoidPtr(pcobj_funcptr);
-  if (!pfunc)
-    return PyErr_Format( PyExc_RuntimeError, "function pointer is NULL" );
+	for (ch = *ptr; ch != '\0' && ch != ')'; ch = *++ptr)
+	{
+		PyObject* po;
+
+		if (pos > ts)
+			return PyErr_Format( PyExc_RuntimeError, "expecting more arguments" );
 
-  l = PyTuple_Size(args);
+		po = PyTuple_GetItem(args, pos);
+
+		++pos; // incr here, code below uses it as 1-based argument index for error strings
 
-  ptr = signature;
-  pos = 0;
-
-  dcReset(gpCall);
+		switch(ch)
+		{
+			case DC_SIGCHAR_BOOL:
+				if ( !PyBool_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a bool", pos );
+				dcArgBool(gpCall, (Py_True == po) ? DC_TRUE : DC_FALSE);
+				break;
 
-  while ( (ch = *ptr) != '\0' && ch != ')' )
-  {
-    PyObject* po;
-
-    int index = pos+1;
-
-    if (pos > l) return PyErr_Format( PyExc_RuntimeError, "expecting more arguments" );
-
-    po = PyTuple_GetItem(args,pos);
+			case DC_SIGCHAR_CHAR:
+			case DC_SIGCHAR_UCHAR:
+				{
+					DCchar c;
+					if ( PyUnicode_Check(po) )
+					{
+#if (PY_VERSION_HEX < 0x03030000)
+						Py_UNICODE cu;
+						if (PyUnicode_GET_SIZE(po) != 1)
+#else
+						Py_UCS4 cu;
+						if (PyUnicode_GET_LENGTH(po) != 1)
+#endif
+							return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a string with length of 1 (a char string)", pos );
 
-    switch(ch)
-    {
-      case DC_SIGCHAR_BOOL:
-      {
-        DCbool b;
-        if ( !PyBool_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a bool", index );
-        b = (Py_True == po) ? DC_TRUE : DC_FALSE;
-        dcArgBool(gpCall, b);
-      }
-      break;
-      case DC_SIGCHAR_CHAR:
-      case DC_SIGCHAR_UCHAR:
-      {
-        DCchar c;
-        if ( DcPyString_Check(po) )
-        {
-          // Py_ssize_t l;
-          size_t l;
-          char* s;
-          l = DcPyString_GET_SIZE(po);
-          if (l != 1) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a string with length of 1 (a char string)", index );
-          s = DcPyString_AsString(po);
-          c = (DCchar) s[0];
-        }
-        else if ( DcPyInt_Check(po) )
-        {
-          long l;
-          l = DcPyInt_AsLong(po);
-          if ( (l > CHAR_MAX) || (l < CHAR_MIN)) return PyErr_Format( PyExc_RuntimeError, "value out of range at argument %d - expecting a char code", index );
-          c = (DCchar) l;
-        }
-        else return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a char", index );
-        dcArgChar(gpCall, c);
-      }
-      break;
-      case DC_SIGCHAR_SHORT:
-      case DC_SIGCHAR_USHORT:
-      {
-        DCshort s;
-        long v;
-        if ( !DcPyInt_Check(po) )
-          return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a short int", index );
-        v = DcPyInt_AS_LONG(po);
-        if ( (v < SHRT_MIN) || (v > SHRT_MAX) )
-          return PyErr_Format( PyExc_RuntimeError, "value out of range at argument %d - expecting a short value", index );
-        s = (DCshort) v;
-        dcArgShort(gpCall, s);
-      }
-      break;
-      case DC_SIGCHAR_INT:
-      case DC_SIGCHAR_UINT:
-      {
-        long v;
-        if ( !DcPyInt_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting an int", index );
-        v = DcPyInt_AS_LONG(po);
-        dcArgInt(gpCall, (DCint) v );
-      }
-      break;
-      case DC_SIGCHAR_LONG:
-      case DC_SIGCHAR_ULONG:
-      {
-        long v;
-        if ( !DcPyInt_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting an int", index );
-        v = DcPyInt_AsLong(po);
+#if (PY_VERSION_HEX < 0x03030000)
+						cu = PyUnicode_AS_UNICODE(po)[0];
+#else
+						cu = PyUnicode_ReadChar(po, 0);
+#endif
+						// check against UCHAR_MAX in every case b/c Py_UCS4 is unsigned
+						if ( (cu > UCHAR_MAX))
+							return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting a char code", pos ); //@@@ error message needs to specify python types
+						c = (DCchar) cu;
+					}
+					else if ( DcPyString_Check(po) )
+					{
+						size_t l;
+						char* s;
+						l = DcPyString_GET_SIZE(po);
+						if (l != 1)
+							return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a string with length of 1 (a char string)", pos );
+						s = DcPyString_AsString(po);
+						c = (DCchar) s[0];
+					}
+					else if ( DcPyInt_Check(po) )
+					{
+						long l = DcPyInt_AsLong(po);
+						if (ch == DC_SIGCHAR_CHAR && (l < CHAR_MIN || l > CHAR_MAX))
+							return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, CHAR_MIN, CHAR_MAX, l );
+						if (ch == DC_SIGCHAR_UCHAR && (l < 0 || l > UCHAR_MAX))
+							return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, UCHAR_MAX, l );
+						c = (DCchar) l;
+					}
+					else
+						return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a char", pos );
+					dcArgChar(gpCall, c);
+				}
+				break;
+
+			case DC_SIGCHAR_SHORT:
+				{
+					long l;
+					if ( !DcPyInt_Check(po) )
+						return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+					l = DcPyInt_AS_LONG(po);
+					if (l < SHRT_MIN || l > SHRT_MAX)
+						return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, SHRT_MIN, SHRT_MAX, l );
+					dcArgShort(gpCall, (DCshort)l);
+				}
+				break;
+
+			case DC_SIGCHAR_USHORT:
+				{
+					long l;
+					if ( !DcPyInt_Check(po) )
+						return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+					l = DcPyInt_AS_LONG(po);
+					if (l < 0 || l > USHRT_MAX)
+						return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, USHRT_MAX, l );
+					dcArgShort(gpCall, (DCshort)l);
+				}
+				break;
 
-      }
-      break;
-      case DC_SIGCHAR_LONGLONG:
-      case DC_SIGCHAR_ULONGLONG:
-      {
-        PY_LONG_LONG pl;
-        DClonglong dl;
-        if ( !PyLong_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a long long", index );
-        pl = PyLong_AsLongLong(po);
-        dl = (DClonglong) pl;
-        dcArgLongLong(gpCall, dl );
-      }
-      break;
-      case DC_SIGCHAR_FLOAT:
-      {
-        DCfloat f;
-        if (!PyFloat_Check(po)) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expeecting a float", index );
-        f = (float) PyFloat_AsDouble(po);
-        dcArgFloat(gpCall, f);
-      }
-      break;
-      case DC_SIGCHAR_DOUBLE:
-      {
-        double d;
-        if (!PyFloat_Check(po)) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expeecting a float", index );
-        d = PyFloat_AsDouble(po);
-        dcArgDouble(gpCall, d);
-      }
-      break;
-      case DC_SIGCHAR_POINTER:
-      {
-        DCpointer p;
-        if ( PyUnicode_Check(po) ) {
-          PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
-          if (bo) {
-            p = PyBytes_AS_STRING(bo); // Borrowed pointer
-            //p = strdup(my_result);
-            //Py_DECREF(bo);
-          }
-        } else if ( DcPyString_Check(po) ) {
-          p = (DCpointer) DcPyString_AsString(po);
-        } else if ( PyLong_Check(po) ) {
-          p = (DCpointer) PyLong_AsVoidPtr(po);
-        } else {
-          return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a promoting pointer-type (int,string)", index );
-        }
-        dcArgPointer(gpCall, p);
-      }
-      break;
-      case DC_SIGCHAR_STRING:
-      {
-        const char* p;
-        if ( PyUnicode_Check(po) ) {
-          PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
-          if (bo) {
-            p = PyBytes_AS_STRING(bo); // Borrowed pointer
-            //p = strdup(my_result);
-            //Py_DECREF(bo);
-          }
-        } else if ( DcPyString_Check(po) ) {
-          p = DcPyString_AsString(po);
-        } else {
-          return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a string", index );
-        }
-        dcArgPointer(gpCall, (DCpointer) p);
-      }
-      break;
-      default: return PyErr_Format( PyExc_RuntimeError, "unknown signature character '%c'", ch);
-    }
+			case DC_SIGCHAR_INT:
+			case DC_SIGCHAR_UINT:
+				if ( !DcPyInt_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+				dcArgInt(gpCall, (DCint) DcPyInt_AS_LONG(po));
+				break;
+
+			case DC_SIGCHAR_LONG:
+			case DC_SIGCHAR_ULONG:
+				if ( !DcPyInt_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+				dcArgLong(gpCall, (DClong) PyLong_AsLong(po));
+				break;
+
+			case DC_SIGCHAR_LONGLONG:
+			case DC_SIGCHAR_ULONGLONG:
+#if PY_MAJOR_VERSION < 3
+				if ( PyInt_Check(po) )
+					dcArgLongLong(gpCall, (DClonglong) PyInt_AS_LONG(po));
+				else
+#endif
+				if ( !PyLong_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting " EXPECT_LONG_TYPE_STR, pos );
+				dcArgLongLong(gpCall, (DClonglong)PyLong_AsLongLong(po));
+				break;
+
+			case DC_SIGCHAR_FLOAT:
+				if (!PyFloat_Check(po))
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expeecting a float", pos );
+				dcArgFloat(gpCall, (float)PyFloat_AsDouble(po));
+				break;
+
+			case DC_SIGCHAR_DOUBLE:
+				if (!PyFloat_Check(po))
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expeecting a float", pos );
+				dcArgDouble(gpCall, PyFloat_AsDouble(po));
+				break;
 
-    ++pos; ++ptr;
-
-  }
-
-  if (pos != l) return PyErr_Format( PyExc_RuntimeError, "too many arguments");
-
-  if (ch == '\0') return PyErr_Format( PyExc_RuntimeError, "return value missing in signature");
-
-  ch = *++ptr;
+			case DC_SIGCHAR_POINTER:
+			{
+				DCpointer p;
+				if ( PyUnicode_Check(po) ) {
+					PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
+					if (bo) {
+						p = PyBytes_AS_STRING(bo); // Borrowed pointer
+						//p = strdup(my_result);
+						//Py_DECREF(bo);
+					}
+				} else if ( DcPyString_Check(po) )
+					p = (DCpointer) DcPyString_AsString(po);
+#if PY_MAJOR_VERSION < 3
+				else if ( PyInt_Check(po) )
+					p = (DCpointer) PyInt_AS_LONG(po);
+#endif
+				else if ( PyLong_Check(po) )
+					p = (DCpointer) PyLong_AsVoidPtr(po);
+				else
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a promoting pointer-type (int,string)", pos ); //@@@ error message needs to specify python types
+				dcArgPointer(gpCall, p);
+			}
+			break;
 
-  switch(ch)
-  {
-    case DC_SIGCHAR_VOID:                                dcCallVoid    (gpCall, pfunc); Py_RETURN_NONE;
-    case DC_SIGCHAR_BOOL:      return Py_BuildValue("i", dcCallBool    (gpCall, pfunc));
-    case DC_SIGCHAR_CHAR:      return Py_BuildValue("b", dcCallChar    (gpCall, pfunc));
-    case DC_SIGCHAR_UCHAR:     return Py_BuildValue("B", dcCallChar    (gpCall, pfunc));
-    case DC_SIGCHAR_SHORT:     return Py_BuildValue("h", dcCallShort   (gpCall, pfunc));
-    case DC_SIGCHAR_USHORT:    return Py_BuildValue("H", dcCallShort   (gpCall, pfunc));
-    case DC_SIGCHAR_INT:       return Py_BuildValue("i", dcCallInt     (gpCall, pfunc));
-    case DC_SIGCHAR_UINT:      return Py_BuildValue("I", dcCallInt     (gpCall, pfunc));
-    case DC_SIGCHAR_LONG:      return Py_BuildValue("l", dcCallLong    (gpCall, pfunc));
-    case DC_SIGCHAR_ULONG:     return Py_BuildValue("k", dcCallLong    (gpCall, pfunc));
-    case DC_SIGCHAR_LONGLONG:  return Py_BuildValue("L", dcCallLongLong(gpCall, pfunc));
-    case DC_SIGCHAR_ULONGLONG: return Py_BuildValue("K", dcCallLongLong(gpCall, pfunc));
-    case DC_SIGCHAR_FLOAT:     return Py_BuildValue("f", dcCallFloat   (gpCall, pfunc));
-    case DC_SIGCHAR_DOUBLE:    return Py_BuildValue("d", dcCallDouble  (gpCall, pfunc));
-    case DC_SIGCHAR_STRING:    return Py_BuildValue("s", dcCallPointer (gpCall, pfunc));
-    case DC_SIGCHAR_POINTER:   return Py_BuildValue("n", dcCallPointer (gpCall, pfunc)); // @@@test, this used to be 'p' which doesn't exist, 'n' is for "Py_ssize_t"
-    default:                   return PyErr_Format(PyExc_RuntimeError, "invalid return type signature");
-  }
+			case DC_SIGCHAR_STRING:
+			{
+				const char* p;
+				if ( PyUnicode_Check(po) ) {
+					PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
+					if (bo) {
+						p = PyBytes_AS_STRING(bo); // Borrowed pointer
+						//p = strdup(my_result);@@@
+						//Py_DECREF(bo);@@@
+					}
+				} else if ( DcPyString_Check(po) ) {
+					p = DcPyString_AsString(po);
+				} else {
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a string", pos ); //@@@ error message needs to specify python types
+				}
+				dcArgPointer(gpCall, (DCpointer) p);
+			}
+			break;
+
+			default:
+				return PyErr_Format( PyExc_RuntimeError, "unknown signature character '%c'", ch);
+		}
+	}
+
+	if (pos != ts)
+		return PyErr_Format( PyExc_RuntimeError, "too many arguments");
+
+	if (ch == '\0')
+		return PyErr_Format( PyExc_RuntimeError, "return value missing in signature");
+
+
+	ch = *++ptr;
+	switch(ch)
+	{
+		case DC_SIGCHAR_VOID:                                dcCallVoid    (gpCall, pfunc); Py_RETURN_NONE;
+		case DC_SIGCHAR_BOOL:      return                    dcCallBool    (gpCall, pfunc)?Py_True:Py_False;
+		case DC_SIGCHAR_CHAR:      return Py_BuildValue("b", dcCallChar    (gpCall, pfunc));
+		case DC_SIGCHAR_UCHAR:     return Py_BuildValue("B", dcCallChar    (gpCall, pfunc));
+		case DC_SIGCHAR_SHORT:     return Py_BuildValue("h", dcCallShort   (gpCall, pfunc));
+		case DC_SIGCHAR_USHORT:    return Py_BuildValue("H", dcCallShort   (gpCall, pfunc));
+		case DC_SIGCHAR_INT:       return Py_BuildValue("i", dcCallInt     (gpCall, pfunc));
+		case DC_SIGCHAR_UINT:      return Py_BuildValue("I", dcCallInt     (gpCall, pfunc));
+		case DC_SIGCHAR_LONG:      return Py_BuildValue("l", dcCallLong    (gpCall, pfunc));
+		case DC_SIGCHAR_ULONG:     return Py_BuildValue("k", dcCallLong    (gpCall, pfunc));
+		case DC_SIGCHAR_LONGLONG:  return Py_BuildValue("L", dcCallLongLong(gpCall, pfunc));
+		case DC_SIGCHAR_ULONGLONG: return Py_BuildValue("K", dcCallLongLong(gpCall, pfunc));
+		case DC_SIGCHAR_FLOAT:     return Py_BuildValue("f", dcCallFloat   (gpCall, pfunc));
+		case DC_SIGCHAR_DOUBLE:    return Py_BuildValue("d", dcCallDouble  (gpCall, pfunc));
+		case DC_SIGCHAR_STRING:    return Py_BuildValue("s", dcCallPointer (gpCall, pfunc));
+		case DC_SIGCHAR_POINTER:   return Py_BuildValue("n", dcCallPointer (gpCall, pfunc)); // @@@test, this used to be 'p' which doesn't exist, 'n' is for "Py_ssize_t"
+		default:                   return PyErr_Format(PyExc_RuntimeError, "invalid return type signature");
+	}
 }
 
 
@@ -362,27 +393,27 @@
 PyMODINIT_FUNC
 PY_MOD_INIT_FUNC_NAME(void)
 {
-  static PyMethodDef pydcMethods[] = {
-    {"load", pydc_load, METH_VARARGS, "load library"},
-    {"find", pydc_find, METH_VARARGS, "find symbols"},
-    {"free", pydc_free, METH_VARARGS, "free library"},
-    {"call", pydc_call, METH_VARARGS, "call function"},
-    {NULL,NULL,0,NULL}
-  };
+	static PyMethodDef pydcMethods[] = {
+		{"load", pydc_load, METH_VARARGS, "load library"},
+		{"find", pydc_find, METH_VARARGS, "find symbols"},
+		{"free", pydc_free, METH_VARARGS, "free library"},
+		{"call", pydc_call, METH_VARARGS, "call function"},
+		{NULL,NULL,0,NULL}
+	};
 
-  PyObject* m;
+	PyObject* m;
 #if PY_MAJOR_VERSION >= 3
-  static struct PyModuleDef moddef = { PyModuleDef_HEAD_INIT, PYDC_MOD_NAME_STR, PYDC_MOD_DESC_STR, -1, pydcMethods, NULL, NULL, NULL, NULL };
-  m = PyModule_Create(&moddef);
+	static struct PyModuleDef moddef = { PyModuleDef_HEAD_INIT, PYDC_MOD_NAME_STR, PYDC_MOD_DESC_STR, -1, pydcMethods, NULL, NULL, NULL, NULL };
+	m = PyModule_Create(&moddef);
 #else
-  m = Py_InitModule3(PYDC_MOD_NAME_STR, pydcMethods, PYDC_MOD_DESC_STR);
+	m = Py_InitModule3(PYDC_MOD_NAME_STR, pydcMethods, PYDC_MOD_DESC_STR);
 #endif
 
-  if(m)
-    gpCall = dcNewCallVM(4096);
+	if(m)
+		gpCall = dcNewCallVM(4096); //@@@ one shared callvm for the entire module, this is not reentrant
 
 #if PY_MAJOR_VERSION >= 3
-  return m;
+	return m;
 #endif
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/pydc/test/Makefile	Wed Apr 08 22:17:43 2020 +0200
@@ -0,0 +1,37 @@
+#from dynload test
+## path to default libc.so file, easier to do via shell than in code (see main() in dynload_plain.c)
+## for compat: first gmake style, then assignment op which will use ! as part of var name on gmake<4
+##             and thus not override previously set var
+#DEF_C_DYLIB=$(shell ((ldd `which ls` | grep -o '/.*/libc.so[^ ]*' || ls /lib*/libc.so* || ls /usr/lib/libc.so*) | grep -v '\.a$$' | (sort -V -r || sort -t . -n -k 2)) 2>/dev/null | head -1)
+#DEF_C_DYLIB!=((ldd `which ls` | grep -o '/.*/libc.so[^ ]*' || ls /lib*/libc.so* || ls /usr/lib/libc.so*) | grep -v '\.a$$' | (sort -V -r || sort -t . -n -k 2)) 2>/dev/null | head -1
+#
+#APP        = dynload_plain
+#OBJS       = dynload_plain.o
+#TEST_U8_SO = dynload_plain_ß_test # @@@ unsure if every platform handles ß, here (ANSI, UTF-8, ...)
+#SRCTOP     = ${VPATH}/../..
+#BLDTOP     = ../..
+#CFLAGS    += -I${SRCTOP}/dynload -DDEF_C_DYLIB=\"${DEF_C_DYLIB}\"
+#LDLIBS_D  += -L${BLDTOP}/dynload -ldynload_s
+#
+## Works on: Darwin, NetBSD.
+# Linux: add '-ldl'
+#.PHONY: all clean install
+#all: ${APP} ${TEST_U8_SO}
+#${APP}: ${OBJS}
+#	${CC} ${LDFLAGS} ${OBJS} ${LDLIBS_D} ${LDLIBS} -o ${APP}
+#${TEST_U8_SO}:
+#	echo 'int dynload_plain_testfunc() { return 5; }' | ${CC} -`[ \`uname\` = Darwin ] && echo dynamiclib || echo shared` -x c - -o ${TEST_U8_SO}
+#clean:
+#	rm -f ${APP} ${OBJS} ${TEST_U8_SO}
+#install:
+#	mkdir -p ${PREFIX}/test
+#	cp ${APP} ${TEST_U8_SO} ${PREFIX}/test
+
+.PHONY: test.so
+
+test.so:
+	for i in char 'unsigned char' short 'unsigned short' int 'unsigned int' long 'unsigned long' 'long long' 'unsigned long long' float double 'const char*'; do \
+		echo "$$i `echo $$i | sed -E 's/(^| )([a-z])[^ ]*/\2/g'`_plus_one($$i v) { return v+1; }"; \
+	done | ${CC} -`[ \`uname\` = Darwin ] && echo dynamiclib || echo shared` -x c - -o $@
+	echo Done! Now you can run types.py
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/pydc/test/types.py	Wed Apr 08 22:17:43 2020 +0200
@@ -0,0 +1,182 @@
+import pydc
+import sys
+import platform
+
+
+
+
+def theader(title):
+  print(title)
+  print('%8s %20s %16s %-20s %12s %-12s -> %16s %-16s    # %s' % ('DC_SIG', 'C_RET_T', 'C_FSYM', 'C_PARAMLIST', 'PYTHON_ARG_T', 'IN_ARGS', 'RET_VAL', 'PYTHON_RET_T', 'NOTES'))
+
+
+def t(lib, dcsig, c_rtype, c_fsym, c_paramlist, extra_msg, *args):
+  try:
+    fp = pydc.find(lib, c_fsym)
+    r = pydc.call(fp, dcsig, *args)
+    rt = type(r).__name__
+  except:
+    r = '[EXCEPTION]'
+    rt = '!'
+    extra_msg += ' "'+str(sys.exc_info()[1])+'"'
+
+  inarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')'
+  inargs = ','.join(map(str, args))
+  print('%8s %20s %16s %-20s %12s %-12s -> %16.16s %-16s    # %s' % (dcsig, c_rtype, c_fsym, c_paramlist, inarg_types, inargs, str(r), '('+rt+')', extra_msg))
+
+
+
+# some libc tests ------------------
+
+try:
+  if sys.platform == "win32":
+    libc = pydc.load("msvcrt")
+  elif sys.platform == "darwin":
+    libc = pydc.load("/usr/lib/libc.dylib")
+  elif "bsd" in sys.platform:
+    libc = pydc.load("/usr/lib/libc.so")
+    #libc = pydc.load("/lib/libc.so.7")
+  elif platform.architecture()[0] == "64bit":
+    libc = pydc.load("/lib64/libc.so.6")
+  else:
+    libc = pydc.load("/lib/libc.so.6")
+  
+  theader('CLIB TESTS:')
+  
+  # void()
+  t(libc, ")v", "void", "sranddev", "(void)", '')
+  
+  # int()
+  t(libc, ")i", "int", "rand", "(void)", '')
+  
+  # void(unsigned int)
+  t(libc, "I)v", "void", "srand", "(int)", '', 123)
+  
+  # int() (and one different helper call for test)
+  t(libc,  ")i",  "int",  "rand", "(void)", 'with seed <------,')
+  t(libc, "I)v", "void", "srand", "(int)",  'set same seed    |', 123)
+  t(libc,  ")i",  "int",  "rand", "(void)", 'should be same result...')
+  t(libc,  ")i",  "int",  "rand", "(void)", '...and now different result')
+  
+  # int(int)
+  t(libc, "i)i", "int", "abs", "(int)", '       10   =>   10',    10)
+  t(libc, "i)i", "int", "abs", "(int)", '        0   =>    0',     0)
+  t(libc, "i)i", "int", "abs", "(int)", '      -9209 => 9209', -9209)
+  
+  # long(long)
+  t(libc, "j)j", "long", "labs", "(long)", '         48 =>         48',          48)
+  t(libc, "j)j", "long", "labs", "(long)", '          0 =>          0',           0)
+  t(libc, "j)j", "long", "labs", "(long)", '-4271477497 => 4271477497', -4271477497)
+  
+  # long long(long long)
+  t(libc, "l)l", "long long", "labs", "(long long)", ' 6334810198 => 6334810198',  6334810198)
+  t(libc, "l)l", "long long", "labs", "(long long)", '          1 =>          1',           1)
+  t(libc, "l)l", "long long", "labs", "(long long)", '          0 =>          0',           0)
+  t(libc, "l)l", "long long", "labs", "(long long)", '         -1 =>          1',          -1)
+  t(libc, "l)l", "long long", "labs", "(long long)", '-7358758407 => 7358758407', -7358758407)
+  
+  pydc.free(libc)
+except:
+  print("skipping clib tests because: "+str(sys.exc_info()[1]))
+
+
+# tests with own .so for testing all conversions ------------------
+
+l = pydc.load(sys.path[0]+"/test.so")
+
+# test all possible arg types and their conversions to and from python, with
+# specific focus/tests in areas where python 2 and 3 differ
+theader('ARG & RET TYPE CONVERSION TESTS:')
+t(l, "B)B", "int",                  "i_plus_one", "(int)",                '      False =>  True (using int func in C)', False)
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '   "a" (97) =>    98',                         'a')
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '   "a" (97) =>    98',                         'a')
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '         -2 =>    -1',                          -2)
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '         10 =>    11',                          10)
+t(l, "s)s", "short",                "s_plus_one", "(short)",              '         10 =>    11',                          10)
+t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '         10 =>    11',                          10)
+t(l, "i)i", "int",                  "i_plus_one", "(int)",                '         10 =>    11',                          10)
+t(l, "I)I", "unsigned int",        "ui_plus_one", "(unsigned int)",       '         10 =>    11',                          10)
+t(l, "j)j", "long",                 "l_plus_one", "(long)",               '         10 =>    11',                          10)
+t(l, "J)J", "unsigned long",       "ul_plus_one", "(unsigned long)",      '         10 =>    11',                          10)
+t(l, "l)l", "long long",           "ll_plus_one", "(long long)",          '         10 =>    11',                          10)
+t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", '         10 =>    11',                          10)
+t(l, "f)f", "float",                "f_plus_one", "(float)",              '      -1.23 => -0.23',                       -1.23)
+t(l, "d)d", "double",               "d_plus_one", "(double)",             '       5.67 =>  6.67',                        5.67)
+t(l, "Z)Z", "const char*",         "cc_plus_one", "(const char*)",        '"lose char" => "ose char"',            'lose char')
+t(l, "p)Z", "const char*",         "cc_plus_one", "(const char*)",        '"w/pointer" => "/pointer"',            'w/pointer')
+t(l, "p)p", "const char*",         "cc_plus_one", "(const char*)",        '        "x" => p+1 (retval is prob odd addr)', 'x')
+t(l, "p)p", "const char*",         "cc_plus_one", "(const char*)",        ' 0xdeadc0de => 0xdeadc0de+1=3735929055',0xdeadc0de)
+
+# tested checked value conversions
+theader('ARG & RET TYPE CONVERSION TESTS FOR RANGE CHECKED TYPES:')
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '        "~" =>    127',             '~')
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '        "~" =>    127',             '~')
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '         "" => input exc:',          '')
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '         "" => input exc:',          '')
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '       "ab" => input exc:',        'ab')
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '       "ab" => input exc:',        'ab')
+
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '       -128 =>   -127',            -128)
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '        127 =>   -128 (wrapped)',   127)
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '       -129 => input exc:',        -129)
+t(l, "c)c", "char",                 "c_plus_one", "(char)",               '        128 => input exc:',         128)
+                                                                                                                    
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '          0 =>      1',               0)
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '        255 =>      0 (wrapped)',   255)
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '         -1 => input exc:',          -1)
+t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '        256 => input exc:',         256)
+                                                                                                                    
+t(l, "s)s", "short",                "s_plus_one", "(short)",              '     -32768 => -32767',          -32768)
+t(l, "s)s", "short",                "s_plus_one", "(short)",              '      32767 => -32768 (wrapped)', 32767)
+t(l, "s)s", "short",                "s_plus_one", "(short)",              '     -32769 => input exc:',      -32769)
+t(l, "s)s", "short",                "s_plus_one", "(short)",              '      32768 => input exc:',       32768)
+                                                                                                                    
+t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '          0 =>     1',                0)
+t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '      65535 =>     0 (wrapped)',  65535)
+t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '         -1 => input exc:',          -1)
+t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '      65536 => input exc:',       65536)
+
+
+#     int
+#     rand(void);
+#rand()
+#define DC_SIGCHAR_VOID         'v' ra
+#define DC_SIGCHAR_BOOL         'B'
+#define DC_SIGCHAR_CHAR         'c'    2 versions
+#define DC_SIGCHAR_UCHAR        'C'
+#define DC_SIGCHAR_SHORT        's'
+#define DC_SIGCHAR_USHORT       'S'
+#define DC_SIGCHAR_INT          'i' r
+#define DC_SIGCHAR_UINT         'I'  a
+#define DC_SIGCHAR_LONG         'j' ra
+#define DC_SIGCHAR_ULONG        'J'
+#define DC_SIGCHAR_LONGLONG     'l' ra
+#define DC_SIGCHAR_ULONGLONG    'L'
+#define DC_SIGCHAR_FLOAT        'f'
+#define DC_SIGCHAR_DOUBLE       'd'
+#define DC_SIGCHAR_POINTER      'p'
+#define DC_SIGCHAR_STRING       'Z'
+#define DC_SIGCHAR_STRUCT       'T'
+#define DC_SIGCHAR_ENDARG       ')' /* also works for end struct */
+
+
+
+#  SIG | FROM PYTHON 2                      | FROM PYTHON 3 @@@                  | C/C++                           | TO PYTHON 2                        | TO PYTHON 3 @@@
+#  ----+------------------------------------+------------------------------------+---------------------------------+------------------------------------+-----------------------------------
+#  'v' |                                    |                                    | void                            | ?NoneType (returned for "xxx)v")    | NoneType (returned for "xxx)v")
+#  'B' | ?PyBool                             | ?PyBool                             | bool                            | ?PyBool                             | ?PyBool
+#  'c' | ?PyInt (range checked)              | ?PyLong (range checked)             | char                            | ?PyInt                              | ?PyLong
+#  'C' | ?PyInt (range checked)              | ?PyLong (range checked)             | unsigned char                   | ?PyInt                              | ?PyLong
+#  's' | ?PyInt (range checked)              | ?PyLong (range checked)             | short                           | ?PyInt                              | ?PyLong
+#  'S' | ?PyInt (range checked)              | ?PyLong (range checked)             | unsigned short                  | ?PyInt                              | ?PyLong
+#  'i' | ?PyInt                              | ?PyLong                             | int                             | ?PyInt                              | ?PyLong
+#  'I' | ?PyInt                              | ?PyLong                             | unsigned int                    | ?PyInt                              | ?PyLong
+#  'j' | ?PyLong                             | ?PyLong                             | long                            | ?PyLong                             | ?PyLong
+#  'J' | ?PyLong                             | ?PyLong                             | unsigned long                   | ?PyLong                             | ?PyLong
+#  'l' | ?PyLongLong                         | ?PyLongLong                         | long long                       | ?PyLongLong                         | ?PyLongLong
+#  'L' | ?PyLongLong                         | ?PyLongLong                         | unsigned long long              | ?PyLongLong                         | ?PyLongLong
+#  'f' | ?PyFloat (cast to single precision) | ?PyFloat (cast to single precision) | float                           | ?PyFloat (cast to double precision) | ?PyFloat (cast to double precision)
+#  'd' | ?PyFloat                            | ?PyFloat                            | double                          | ?PyFloat                            | ?PyFloat
+#  'p' | ?PyUnicode/PyString/PyLong          | ?PyUnicode/PyBytes/PyLong           | void*                           | ?Py_ssize_t                         | ?Py_ssize_t
+#  'Z' | ?PyUnicode/PyString                 | ?PyUnicode/PyBytes                  | const char* (UTF-8 for unicode) | ?PyString                           | ?PyUnicode
+