Repository: Zuzu-Typ/PyGLM (language: python)

Issue:
Unexpected behavior when constructing vectors from wrong-dtype numpy arrays
In the wiki, it is stated that the vector objects support both construction from iterables and from objects that supports the buffer protocol (such as numpy arrays). It seems that the buffer protocol construction has precedence over the iterable construction. This makes sense, but it has an unfortunate side-effect.

When constructing an integer vector using a numpy array of integers of the wrong size, the resulting vector is not as I would expect:
```python
>>> a = np.array([1,1,1], dtype=np.int64)
>>> glm.ivec3(a)
ivec3( 1, 0, 1 )
```
On my system, `dtype=int64` is the default, so `ivec3(np.array([1,1,1]))` produces this "bug". This effect only seems to occur for integer and boolean vectors.

If the vector had used the iterable construction method here, the result would be as expected.

This effect makes combining glm and numpy quite a bit more dangerous. This "bug" is subtle and easy to miss. For safety, vectors always have to be constructed using the unpacking operator (`ivec3(*np.array([1,1,1]))`). Even worse: since arbitrary iterables may also support the buffer protocol, this precaution has to be taken in all functions that accept a generic iterable.

My feature request:
Could PyGLM be changed to only use buffer protocol construction if the types match, and use iterable construction otherwise?



Discussion / hints:
Hi there @avdstaaij ,

you seem to have found a bug. 
On my system, the same code runs without issues:
```Python
>>> a = np.array([1,1,1], dtype=np.int64)
>>> glm.ivec3(a)
ivec3( 1, 1, 1 )
```

However, running the code on linux produces the error you've mentioned.
T he bug persists with any given vector size:
```Python
>>> a = numpy.array([1,1,1], dtype="int64")
>>> glm.ivec3(a)
ivec3( 1, 0, 1 )
>>> glm.i8vec3(a)
i8vec3( 1, 0, 1 )
>>> glm.i64vec3(a)
i64vec3( 1, 0, 1 )
```

I haven't been able to look at possible causes in the code yet.


The way PyGLM converts a given object to e.g. a 32-bit int vector is by following a simple checklist:
1. Is the object a number?
2. Is the type of the object a PyGLM type?
3. Does the object support the buffer protocol?
4. Is the object a tuple or list?

If the object does support the buffer protocol, PyGLM extracts its data and identifies, which PyGLM type it corresponds to. Afterwards, it uses a suitable constructor for the target type.
I.e. in your example, PyGLM would first convert the numpy array to a 64-bit int vector (i64vec3) and continue by converting that to a 32-bit int vector (ivec3). This way you can convert from any bit-width to any other bit-width as you would expect - unless there is a bug, as is the case here.
The reason for this bug is that the data given by numpy is interpreted based on its format string. On the linux platform I've tested on, the C datatype `long` appears to be 8 bytes wide, unlike on my Windows OS, where it's 4 bytes wide.
Therefore, on the Windows machine, numpy reports the format (`np.array((1,1,1), dtype=np.int64).data.format`) as `"q"` (long long) and on Linux it reports as `"l"` (long). PyGLM always interprets `"l"` as 4-bit, regardless of the actual size of `long`, according to this table: https://docs.python.org/3/library/struct.html#format-characters.  
This needs to be fixed.

Fix patch:
diff --git a/PyGLM/internal_functions/type_checkers.h b/PyGLM/internal_functions/type_checkers.h
index 53d9654..4f8393b 100644
--- a/PyGLM/internal_functions/type_checkers.h
+++ b/PyGLM/internal_functions/type_checkers.h
@@ -11,6 +11,8 @@
 #include "helper_macros.h"
 #include "number_functions.h"
 
+#include <cstddef>
+
 #define PyGLM_SHAPE_GET(C, R) (1 << (11 + (3 * (C-2) + (R-2))))
 
 #define PyGLM_TYPE_GET(T) (PyGLMTypeInfo::getDT<T>())
