Browse code

Add Members

Robert Cranston authored on 01/03/2023 23:07:13
Showing 7 changed files

... ...
@@ -16,7 +16,7 @@ add_library(${PROJECT_NAME} INTERFACE)
16 16
 ## Common
17 17
 include(common.cmake)
18 18
 common(
19
-    CXX_STANDARD 11
19
+    CXX_STANDARD 14
20 20
     DISABLE_CPPCHECK # GLTRAITS_TEST_OBJECT(template gen_objects<Args...>)
21 21
     PACKAGES
22 22
         glm
... ...
@@ -1,6 +1,6 @@
1 1
 # [`gltraits`][]
2 2
 
3
-A [C++11][]/[OpenGL][]>=[1.0][](/[GLM][]) [trait][]s library.
3
+A [C++11][](/[C++14][])/[OpenGL][]>=[1.0][](/[GLM][]) [trait][]s library.
4 4
 
5 5
 This library seeks to unify some parts of the OpenGL [API][] to ease [generic
6 6
 programming][]. It also provides sensible default arguments, [optional][debug]
... ...
@@ -46,6 +46,7 @@ further.
46 46
 
47 47
 [`gltraits`]: https://git.rcrnstn.net/rcrnstn/gltraits
48 48
 [C++11]: https://en.wikipedia.org/wiki/C++11
49
+[C++14]: https://en.wikipedia.org/wiki/C++14
49 50
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
50 51
 [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
51 52
 [GLM]: https://glm.g-truc.net
... ...
@@ -383,6 +384,92 @@ signatures for `glFramebufferTexture1D` and `glFramebufferTexture{2,3}D`.)
383 384
 [immutable storage]: https://www.khronos.org/opengl/wiki/Texture_Storage#Immutable_storage
384 385
 [pixel transfer parameters]: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_transfer_parameters
385 386
 
387
+### Member
388
+
389
+`GLTraits::members()` provides compile time reflection for types consisting
390
+(only) of types supported by `GLTraits::Value`. This feature is only available
391
+if [C++14][] or above is supported.
392
+
393
+If `T` is a non-[empty][] [trivially copyable][] [standard-layout][] type and
394
+there exists template specializations of `GLTraits::Value` for the types of
395
+either
396
+
397
+-   public member variables of `T`, if `T` is a [class][], or
398
+-   elements of `T`, if `T` is an [array][], or
399
+-   `T` itself, if `T` is a [scalar][]
400
+
401
+then `constexpr auto GLTraits::members<T>()` returns an instance of the
402
+[empty][] `struct GLTraits::Members<typename... Values>` where `Values...` are
403
+the ("member") types above, in the order they occur in `T`.
404
+
405
+An example use case would be along the lines of:
406
+
407
+```cpp
408
+template<
409
+    typename    Value,
410
+    typename... Values
411
+>
412
+void vertex_setup(
413
+    GLTraits::Members<Value, Values...>,
414
+    std::size_t stride,
415
+    std::size_t offset   = 0,
416
+    GLint       location = 0
417
+)
418
+{
419
+    auto unaligned = offset % alignof(Value);
420
+    if (unaligned)
421
+        offset += alignof(Value) - unaligned;
422
+    GLTraits::Value<Value>::vertex_attrib_pointer(location, offset, stride);
423
+    vertex_setup(
424
+        GLTraits::Members<Values...>{},
425
+        stride,
426
+        offset   + sizeof(Value),
427
+        location + GLTraits::Value<Value>::columns
428
+    );
429
+}
430
+void vertex_setup(GLTraits::Members<>, std::size_t, std::size_t, GLint)
431
+{}
432
+
433
+struct Vertex
434
+{
435
+    glm::vec3  position;
436
+    glm::vec2  tex_coord;
437
+    glm::mat3  tbn;
438
+    glm::ivec4 bone_indices;
439
+    glm::vec4  bone_weights;
440
+    GLubyte    flags;
441
+    GLdouble   unaligned;
442
+};
443
+vertex_setup(GLTraits::members<Vertex>(), sizeof(Vertex));
444
+```
445
+
446
+This use case is tested in [`tests/vertex_setup.cpp`][]. To verify that the
447
+compiler sees through all the templates, a script that builds it and
448
+disassembles it available in [`doc/vertex_setup`][] and its output in
449
+[`doc/vertex_setup.i`][].
450
+
451
+It is recommended to use a library based on `gltraits` that abstracts this
452
+further.
453
+
454
+Many thanks to Antony Polukhin (the author of [Boost.PFR][] and its standalone
455
+version [`magic_get`][]) on whose talks this implementation is based:
456
+
457
+-   [CppCon 2016: C++14 Reflections Without Macros, Markup nor External Tooling][]
458
+-   [Meeting C++ 2018: Better C++14 reflections][]
459
+
460
+[trivially copyable]: https://en.cppreference.com/w/cpp/types/is_trivially_copyable
461
+[standard-layout]: https://en.cppreference.com/w/cpp/types/is_standard_layout
462
+[class]: https://en.cppreference.com/w/cpp/types/is_class
463
+[array]: https://en.cppreference.com/w/cpp/types/is_array
464
+[scalar]: https://en.cppreference.com/w/cpp/types/is_scalar
465
+[`tests/vertex_setup.cpp`]: tests/vertex_setup.cpp
466
+[`doc/vertex_setup`]: doc/vertex_setup
467
+[`doc/vertex_setup.i`]: doc/vertex_setup.i
468
+[Boost.PFR]: https://www.boost.org/libs/pfr
469
+[`magic_get`]: https://github.com/apolukhin/magic_get
470
+[CppCon 2016: C++14 Reflections Without Macros, Markup nor External Tooling]: https://www.youtube.com/watch?v=abdeAew3gmQ
471
+[Meeting C++ 2018: Better C++14 reflections]: https://www.youtube.com/watch?v=UlNUNxLtBI0
472
+
386 473
 ## Building
387 474
 
388 475
 See [`BUILDING.md`][].
389 476
new file mode 100755
... ...
@@ -0,0 +1,22 @@
1
+#!/bin/sh
2
+set -euC
3
+
4
+cd "$(dirname "$0")/.."
5
+
6
+file="$(basename "$0")"
7
+target="gltraits-test-$file"
8
+build_dir="_build"
9
+in_file="$build_dir/$target"
10
+out_file="doc/$file.i"
11
+
12
+rm    -rf     "$build_dir"
13
+cmake -B      "$build_dir" -DCMAKE_BUILD_TYPE='RelWithDebInfo'
14
+cmake --build "$build_dir" --target "$target"
15
+
16
+${GDB:-gdb} \
17
+  -n -batch \
18
+  -ex 'set print asm-demangle on' \
19
+  -ex 'disassemble/s main' \
20
+  "$in_file" \
21
+| sed "s|$(pwd)/||;s/^[0-9]\+//" \
22
+>| "$out_file"
0 23
new file mode 100644
... ...
@@ -0,0 +1,508 @@
1
+Dump of assembler code for function main():
2
+tests/vertex_setup.cpp:
3
+	{
4
+   0x0000000000005890 <+0>:	endbr64 
5
+   0x0000000000005894 <+4>:	push   %r14
6
+   0x0000000000005896 <+6>:	push   %r13
7
+   0x0000000000005898 <+8>:	push   %r12
8
+   0x000000000000589a <+10>:	push   %rbp
9
+   0x000000000000589b <+11>:	push   %rbx
10
+   0x000000000000589c <+12>:	sub    $0x40,%rsp
11
+
12
+_build/_deps/glbase-src/include/glbase.hpp:
13
+	    GLBASE_GET_GLOBAL(int, debug)
14
+   0x00000000000058a0 <+16>:	mov    0x11719(%rip),%r13        # 0x16fc0
15
+
16
+tests/vertex_setup.cpp:
17
+	{
18
+   0x00000000000058a7 <+23>:	mov    %fs:0x28,%rax
19
+   0x00000000000058b0 <+32>:	mov    %rax,0x38(%rsp)
20
+   0x00000000000058b5 <+37>:	xor    %eax,%eax
21
+
22
+include/gltraits.hpp:
23
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
24
+   0x00000000000058b7 <+39>:	test   %r13,%r13
25
+   0x00000000000058ba <+42>:	je     0x58c1 <main()+49>
26
+   0x00000000000058bc <+44>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
27
+   0x00000000000058c1 <+49>:	mov    $0xfffffffffffffff0,%r12
28
+   0x00000000000058c8 <+56>:	mov    %fs:(%r12),%r9d
29
+   0x00000000000058cd <+61>:	test   %r9d,%r9d
30
+   0x00000000000058d0 <+64>:	jg     0x5a74 <main()+484>
31
+   0x00000000000058d6 <+70>:	mov    0x116f3(%rip),%r14        # 0x16fd0
32
+   0x00000000000058dd <+77>:	xor    %r9d,%r9d
33
+   0x00000000000058e0 <+80>:	xor    %ecx,%ecx
34
+   0x00000000000058e2 <+82>:	xor    %edi,%edi
35
+   0x00000000000058e4 <+84>:	mov    $0x68,%r8d
36
+   0x00000000000058ea <+90>:	mov    $0x1406,%edx
37
+   0x00000000000058ef <+95>:	mov    $0x3,%esi
38
+   0x00000000000058f4 <+100>:	call   *(%r14)
39
+   0x00000000000058f7 <+103>:	test   %r13,%r13
40
+   0x00000000000058fa <+106>:	je     0x5901 <main()+113>
41
+   0x00000000000058fc <+108>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
42
+   0x0000000000005901 <+113>:	mov    %fs:(%r12),%r8d
43
+   0x0000000000005906 <+118>:	test   %r8d,%r8d
44
+   0x0000000000005909 <+121>:	jg     0x5c01 <main()+881>
45
+   0x000000000000590f <+127>:	xor    %ecx,%ecx
46
+   0x0000000000005911 <+129>:	mov    $0xc,%r9d
47
+   0x0000000000005917 <+135>:	mov    $0x68,%r8d
48
+   0x000000000000591d <+141>:	mov    $0x1406,%edx
49
+   0x0000000000005922 <+146>:	mov    $0x2,%esi
50
+   0x0000000000005927 <+151>:	mov    $0x1,%edi
51
+   0x000000000000592c <+156>:	call   *(%r14)
52
+
53
+	GLTRAITS_VALUE_MATRIXS(glm::mat,  FLOAT,          glm::value_ptr, GL_FALSE, 32F,  f,  ,  ,         KEEP)
54
+   0x000000000000592f <+159>:	test   %r13,%r13
55
+   0x0000000000005932 <+162>:	je     0x5939 <main()+169>
56
+   0x0000000000005934 <+164>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
57
+   0x0000000000005939 <+169>:	mov    %fs:(%r12),%edi
58
+   0x000000000000593e <+174>:	test   %edi,%edi
59
+   0x0000000000005940 <+176>:	jg     0x5bca <main()+826>
60
+
61
+tests/vertex_setup.cpp:
62
+	{
63
+   0x0000000000005946 <+182>:	mov    $0x14,%ebx
64
+   0x000000000000594b <+187>:	mov    $0x2,%ebp
65
+
66
+include/gltraits.hpp:
67
+	GLTRAITS_VALUE_MATRIXS(glm::mat,  FLOAT,          glm::value_ptr, GL_FALSE, 32F,  f,  ,  ,         KEEP)
68
+   0x0000000000005950 <+192>:	mov    %rbx,%r9
69
+   0x0000000000005953 <+195>:	add    $0xc,%rbx
70
+   0x0000000000005957 <+199>:	mov    %ebp,%edi
71
+   0x0000000000005959 <+201>:	mov    $0x68,%r8d
72
+   0x000000000000595f <+207>:	xor    %ecx,%ecx
73
+   0x0000000000005961 <+209>:	mov    $0x1406,%edx
74
+   0x0000000000005966 <+214>:	mov    $0x3,%esi
75
+   0x000000000000596b <+219>:	add    $0x1,%ebp
76
+   0x000000000000596e <+222>:	call   *(%r14)
77
+   0x0000000000005971 <+225>:	cmp    $0x38,%rbx
78
+   0x0000000000005975 <+229>:	jne    0x5950 <main()+192>
79
+
80
+	GLTRAITS_VALUE_VECTOR( glm::ivec, INT,            glm::value_ptr,           32I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
81
+   0x0000000000005977 <+231>:	test   %r13,%r13
82
+   0x000000000000597a <+234>:	je     0x5981 <main()+241>
83
+   0x000000000000597c <+236>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
84
+   0x0000000000005981 <+241>:	mov    %fs:(%r12),%esi
85
+   0x0000000000005986 <+246>:	test   %esi,%esi
86
+   0x0000000000005988 <+248>:	jg     0x5b93 <main()+771>
87
+   0x000000000000598e <+254>:	mov    $0x38,%r8d
88
+   0x0000000000005994 <+260>:	mov    $0x68,%ecx
89
+   0x0000000000005999 <+265>:	mov    $0x1404,%edx
90
+   0x000000000000599e <+270>:	mov    0x11633(%rip),%rbx        # 0x16fd8
91
+   0x00000000000059a5 <+277>:	mov    $0x4,%esi
92
+   0x00000000000059aa <+282>:	mov    $0x5,%edi
93
+   0x00000000000059af <+287>:	call   *(%rbx)
94
+
95
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
96
+   0x00000000000059b1 <+289>:	test   %r13,%r13
97
+   0x00000000000059b4 <+292>:	je     0x59bb <main()+299>
98
+   0x00000000000059b6 <+294>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
99
+   0x00000000000059bb <+299>:	mov    %fs:(%r12),%ecx
100
+   0x00000000000059c0 <+304>:	test   %ecx,%ecx
101
+   0x00000000000059c2 <+306>:	jg     0x5b5c <main()+716>
102
+   0x00000000000059c8 <+312>:	xor    %ecx,%ecx
103
+   0x00000000000059ca <+314>:	mov    $0x48,%r9d
104
+   0x00000000000059d0 <+320>:	mov    $0x68,%r8d
105
+   0x00000000000059d6 <+326>:	mov    $0x1406,%edx
106
+   0x00000000000059db <+331>:	mov    $0x4,%esi
107
+   0x00000000000059e0 <+336>:	mov    $0x6,%edi
108
+   0x00000000000059e5 <+341>:	call   *(%r14)
109
+
110
+	GLTRAITS_VALUE_SCALAR( GLubyte,   UNSIGNED_BYTE,  UNSIGNED_INT,             8UI,  ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
111
+   0x00000000000059e8 <+344>:	test   %r13,%r13
112
+   0x00000000000059eb <+347>:	je     0x59f2 <main()+354>
113
+   0x00000000000059ed <+349>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
114
+   0x00000000000059f2 <+354>:	mov    %fs:(%r12),%edx
115
+   0x00000000000059f7 <+359>:	test   %edx,%edx
116
+   0x00000000000059f9 <+361>:	jg     0x5b25 <main()+661>
117
+   0x00000000000059ff <+367>:	mov    $0x58,%r8d
118
+   0x0000000000005a05 <+373>:	mov    $0x68,%ecx
119
+   0x0000000000005a0a <+378>:	mov    $0x1401,%edx
120
+   0x0000000000005a0f <+383>:	mov    $0x1,%esi
121
+   0x0000000000005a14 <+388>:	mov    $0x7,%edi
122
+   0x0000000000005a19 <+393>:	call   *(%rbx)
123
+
124
+	GLTRAITS_VALUE_SCALAR( GLushort,  UNSIGNED_SHORT, UNSIGNED_INT,             16UI, ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
125
+	GLTRAITS_VALUE_SCALAR( GLuint,    UNSIGNED_INT,   UNSIGNED_INT,             32UI, ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
126
+	GLTRAITS_VALUE_SCALAR( GLdouble,  DOUBLE,         DOUBLE,                   ED,   d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
127
+   0x0000000000005a1b <+395>:	test   %r13,%r13
128
+   0x0000000000005a1e <+398>:	je     0x5a25 <main()+405>
129
+   0x0000000000005a20 <+400>:	call   0x3480 <TLS init function for GLBase::debug_@plt>
130
+   0x0000000000005a25 <+405>:	mov    %fs:(%r12),%eax
131
+   0x0000000000005a2a <+410>:	test   %eax,%eax
132
+   0x0000000000005a2c <+412>:	jg     0x5aab <main()+539>
133
+   0x0000000000005a2e <+414>:	mov    $0x60,%r8d
134
+   0x0000000000005a34 <+420>:	mov    $0x68,%ecx
135
+   0x0000000000005a39 <+425>:	mov    $0x140a,%edx
136
+   0x0000000000005a3e <+430>:	mov    0x1156b(%rip),%rax        # 0x16fb0
137
+   0x0000000000005a45 <+437>:	mov    $0x1,%esi
138
+   0x0000000000005a4a <+442>:	mov    $0x8,%edi
139
+   0x0000000000005a4f <+447>:	call   *(%rax)
140
+
141
+tests/vertex_setup.cpp:
142
+	}
143
+   0x0000000000005a51 <+449>:	mov    0x38(%rsp),%rax
144
+   0x0000000000005a56 <+454>:	sub    %fs:0x28,%rax
145
+   0x0000000000005a5f <+463>:	jne    0x5c38 <main()+936>
146
+   0x0000000000005a65 <+469>:	add    $0x40,%rsp
147
+   0x0000000000005a69 <+473>:	xor    %eax,%eax
148
+   0x0000000000005a6b <+475>:	pop    %rbx
149
+   0x0000000000005a6c <+476>:	pop    %rbp
150
+   0x0000000000005a6d <+477>:	pop    %r12
151
+   0x0000000000005a6f <+479>:	pop    %r13
152
+   0x0000000000005a71 <+481>:	pop    %r14
153
+   0x0000000000005a73 <+483>:	ret    
154
+
155
+/usr/include/c++/11/bits/basic_string.h:
156
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
157
+   0x0000000000005a74 <+484>:	lea    0x10(%rsp),%rbp
158
+   0x0000000000005a79 <+489>:	lea    0x20(%rsp),%rax
159
+
160
+include/gltraits.hpp:
161
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
162
+   0x0000000000005a7e <+494>:	mov    $0x2,%edi
163
+
164
+/usr/include/c++/11/bits/basic_string.h:
165
+	      { _M_string_length = __length; }
166
+   0x0000000000005a83 <+499>:	movq   $0x0,0x18(%rsp)
167
+
168
+include/gltraits.hpp:
169
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
170
+   0x0000000000005a8c <+508>:	mov    %rbp,%rsi
171
+
172
+/usr/include/c++/11/bits/basic_string.h:
173
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
174
+   0x0000000000005a8f <+511>:	mov    %rax,0x10(%rsp)
175
+
176
+/usr/include/c++/11/bits/char_traits.h:
177
+	      { __c1 = __c2; }
178
+   0x0000000000005a94 <+516>:	movb   $0x0,0x20(%rsp)
179
+
180
+include/gltraits.hpp:
181
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
182
+   0x0000000000005a99 <+521>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
183
+
184
+/usr/include/c++/11/bits/basic_string.h:
185
+	      { _M_dispose(); }
186
+   0x0000000000005a9e <+526>:	mov    %rbp,%rdi
187
+   0x0000000000005aa1 <+529>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
188
+   0x0000000000005aa6 <+534>:	jmp    0x58d6 <main()+70>
189
+
190
+	      basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())
191
+   0x0000000000005aab <+539>:	lea    0x10(%rsp),%rbp
192
+   0x0000000000005ab0 <+544>:	lea    0x20(%rsp),%rax
193
+
194
+/usr/include/c++/11/bits/basic_string.tcc:
195
+		    _M_data(_M_create(__dnew, size_type(0)));
196
+   0x0000000000005ab5 <+549>:	xor    %edx,%edx
197
+
198
+		size_type __dnew = static_cast<size_type>(std::distance(__beg, __end));
199
+   0x0000000000005ab7 <+551>:	movq   $0x1a,0x8(%rsp)
200
+
201
+	
202
+		if (__dnew > size_type(_S_local_capacity))
203
+		  {
204
+		    _M_data(_M_create(__dnew, size_type(0)));
205
+   0x0000000000005ac0 <+560>:	lea    0x8(%rsp),%rsi
206
+   0x0000000000005ac5 <+565>:	mov    %rbp,%rdi
207
+
208
+/usr/include/c++/11/bits/basic_string.h:
209
+		: allocator_type(__a), _M_p(__dat) { }
210
+   0x0000000000005ac8 <+568>:	mov    %rax,0x10(%rsp)
211
+
212
+/usr/include/c++/11/bits/basic_string.tcc:
213
+	      basic_string<_CharT, _Traits, _Alloc>::
214
+   0x0000000000005acd <+573>:	call   0x38a0 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)@plt>
215
+
216
+		    _M_capacity(__dnew);
217
+		  }
218
+	
219
+		// Check for out_of_range and length_error exceptions.
220
+		__try
221
+		  { this->_S_copy_chars(_M_data(), __beg, __end); }
222
+   0x0000000000005ad2 <+578>:	lea    0xb545(%rip),%rdx        # 0x1101e
223
+
224
+		    _M_data(_M_create(__dnew, size_type(0)));
225
+   0x0000000000005ad9 <+585>:	mov    %rax,%rdi
226
+
227
+/usr/include/c++/11/bits/basic_string.h:
228
+	      { _M_dataplus._M_p = __p; }
229
+   0x0000000000005adc <+588>:	mov    %rax,0x10(%rsp)
230
+
231
+	
232
+	      pointer
233
+	      _M_data() const
234
+	      { return _M_dataplus._M_p; }
235
+	
236
+	      pointer
237
+	      _M_local_data()
238
+	      {
239
+	#if __cplusplus >= 201103L
240
+		return std::pointer_traits<pointer>::pointer_to(*_M_local_buf);
241
+	#else
242
+		return pointer(_M_local_buf);
243
+	#endif
244
+	      }
245
+	
246
+	      const_pointer
247
+	      _M_local_data() const
248
+	      {
249
+	#if __cplusplus >= 201103L
250
+		return std::pointer_traits<const_pointer>::pointer_to(*_M_local_buf);
251
+	#else
252
+		return const_pointer(_M_local_buf);
253
+	#endif
254
+	      }
255
+	
256
+	      void
257
+	      _M_capacity(size_type __capacity)
258
+	      { _M_allocated_capacity = __capacity; }
259
+   0x0000000000005ae1 <+593>:	mov    0x8(%rsp),%rax
260
+
261
+/usr/include/c++/11/bits/basic_string.tcc:
262
+		  { this->_S_copy_chars(_M_data(), __beg, __end); }
263
+   0x0000000000005ae6 <+598>:	lea    -0x1a(%rdx),%rsi
264
+
265
+/usr/include/c++/11/bits/basic_string.h:
266
+	      { _M_allocated_capacity = __capacity; }
267
+   0x0000000000005aea <+602>:	mov    %rax,0x20(%rsp)
268
+
269
+/usr/include/c++/11/bits/basic_string.tcc:
270
+		  { this->_S_copy_chars(_M_data(), __beg, __end); }
271
+   0x0000000000005aef <+607>:	call   0x36c0 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_S_copy_chars(char*, char const*, char const*)@plt>
272
+
273
+		__catch(...)
274
+		  {
275
+		    _M_dispose();
276
+		    __throw_exception_again;
277
+		  }
278
+	
279
+		_M_set_length(__dnew);
280
+   0x0000000000005af4 <+612>:	mov    0x8(%rsp),%rax
281
+
282
+/usr/include/c++/11/bits/char_traits.h:
283
+	      { __c1 = __c2; }
284
+   0x0000000000005af9 <+617>:	mov    0x10(%rsp),%rdx
285
+
286
+include/gltraits.hpp:
287
+	GLTRAITS_VALUE_SCALAR( GLdouble,  DOUBLE,         DOUBLE,                   ED,   d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
288
+   0x0000000000005afe <+622>:	mov    %rbp,%rsi
289
+   0x0000000000005b01 <+625>:	mov    $0x40000001,%edi
290
+
291
+/usr/include/c++/11/bits/basic_string.h:
292
+	      { _M_string_length = __length; }
293
+   0x0000000000005b06 <+630>:	mov    %rax,0x18(%rsp)
294
+
295
+include/gltraits.hpp:
296
+	GLTRAITS_VALUE_SCALAR( GLdouble,  DOUBLE,         DOUBLE,                   ED,   d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
297
+   0x0000000000005b0b <+635>:	shl    $0x2,%rdi
298
+
299
+/usr/include/c++/11/bits/char_traits.h:
300
+	      { __c1 = __c2; }
301
+   0x0000000000005b0f <+639>:	movb   $0x0,(%rdx,%rax,1)
302
+
303
+include/gltraits.hpp:
304
+	GLTRAITS_VALUE_SCALAR( GLdouble,  DOUBLE,         DOUBLE,                   ED,   d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
305
+   0x0000000000005b13 <+643>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
306
+
307
+/usr/include/c++/11/bits/basic_string.h:
308
+	      { _M_dispose(); }
309
+   0x0000000000005b18 <+648>:	mov    %rbp,%rdi
310
+   0x0000000000005b1b <+651>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
311
+   0x0000000000005b20 <+656>:	jmp    0x5a2e <main()+414>
312
+
313
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
314
+   0x0000000000005b25 <+661>:	lea    0x10(%rsp),%rbp
315
+   0x0000000000005b2a <+666>:	lea    0x20(%rsp),%rax
316
+
317
+include/gltraits.hpp:
318
+	GLTRAITS_VALUE_SCALAR( GLubyte,   UNSIGNED_BYTE,  UNSIGNED_INT,             8UI,  ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
319
+   0x0000000000005b2f <+671>:	mov    $0x3,%edi
320
+
321
+/usr/include/c++/11/bits/basic_string.h:
322
+	      { _M_string_length = __length; }
323
+   0x0000000000005b34 <+676>:	movq   $0x0,0x18(%rsp)
324
+
325
+include/gltraits.hpp:
326
+	GLTRAITS_VALUE_SCALAR( GLubyte,   UNSIGNED_BYTE,  UNSIGNED_INT,             8UI,  ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
327
+   0x0000000000005b3d <+685>:	mov    %rbp,%rsi
328
+
329
+/usr/include/c++/11/bits/basic_string.h:
330
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
331
+   0x0000000000005b40 <+688>:	mov    %rax,0x10(%rsp)
332
+
333
+/usr/include/c++/11/bits/char_traits.h:
334
+	      { __c1 = __c2; }
335
+   0x0000000000005b45 <+693>:	movb   $0x0,0x20(%rsp)
336
+
337
+include/gltraits.hpp:
338
+	GLTRAITS_VALUE_SCALAR( GLubyte,   UNSIGNED_BYTE,  UNSIGNED_INT,             8UI,  ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
339
+   0x0000000000005b4a <+698>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
340
+
341
+/usr/include/c++/11/bits/basic_string.h:
342
+	      { _M_dispose(); }
343
+   0x0000000000005b4f <+703>:	mov    %rbp,%rdi
344
+   0x0000000000005b52 <+706>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
345
+   0x0000000000005b57 <+711>:	jmp    0x59ff <main()+367>
346
+
347
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
348
+   0x0000000000005b5c <+716>:	lea    0x10(%rsp),%rbp
349
+   0x0000000000005b61 <+721>:	lea    0x20(%rsp),%rax
350
+
351
+include/gltraits.hpp:
352
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
353
+   0x0000000000005b66 <+726>:	mov    $0x2,%edi
354
+
355
+/usr/include/c++/11/bits/basic_string.h:
356
+	      { _M_string_length = __length; }
357
+   0x0000000000005b6b <+731>:	movq   $0x0,0x18(%rsp)
358
+
359
+include/gltraits.hpp:
360
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
361
+   0x0000000000005b74 <+740>:	mov    %rbp,%rsi
362
+
363
+/usr/include/c++/11/bits/basic_string.h:
364
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
365
+   0x0000000000005b77 <+743>:	mov    %rax,0x10(%rsp)
366
+
367
+/usr/include/c++/11/bits/char_traits.h:
368
+	      { __c1 = __c2; }
369
+   0x0000000000005b7c <+748>:	movb   $0x0,0x20(%rsp)
370
+
371
+include/gltraits.hpp:
372
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
373
+   0x0000000000005b81 <+753>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
374
+
375
+/usr/include/c++/11/bits/basic_string.h:
376
+	      { _M_dispose(); }
377
+   0x0000000000005b86 <+758>:	mov    %rbp,%rdi
378
+   0x0000000000005b89 <+761>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
379
+   0x0000000000005b8e <+766>:	jmp    0x59c8 <main()+312>
380
+
381
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
382
+   0x0000000000005b93 <+771>:	lea    0x10(%rsp),%rbp
383
+   0x0000000000005b98 <+776>:	lea    0x20(%rsp),%rax
384
+
385
+include/gltraits.hpp:
386
+	GLTRAITS_VALUE_VECTOR( glm::ivec, INT,            glm::value_ptr,           32I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
387
+   0x0000000000005b9d <+781>:	mov    $0x3,%edi
388
+
389
+/usr/include/c++/11/bits/basic_string.h:
390
+	      { _M_string_length = __length; }
391
+   0x0000000000005ba2 <+786>:	movq   $0x0,0x18(%rsp)
392
+
393
+include/gltraits.hpp:
394
+	GLTRAITS_VALUE_VECTOR( glm::ivec, INT,            glm::value_ptr,           32I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
395
+   0x0000000000005bab <+795>:	mov    %rbp,%rsi
396
+
397
+/usr/include/c++/11/bits/basic_string.h:
398
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
399
+   0x0000000000005bae <+798>:	mov    %rax,0x10(%rsp)
400
+
401
+/usr/include/c++/11/bits/char_traits.h:
402
+	      { __c1 = __c2; }
403
+   0x0000000000005bb3 <+803>:	movb   $0x0,0x20(%rsp)
404
+
405
+include/gltraits.hpp:
406
+	GLTRAITS_VALUE_VECTOR( glm::ivec, INT,            glm::value_ptr,           32I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
407
+   0x0000000000005bb8 <+808>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
408
+
409
+/usr/include/c++/11/bits/basic_string.h:
410
+	      { _M_dispose(); }
411
+   0x0000000000005bbd <+813>:	mov    %rbp,%rdi
412
+   0x0000000000005bc0 <+816>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
413
+   0x0000000000005bc5 <+821>:	jmp    0x598e <main()+254>
414
+
415
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
416
+   0x0000000000005bca <+826>:	lea    0x10(%rsp),%rbp
417
+   0x0000000000005bcf <+831>:	lea    0x20(%rsp),%rax
418
+
419
+include/gltraits.hpp:
420
+	GLTRAITS_VALUE_MATRIXS(glm::mat,  FLOAT,          glm::value_ptr, GL_FALSE, 32F,  f,  ,  ,         KEEP)
421
+   0x0000000000005bd4 <+836>:	mov    $0x2,%edi
422
+
423
+/usr/include/c++/11/bits/basic_string.h:
424
+	      { _M_string_length = __length; }
425
+   0x0000000000005bd9 <+841>:	movq   $0x0,0x18(%rsp)
426
+
427
+include/gltraits.hpp:
428
+	GLTRAITS_VALUE_MATRIXS(glm::mat,  FLOAT,          glm::value_ptr, GL_FALSE, 32F,  f,  ,  ,         KEEP)
429
+   0x0000000000005be2 <+850>:	mov    %rbp,%rsi
430
+
431
+/usr/include/c++/11/bits/basic_string.h:
432
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
433
+   0x0000000000005be5 <+853>:	mov    %rax,0x10(%rsp)
434
+
435
+/usr/include/c++/11/bits/char_traits.h:
436
+	      { __c1 = __c2; }
437
+   0x0000000000005bea <+858>:	movb   $0x0,0x20(%rsp)
438
+
439
+include/gltraits.hpp:
440
+	GLTRAITS_VALUE_MATRIXS(glm::mat,  FLOAT,          glm::value_ptr, GL_FALSE, 32F,  f,  ,  ,         KEEP)
441
+   0x0000000000005bef <+863>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
442
+
443
+/usr/include/c++/11/bits/basic_string.h:
444
+	      { _M_dispose(); }
445
+   0x0000000000005bf4 <+868>:	mov    %rbp,%rdi
446
+   0x0000000000005bf7 <+871>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
447
+   0x0000000000005bfc <+876>:	jmp    0x5946 <main()+182>
448
+
449
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
450
+   0x0000000000005c01 <+881>:	lea    0x10(%rsp),%rbp
451
+   0x0000000000005c06 <+886>:	lea    0x20(%rsp),%rax
452
+
453
+include/gltraits.hpp:
454
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
455
+   0x0000000000005c0b <+891>:	mov    $0x2,%edi
456
+
457
+/usr/include/c++/11/bits/basic_string.h:
458
+	      { _M_string_length = __length; }
459
+   0x0000000000005c10 <+896>:	movq   $0x0,0x18(%rsp)
460
+
461
+include/gltraits.hpp:
462
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
463
+   0x0000000000005c19 <+905>:	mov    %rbp,%rsi
464
+
465
+/usr/include/c++/11/bits/basic_string.h:
466
+		: allocator_type(std::move(__a)), _M_p(__dat) { }
467
+   0x0000000000005c1c <+908>:	mov    %rax,0x10(%rsp)
468
+
469
+/usr/include/c++/11/bits/char_traits.h:
470
+	      { __c1 = __c2; }
471
+   0x0000000000005c21 <+913>:	movb   $0x0,0x20(%rsp)
472
+
473
+include/gltraits.hpp:
474
+	GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
475
+   0x0000000000005c26 <+918>:	call   0xebc0 <GLBase::check_supported(std::array<int, 2ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>
476
+
477
+/usr/include/c++/11/bits/basic_string.h:
478
+	      { _M_dispose(); }
479
+   0x0000000000005c2b <+923>:	mov    %rbp,%rdi
480
+   0x0000000000005c2e <+926>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
481
+   0x0000000000005c33 <+931>:	jmp    0x590f <main()+127>
482
+
483
+tests/vertex_setup.cpp:
484
+	}
485
+   0x0000000000005c38 <+936>:	call   0x36e0 <__stack_chk_fail@plt>
486
+   0x0000000000005c3d <+941>:	endbr64 
487
+
488
+/usr/include/c++/11/bits/basic_string.h:
489
+	      { _M_dispose(); }
490
+   0x0000000000005c41 <+945>:	mov    %rax,%r12
491
+   0x0000000000005c44 <+948>:	jmp    0x5c6a <main()+986>
492
+   0x0000000000005c46 <+950>:	endbr64 
493
+   0x0000000000005c4a <+954>:	jmp    0x5c41 <main()+945>
494
+   0x0000000000005c4c <+956>:	endbr64 
495
+   0x0000000000005c50 <+960>:	jmp    0x5c41 <main()+945>
496
+   0x0000000000005c52 <+962>:	endbr64 
497
+   0x0000000000005c56 <+966>:	jmp    0x5c41 <main()+945>
498
+   0x0000000000005c58 <+968>:	endbr64 
499
+   0x0000000000005c5c <+972>:	jmp    0x5c41 <main()+945>
500
+   0x0000000000005c5e <+974>:	endbr64 
501
+   0x0000000000005c62 <+978>:	jmp    0x5c41 <main()+945>
502
+   0x0000000000005c64 <+980>:	endbr64 
503
+   0x0000000000005c68 <+984>:	jmp    0x5c41 <main()+945>
504
+   0x0000000000005c6a <+986>:	mov    %rbp,%rdi
505
+   0x0000000000005c6d <+989>:	call   0x3710 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt>
506
+   0x0000000000005c72 <+994>:	mov    %r12,%rdi
507
+   0x0000000000005c75 <+997>:	call   0x3890 <_Unwind_Resume@plt>
508
+End of assembler dump.
... ...
@@ -38,6 +38,28 @@ public:
38 38
     template<std::size_t N>
39 39
     struct Texture;
40 40
 
41
+    //// Member
42
+
43
+    #if __cplusplus >= 201402L
44
+
45
+    template<typename... Value>
46
+    struct Members
47
+    {};
48
+
49
+    template<typename T>
50
+    static constexpr auto members()
51
+    {
52
+        static_assert(
53
+            !std::is_empty<T>::value &&
54
+            std::is_trivially_copyable<T>::value &&
55
+            std::is_standard_layout<T>::value,
56
+            "T must be a non-empty trivially copyable standard-layout type"
57
+        );
58
+        return members_<T>(MakeIndices<member_count<T>()>{});
59
+    }
60
+
61
+    #endif
62
+
41 63
 private:
42 64
 
43 65
     //// Helpers
... ...
@@ -71,6 +93,72 @@ private:
71 93
     >
72 94
     struct Texture_;
73 95
 
96
+    //// Member
97
+
98
+    // Stripped down version of Boost::PFR.
99
+
100
+    #if __cplusplus >= 201402L
101
+
102
+    struct MemberInfo
103
+    {
104
+        GLenum id;
105
+    };
106
+
107
+    template<std::size_t N>
108
+    struct MemberInfos
109
+    {
110
+        MemberInfo infos[N];
111
+    };
112
+
113
+    struct MemberAny
114
+    {
115
+        MemberInfo * info;
116
+
117
+        template<typename Value>
118
+        constexpr operator Value()
119
+        {
120
+            return (
121
+                info->id = GLTraits::Value<Value>::id,
122
+                Value{}
123
+            );
124
+        }
125
+    };
126
+
127
+    template<typename T, std::size_t I0, std::size_t... Is>
128
+    static constexpr auto member_count_(Indices<I0, Is...>)
129
+        -> decltype(T{(I0, MemberAny{}), (Is, MemberAny{})...}, std::size_t{})
130
+    {
131
+        return 1 + sizeof...(Is);
132
+    }
133
+
134
+    template<typename T, std::size_t... Is>
135
+    static constexpr auto member_count_(Indices<Is...>)
136
+    {
137
+        return member_count_<T>(MakeIndices<sizeof...(Is) - 1>{});
138
+    }
139
+
140
+    template<typename T>
141
+    static constexpr auto member_count()
142
+    {
143
+        return member_count_<T>(MakeIndices<sizeof(T)>{});
144
+    }
145
+
146
+    template<typename T, std::size_t... Is>
147
+    static constexpr auto member_infos(MemberInfos<sizeof...(Is)> infos = {})
148
+    {
149
+        return ((void)T{MemberAny{&infos.infos[Is]}...}, infos);
150
+    }
151
+
152
+    template<typename T, std::size_t... Is>
153
+    static constexpr auto members_(Indices<Is...>)
154
+    {
155
+        return Members<
156
+            typename ValueID<member_infos<T, Is...>().infos[Is].id>::Value...
157
+        >{};
158
+    }
159
+
160
+    #endif
161
+
74 162
 };
75 163
 
76 164
 //// Helpers
... ...
@@ -1,3 +1,4 @@
1
+#include <cstddef>
1 2
 #include <iomanip>
2 3
 #include <iostream>
3 4
 #include <string>
... ...
@@ -108,6 +109,40 @@ struct GLTraitsTest : protected GLBase
108 109
             GLTRAITS_TEST_TEXTURE(compressed_texture_sub_image);
109 110
     }
110 111
 
112
+    #if __cplusplus >= 201402L
113
+    template<
114
+        typename    Value,
115
+        typename... Values
116
+    >
117
+    void static test_members(
118
+        GLTraits::Members<Value, Values...>,
119
+        std::size_t offset = 0
120
+    )
121
+    {
122
+        auto unaligned = offset % alignof(Value);
123
+        if (unaligned)
124
+            offset += alignof(Value) - unaligned;
125
+        auto end = offset + sizeof(Value);
126
+        using Traits = GLTraits::Value<Value>;
127
+        static_assert(
128
+            std::is_empty<GLTraits::Members<
129
+                Value, Values...
130
+            >>::value,
131
+            "GLTraits::Members must be empty"
132
+        );
133
+        #define GLTRAITS_TEST_MEMBERS(IND, NAME, OFFSET, END) \
134
+            std::cout \
135
+                << std::left  << std::setw(IND)           << "" \
136
+                << std::left  << std::setw(12 - IND)      << NAME   << " " \
137
+                << std::right << std::setw(2) << std::dec << OFFSET << " " \
138
+                << std::right << std::setw(3) << std::dec << END    << "\n";
139
+        GLTRAITS_TEST_MEMBERS(2, Traits::name, offset, end)
140
+        test_members(GLTraits::Members<Values...>{}, end);
141
+    }
142
+    void static test_members(GLTraits::Members<>, std::size_t)
143
+    {}
144
+    #endif
145
+
111 146
 };
112 147
 
113 148
 
... ...
@@ -135,4 +170,36 @@ int main()
135 170
     GLTraitsTest::test_texture<1>();
136 171
     GLTraitsTest::test_texture<2>();
137 172
     GLTraitsTest::test_texture<3>();
173
+
174
+    #if __cplusplus >= 201402L
175
+    struct Vertex
176
+    {
177
+        glm::vec3  position;
178
+        glm::vec2  tex_coord;
179
+        glm::mat3  tbn;
180
+        glm::ivec4 bone_indices;
181
+        glm::vec4  bone_weights;
182
+        GLubyte    flags;
183
+        GLdouble   unaligned;
184
+    };
185
+    #define GLTRAITS_TEST_MEMBERS_T(T) \
186
+        GLTRAITS_TEST_MEMBERS(0, #T, 0, sizeof(T))
187
+    #define GLTRAITS_TEST_MEMBERS_OFFSETOF(T, MEMBER) \
188
+        GLTRAITS_TEST_MEMBERS( \
189
+            2, \
190
+            GLTraits::Value<decltype(T::MEMBER)>::name, \
191
+            offsetof(T, MEMBER), \
192
+            offsetof(T, MEMBER) + sizeof(T::MEMBER) \
193
+        )
194
+    GLTRAITS_TEST_MEMBERS_T(Vertex)
195
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, position)
196
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, tex_coord)
197
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, tbn)
198
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, bone_indices)
199
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, bone_weights)
200
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, flags)
201
+    GLTRAITS_TEST_MEMBERS_OFFSETOF(Vertex, unaligned)
202
+    GLTRAITS_TEST_MEMBERS_T(Vertex)
203
+    GLTraitsTest::test_members(GLTraits::members<Vertex>());
204
+    #endif
138 205
 }
139 206
new file mode 100644
... ...
@@ -0,0 +1,48 @@
1
+#include <cstddef>
2
+
3
+#include <glm/glm.hpp>
4
+
5
+#include <glbase.hpp>
6
+#include <gltraits.hpp>
7
+
8
+
9
+template<
10
+    typename    Value,
11
+    typename... Values
12
+>
13
+void vertex_setup(
14
+    GLTraits::Members<Value, Values...>,
15
+    std::size_t stride,
16
+    std::size_t offset   = 0,
17
+    GLint       location = 0
18
+)
19
+{
20
+    auto unaligned = offset % alignof(Value);
21
+    if (unaligned)
22
+        offset += alignof(Value) - unaligned;
23
+    GLTraits::Value<Value>::vertex_attrib_pointer(location, offset, stride);
24
+    vertex_setup(
25
+        GLTraits::Members<Values...>{},
26
+        stride,
27
+        offset   + sizeof(Value),
28
+        location + GLTraits::Value<Value>::columns
29
+    );
30
+}
31
+void vertex_setup(GLTraits::Members<>, std::size_t, std::size_t, GLint)
32
+{}
33
+
34
+
35
+int main()
36
+{
37
+    struct Vertex
38
+    {
39
+        glm::vec3  position;
40
+        glm::vec2  tex_coord;
41
+        glm::mat3  tbn;
42
+        glm::ivec4 bone_indices;
43
+        glm::vec4  bone_weights;
44
+        GLubyte    flags;
45
+        GLdouble   unaligned;
46
+    };
47
+    vertex_setup(GLTraits::members<Vertex>(), sizeof(Vertex));
48
+}