@@ -26,6 +28,127 @@
 #define _SUB_PyGLM_PTI_GET_TYPE(o) ((o->ob_type->tp_dealloc == (destructor)vec_dealloc) ? PyGLM_T_VEC : (o->ob_type->tp_dealloc == (destructor)mat_dealloc) ? PyGLM_T_MAT : (o->ob_type->tp_dealloc == (destructor)qua_dealloc) ? PyGLM_T_QUA : (o->ob_type->tp_dealloc == (destructor)mvec_dealloc) ? PyGLM_T_MVEC : PyGLM_UNKNOWN)
 #define PyGLM_PTI_GET_TYPE(o) _SUB_PyGLM_PTI_GET_TYPE(((PyObject*)o))
 
+enum class _FormatType {
+	FLOAT, DOUBLE, INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, BOOL, NONE
+};
+
+static bool is_big_endian(void)
+{
+    union {
+        uint32_t i;
+        int8_t c[4];
+    } num = {0x01020304};
+
+    return num.c[0] == 1;
+}
+
+static _FormatType getFormatFromSize(int size, bool isSigned) {
+	switch(size) {
+		case 1:
+			if (isSigned)
+				return _FormatType::INT8;
+			return _FormatType::UINT8;
+		case 2:
+			if (isSigned)
+				return _FormatType::INT16;
+			return _FormatType::UINT16;
+		case 4:
+			if (isSigned)
+				return _FormatType::INT32;
+			return _FormatType::UINT32;
+		case 8:
+			if (isSigned)
+				return _FormatType::INT64;
+			return _FormatType::UINT64;
+	}
+	return _FormatType::NONE;
+}
+
+static _FormatType getFormatType(char* format) {
+	if (format == NULL) {
+		return _FormatType::UINT8;
+	}
+	if (format[0] == '=' || is_big_endian() && format[0] == '>' || !is_big_endian() && format[0] == '<' || is_big_endian() && format[0] == '!') {
+		switch(format[1]) {
+			case 'b':
+				return _FormatType::INT8;
+			case 'c':
+			case 'B':
+				return _FormatType::UINT8;
+			case 'h':
+				return _FormatType::INT16;
+			case 'H':
+				return _FormatType::UINT16;
+			case 'i':
+			case 'l':
+				return _FormatType::INT32;
+			case 'I':
+			case 'L':
+				return _FormatType::UINT32;
+			case 'q':
+			case 'n':
+				return _FormatType::INT64;
+			case 'Q':
+			case 'N':
+				return _FormatType::UINT64;
+			case 'f':
+				return _FormatType::FLOAT;
+			case 'd':
+				return _FormatType::DOUBLE;
+			case 'P':
+				return _FormatType::UINT64;
+			case '?':
+				return _FormatType::BOOL;
+			default:
+				return _FormatType::NONE;
+
+		}
+	}
+	if (!is_big_endian() && format[0] == '>' || is_big_endian() && format[0] == '<' || !is_big_endian() && format[0] == '!') {
+		return _FormatType::NONE;
+	}
+
+	const char formatChar = (format[0] == '@') ? format[1] : format[0];
+
+	switch(formatChar) {
+		case 'f':
+			return _FormatType::FLOAT;
+		case 'd':
+			return _FormatType::DOUBLE;
+		case 'b':
+			return getFormatFromSize(sizeof(char), true);
+		case 'c':
+		case 'B':
+			return getFormatFromSize(sizeof(unsigned char), false);
+		case 'h':
+			return getFormatFromSize(sizeof(short), true);
+		case 'H':
+			return getFormatFromSize(sizeof(unsigned short), false);
+		case 'i':
+			return getFormatFromSize(sizeof(int), true);
+		case 'I':
+			return getFormatFromSize(sizeof(unsigned int), false);
+		case 'l':
+			return getFormatFromSize(sizeof(long), true);
+		case 'L':
+			return getFormatFromSize(sizeof(unsigned long), false);
+		case 'q':
+			return getFormatFromSize(sizeof(long long), true);
+		case 'Q':
+			return getFormatFromSize(sizeof(unsigned long long), false);
+		case 'n':
+			return getFormatFromSize(sizeof(Py_ssize_t), true);
+		case 'N':
+			return getFormatFromSize(sizeof(std::size_t), false);
+		case 'P':
+			return getFormatFromSize(sizeof(void*), false);
+		case '?':
+			return _FormatType::BOOL;
+		default:
+			return _FormatType::NONE;
+	}
+}
+
 // necessary forward declarations
 template<int L, typename T>
 static glm::vec<L, T> unpack_vec(PyObject* value);
@@ -432,6 +555,14 @@ struct PyGLMTypeInfo {
 
 				view.readonly = -1;
 			}
+
+			const _FormatType formatType = getFormatType(view.format);
+
+			if (formatType == _FormatType::NONE) {
+				PyBuffer_Release(&view);
+				return;
+			}
+
 			switch (view.ndim) {
 			case 1: // one dimensional array (vec / qua)
 				if (view.shape == NULL) {
@@ -444,80 +575,78 @@ struct PyGLMTypeInfo {
 						PyBuffer_Release(&view);
 						return;
 					}
-					switch (view.format[0]) {
-					case 'f':
+					switch (formatType) {
+					case _FormatType::FLOAT:
 						if (!(accepted_types & PyGLM_DT_FLOAT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_FLOAT);
 						break;
-					case 'd':
+					case _FormatType::DOUBLE:
 						if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_DOUBLE);
 						break;
-					case 'b':
+					case _FormatType::INT8:
 						if (!(accepted_types & PyGLM_DT_INT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_INT8);
 						break;
-					case 'B':
+					case _FormatType::UINT8:
 						if (!(accepted_types & PyGLM_DT_UINT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_UINT8);
 						break;
-					case 'h':
+					case _FormatType::INT16:
 						if (!(accepted_types & PyGLM_DT_INT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_INT16);
 						break;
-					case 'H':
+					case _FormatType::UINT16:
 						if (!(accepted_types & PyGLM_DT_UINT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_UINT16);
 						break;
-					case 'l':
-					case 'i':
+					case _FormatType::INT32:
 						if (!(accepted_types & PyGLM_DT_INT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_INT);
 						break;
-					case 'L':
-					case 'I':
+					case _FormatType::UINT32:
 						if (!(accepted_types & PyGLM_DT_UINT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_UINT);
 						break;
-					case 'q':
+					case _FormatType::INT64:
 						if (!(accepted_types & PyGLM_DT_INT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_INT64);
 						break;
-					case 'Q':
+					case _FormatType::UINT64:
 						if (!(accepted_types & PyGLM_DT_UINT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_1 | PyGLM_DT_UINT64);
 						break;
-					case '?':
+					case _FormatType::BOOL:
 						if (!(accepted_types & PyGLM_DT_BOOL)) {
 							PyBuffer_Release(&view);
 							return;
@@ -534,80 +663,80 @@ struct PyGLMTypeInfo {
 						PyBuffer_Release(&view);
 						return;
 					}
-					switch (view.format[0]) {
-					case 'f':
+					switch (formatType) {
+					case _FormatType::FLOAT:
 						if (!(accepted_types & PyGLM_DT_FLOAT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_FLOAT);
 						break;
-					case 'd':
+					case _FormatType::DOUBLE:
 						if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_DOUBLE);
 						break;
-					case 'b':
+					case _FormatType::INT8:
 						if (!(accepted_types & PyGLM_DT_INT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_INT8);
 						break;
-					case 'B':
+					case _FormatType::UINT8:
 						if (!(accepted_types & PyGLM_DT_UINT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_UINT8);
 						break;
-					case 'h':
+					case _FormatType::INT16:
 						if (!(accepted_types & PyGLM_DT_INT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_INT16);
 						break;
-					case 'H':
+					case _FormatType::UINT16:
 						if (!(accepted_types & PyGLM_DT_UINT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_UINT16);
 						break;
-					case 'l':
-					case 'i':
+					
+					case _FormatType::INT32:
 						if (!(accepted_types & PyGLM_DT_INT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_INT);
 						break;
-					case 'L':
-					case 'I':
+					
+					case _FormatType::UINT32:
 						if (!(accepted_types & PyGLM_DT_UINT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_UINT);
 						break;
-					case 'q':
+					case _FormatType::INT64:
 						if (!(accepted_types & PyGLM_DT_INT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_INT64);
 						break;
-					case 'Q':
+					case _FormatType::UINT64:
 						if (!(accepted_types & PyGLM_DT_UINT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_2 | PyGLM_DT_UINT64);
 						break;
-					case '?':
+					case _FormatType::BOOL:
 						if (!(accepted_types & PyGLM_DT_BOOL)) {
 							PyBuffer_Release(&view);
 							return;
@@ -624,80 +753,80 @@ struct PyGLMTypeInfo {
 						PyBuffer_Release(&view);
 						return;
 					}
-					switch (view.format[0]) {
-					case 'f':
+					switch (formatType) {
+					case _FormatType::FLOAT:
 						if (!(accepted_types & PyGLM_DT_FLOAT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_FLOAT);
 						break;
-					case 'd':
+					case _FormatType::DOUBLE:
 						if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_DOUBLE);
 						break;
-					case 'b':
+					case _FormatType::INT8:
 						if (!(accepted_types & PyGLM_DT_INT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_INT8);
 						break;
-					case 'B':
+					case _FormatType::UINT8:
 						if (!(accepted_types & PyGLM_DT_UINT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_UINT8);
 						break;
-					case 'h':
+					case _FormatType::INT16:
 						if (!(accepted_types & PyGLM_DT_INT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_INT16);
 						break;
-					case 'H':
+					case _FormatType::UINT16:
 						if (!(accepted_types & PyGLM_DT_UINT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_UINT16);
 						break;
-					case 'l':
-					case 'i':
+					
+					case _FormatType::INT32:
 						if (!(accepted_types & PyGLM_DT_INT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_INT);
 						break;
-					case 'L':
-					case 'I':
+					
+					case _FormatType::UINT32:
 						if (!(accepted_types & PyGLM_DT_UINT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_UINT);
 						break;
-					case 'q':
+					case _FormatType::INT64:
 						if (!(accepted_types & PyGLM_DT_INT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_INT64);
 						break;
-					case 'Q':
+					case _FormatType::UINT64:
 						if (!(accepted_types & PyGLM_DT_UINT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_3 | PyGLM_DT_UINT64);
 						break;
-					case '?':
+					case _FormatType::BOOL:
 						if (!(accepted_types & PyGLM_DT_BOOL)) {
 							PyBuffer_Release(&view);
 							return;
@@ -714,8 +843,8 @@ struct PyGLMTypeInfo {
 						PyBuffer_Release(&view);
 						return;
 					}
-					switch (view.format[0]) {
-					case 'f':
+					switch (formatType) {
+					case _FormatType::FLOAT:
 						if (!(accepted_types & PyGLM_DT_FLOAT)) {
 							PyBuffer_Release(&view);
 							return;
@@ -725,7 +854,7 @@ struct PyGLMTypeInfo {
 						else
 							setInfo(PyGLM_T_QUA | PyGLM_DT_FLOAT);
 						break;
-					case 'd':
+					case _FormatType::DOUBLE:
 						if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 							PyBuffer_Release(&view);
 							return;
@@ -735,65 +864,65 @@ struct PyGLMTypeInfo {
 						else
 							setInfo(PyGLM_T_QUA | PyGLM_DT_DOUBLE);
 						break;
-					case 'b':
+					case _FormatType::INT8:
 						if (!(accepted_types & PyGLM_DT_INT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_INT8);
 						break;
-					case 'B':
+					case _FormatType::UINT8:
 						if (!(accepted_types & PyGLM_DT_UINT8)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_UINT8);
 						break;
-					case 'h':
+					case _FormatType::INT16:
 						if (!(accepted_types & PyGLM_DT_INT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_INT16);
 						break;
-					case 'H':
+					case _FormatType::UINT16:
 						if (!(accepted_types & PyGLM_DT_UINT16)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_UINT16);
 						break;
-					case 'l':
-					case 'i':
+					
+					case _FormatType::INT32:
 						if (!(accepted_types & PyGLM_DT_INT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_INT);
 						break;
-					case 'L':
-					case 'I':
+					
+					case _FormatType::UINT32:
 						if (!(accepted_types & PyGLM_DT_UINT)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_UINT);
 						break;
-					case 'q':
+					case _FormatType::INT64:
 						if (!(accepted_types & PyGLM_DT_INT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_INT64);
 						break;
-					case 'Q':
+					case _FormatType::UINT64:
 						if (!(accepted_types & PyGLM_DT_UINT64)) {
 							PyBuffer_Release(&view);
 							return;
 						}
 						setInfo(PyGLM_T_VEC | PyGLM_SHAPE_4 | PyGLM_DT_UINT64);
 						break;
-					case '?':
+					case _FormatType::BOOL:
 						if (!(accepted_types & PyGLM_DT_BOOL)) {
 							PyBuffer_Release(&view);
 							return;
@@ -827,31 +956,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x2 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x2 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x2 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -868,31 +997,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x3 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x3 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x3 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -909,31 +1038,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x4 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x4 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_2x4 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -961,31 +1090,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x2 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x2 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x2 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -1002,31 +1131,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x3 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x3 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x3 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -1043,31 +1172,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x4 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x4 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_3x4 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -1095,31 +1224,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x2 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x2 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x2 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -1136,31 +1265,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x3 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x3 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x3 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;
@@ -1177,31 +1306,31 @@ struct PyGLMTypeInfo {
 							PyBuffer_Release(&view);
 							return;
 						}
-						switch (view.format[0]) {
-						case 'f':
+						switch (formatType) {
+						case _FormatType::FLOAT:
 							if (!(accepted_types & PyGLM_DT_FLOAT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x4 | PyGLM_DT_FLOAT);
 							break;
-						case 'd':
+						case _FormatType::DOUBLE:
 							if (!(accepted_types & PyGLM_DT_DOUBLE)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x4 | PyGLM_DT_DOUBLE);
 							break;
-						case 'l':
-						case 'i':
+						
+						case _FormatType::INT32:
 							if (!(accepted_types & PyGLM_DT_INT)) {
 								PyBuffer_Release(&view);
 								return;
 							}
 							setInfo(PyGLM_T_MAT | PyGLM_SHAPE_4x4 | PyGLM_DT_INT);
 							break;
-						case 'L':
-						case 'I':
+						
+						case _FormatType::UINT32:
 							if (!(accepted_types & PyGLM_DT_UINT)) {
 								PyBuffer_Release(&view);
 								return;


Test patch:
diff --git a/test/PyGLM_test.py b/test/PyGLM_test.py
index d7ffa38..90646da 100644
--- a/test/PyGLM_test.py
+++ b/test/PyGLM_test.py
@@ -1,4 +1,4 @@
-import glm, sys, random, time, copy, re, math
+import glm, sys, random, time, copy, re, math, ctypes
 
 from collections import OrderedDict
 
@@ -1167,6 +1167,24 @@ def test_buffer_protocol():
     arr = glm.array(glm.mat4(), glm.mat4(2))
     mv = memoryview(arr)
     assert glm.array(mv) == arr, arr
+
+    for T in (ctypes.c_float, 
+            ctypes.c_double, 
+            ctypes.c_char, 
+            ctypes.c_byte, 
+            ctypes.c_ubyte,
+            ctypes.c_short,
+            ctypes.c_ushort,
+            ctypes.c_int,
+            ctypes.c_uint,
+            ctypes.c_long,
+            ctypes.c_ulong,
+            ctypes.c_longlong,
+            ctypes.c_ulonglong,
+            ctypes.c_size_t,
+            ctypes.c_ssize_t,
+            ctypes.c_void_p):
+        assert glm.vec1((T*1)()) == glm.vec1()
 #/buffer protocol #
 
 # lists and tuples #