Implemented pre-C99 support for macro expansion for callbacks.
[freeglut] / src / fg_callback_macros.h
1 /*
2  * fg_callback_macros.h
3  *
4  * The freeglut library callback macro file.
5  *
6  * Copyright (C) 2016 Vincent Simonetti
7  * Creation date: Sat Jan 16 2016
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26
27 #ifndef FREEGLUT_CALLBACK_MACROS_H
28 #define FREEGLUT_CALLBACK_MACROS_H
29
30 #ifndef FREEGLUT_INTERNAL_H
31 #error "fg_internal.h needs to be included before this header"
32 #endif
33
34 /*
35  * Compiler defines:
36  * FG_COMPILER_SUPPORTS_GCC_VA_ARGS_HACK: if the compiler supports GCC's varadic macro implementation (AKA, ##__VA_ARGS__)
37  * FG_COMPILER_SUPPORTS_VA_ARGS: if the compiler supports varadic macros
38  */
39
40 /*
41  * Info:
42  *
43  * This took a while to figure out, so be sure try to understand what is happening so that you can ensure that whatever you
44  * change won't break other areas.
45  * 
46  * If you are just adding a new callback/changing it's argument count, just go to the bottom of the file.
47  *
48  * This whole file exists purely for the sake of preventing the need to implement additional parsing logic for each callback
49  * to pass user arguments. Of course, the necessity to support older compilers means that, as seen in the line above, there
50  * is still a requirement to add/modify code to handle callbacks. If freeglut ever requires newer compilers (at minimum, ones
51  * that support C99 or higher), code can very slowly be removed from this file. Even better would be if the C standard eventually
52  * supports something similar to what GCC has implemented or offers an alternative. Another option is if C++ would be "allowed" by
53  * project maintaners, as then templates can be used and function overloading. Ironically, the template would probably look worse
54  * then the GCC macro, so maybe it's good to stay as is.
55  *
56  * Onto the different "versions" of macros:
57  * 
58  * There is one for GCC/Clang(/and supposedly the Intel compiler) which supports the non-standard ##__VA_ARGS__ token. The code may 
59  * look ugly, but the result is, if this was standard, no one would ever need to open this file unless they were curious (or needed 
60  * more then 5 arguments for a callback, but that's trivial to add). It works by adding many fake macros to a "picker" macro
61  * (PP_HAS_ARGS_IMPL2) which then indictaes which macro counter to use. As we can already use varadic macros (the VA in __VA_ARGS__),
62  * this just becomes a "reuse the arguments*.
63  * 
64  * The next is for any non-GCC/Clang/etc. compiler *cough* MSVC/compiler you probably shouldn't be using *cough* that supports C99
65  * by default. It requires each callback to have a specific argument count passthrough macro. The only reason there are specific
66  * count macros is so that (see paraghraph below) don't need have their own set of callback macros. Ideally, there would only be
67  * ZERO and ONE_OR_MORE. This works by having callback-specific macros call a specific handler macro to return user data (ZERO) or
68  * return one or more arguments along with userData (ONE_OR_MORE) where, with varadic macros, it just reuses the arguments.
69  *
70  * The last set is for the poor individual who has to use a compiler that doesn't support C99 by default, or may not support it at
71  * all. Stuff like MSVC6... It works by having a specific-count macro that "extracts" each argument to have them reused without the
72  * parathesis.
73  *
74  * A note on parathesis, as earlier mentioned, if the GCC varadic macro element was standard, then instead of needing:
75  *
76  * func EXPAND_WCB(Mouse)(( (GLUT_LEFT_BUTTON, GLUT_DOWN, 10, 30), userData));
77  *
78  * ...you can do the following:
79  *
80  * func EXPAND_WCB (GLUT_LEFT_BUTTON, GLUT_DOWN, 10, 30);
81  *
82  * Wow... so much nice and easier to understand. Sub-note: I have not worked on a version that explicitly takes userData, so for now
83  * if you can get to that version, look in the version control change history for this file and you'll find that version which
84  * implicitly passes "userData" and only works on GCC vardiac macro supporting compilers.
85  */
86
87 #ifdef FG_COMPILER_SUPPORTS_GCC_VA_ARGS_HACK
88
89  /*
90  * EXPAND_WCB() is used as:
91  * 
92  *     EXPAND_WCB( cbname )(( arg_list, userData ))
93  * 
94  * ... where {(arg_list)} is the parameter list and userData is user
95  * provided data.
96  *
97  * All additional macros are to get around trailing ',' for zero-arg
98  * callbacks.
99  *
100  * Modification of:
101  * http://stackoverflow.com/questions/5355241/generating-function-declaration-using-a-macro-iteration/5355946#5355946
102  *
103  * --------------
104  *
105  * GCC-specific design that doesn't require per-callback defines
106  *
107  * The naming is terrible... and it's very convuluted and complex, but
108  * should not require any modification unless additional arguments are to 
109  * be supported.
110  * 
111  * Supports callbacks up to 5 args (follow the pattern of PP_HAS_ARGS_IMPL2
112  * and PP_HAS_ARGS_SOURCE to add more)
113  *
114  * Edit with care.
115  */
116
117 #define EXPAND_WCB_UNPARAN(...) __VA_ARGS__
118
119 #define PP_HAS_ARGS_IMPL2(_0, _1, _2, _3, _4, _5, N, ...) N
120 #define PP_HAS_ARGS_SOURCE() ONE_OR_MORE, ONE_OR_MORE, ONE_OR_MORE, ONE_OR_MORE, ONE_OR_MORE, ZERO
121
122 #define PP_HAS_ARGS_IMPL(...) PP_HAS_ARGS_IMPL2( __VA_ARGS__ )
123 #define PP_HAS_ARGS(...) PP_HAS_ARGS_IMPL( NOT_EXIST, ##__VA_ARGS__, PP_HAS_ARGS_SOURCE() )
124
125 #define EXPAND_WCB_ZERO(args, userData) ( userData )
126 #define EXPAND_WCB_ONE_OR_MORE(args, userData) ( EXPAND_WCB_UNPARAN args, userData )
127
128 #define EXPAND_WCB_DISAMBIGUATE2(has_args, args, userData) EXPAND_WCB_ ## has_args ( args, userData )
129 #define EXPAND_WCB_DISAMBIGUATE(has_args, args, userData) EXPAND_WCB_DISAMBIGUATE2( has_args, args, userData )
130
131 #define EXPAND_WCB_UNWRAP_ARGS2(args, userData) EXPAND_WCB_DISAMBIGUATE( PP_HAS_ARGS args, args, userData )
132 #define EXPAND_WCB_UNWRAP_ARGS(args) EXPAND_WCB_UNWRAP_ARGS2 args
133
134 #define EXPAND_WCB(cbname) EXPAND_WCB_UNWRAP_ARGS
135
136 #else
137
138 /*
139  * EXPAND_WCB() is used as:
140  * 
141  *     EXPAND_WCB( cbname )(( arg_list, userData ))
142  * 
143  * ... where {(arg_list)} is the parameter list and userData is user
144  * provided data.
145  *
146  * This will take the arg_list and extend it by one argument, adding
147  * the argument "userData" to the end of the list.
148  *
149  * In order for this to work, each callback must have a define that
150  * properly handles the arguments as needed by the callback.
151  * This callback is in the format of EXPAND_WCB_SUB_<cbname>.
152  * Helper functions exist for zero to five parameters: EXPAND_WCB_ZERO,
153  * EXPAND_WCB_ONE, EXPAND_WCB_TWO, EXPAND_WCB_THREE< EXPAND_WCB_FOUR,
154  * and EXPAND_WCB_FIVE. Each handle the callback argument counts.
155  *
156  * An example for the "Entry" callback, where "Entry" is the cbname:
157  * typedef void (* FGCBEntry  )( int );
158  * typedef void (* FGCBEntryUC)( int, FGCBUserData );
159  * #define EXPAND_WCB_SUB_Entry(args) EXPAND_WCB_ONE args
160  */
161
162 #ifdef FG_COMPILER_SUPPORTS_VA_ARGS
163
164 #define EXPAND_WCB_UNPARAN(...) __VA_ARGS__
165 #define EXPAND_WCB_ONE_OR_MORE(args, userData) ( EXPAND_WCB_UNPARAN args, userData )
166
167 #define EXPAND_WCB_ONE(args, userData) EXPAND_WCB_ONE_OR_MORE( args, userData )
168 #define EXPAND_WCB_TWO(args, userData) EXPAND_WCB_ONE_OR_MORE( args, userData )
169 #define EXPAND_WCB_THREE(args, userData) EXPAND_WCB_ONE_OR_MORE( args, userData )
170 #define EXPAND_WCB_FOUR(args, userData) EXPAND_WCB_ONE_OR_MORE( args, userData )
171 #define EXPAND_WCB_FIVE(args, userData) EXPAND_WCB_ONE_OR_MORE( args, userData )
172
173 #else
174
175 #define EXTRACT_ONE_ARGS(arg1) arg1
176 #define EXTRACT_TWO_ARGS(arg1, arg2) arg1, arg2
177 #define EXTRACT_THREE_ARGS(arg1, arg2, arg3) arg1, arg2, arg3
178 #define EXTRACT_FOUR_ARGS(arg1, arg2, arg3, arg4) arg1, arg2, arg3, arg4
179 #define EXTRACT_FIVE_ARGS(arg1, arg2, arg3, arg4, arg5) arg1, arg2, arg3, arg4, arg5
180
181 #define EXPAND_WCB_ONE(args, userData) (EXTRACT_ONE_ARGS args, userData)
182 #define EXPAND_WCB_TWO(args, userData) (EXTRACT_TWO_ARGS args, userData)
183 #define EXPAND_WCB_THREE(args, userData) (EXTRACT_THREE_ARGS args, userData)
184 #define EXPAND_WCB_FOUR(args, userData) (EXTRACT_FOUR_ARGS args, userData)
185 #define EXPAND_WCB_FIVE(args, userData) (EXTRACT_FIVE_ARGS args, userData)
186
187 #endif
188
189 #define EXPAND_WCB_ZERO(args, userData) ( userData )
190
191 #define EXPAND_WCB(cbname) EXPAND_WCB_SUB_ ## cbname
192
193 /* 
194  * Freeglut callbacks type definitions macros
195  *
196  * Every time a callback is updated in fg_internal.h is updated, this needs updated
197  * if argument counts change, new callbacks are added, or callbacks are removed.
198  */
199
200 #define EXPAND_WCB_SUB_Display(args) EXPAND_WCB_ZERO args
201 #define EXPAND_WCB_SUB_Reshape(args) EXPAND_WCB_TWO args
202 #define EXPAND_WCB_SUB_Position(args) EXPAND_WCB_TWO args
203 #define EXPAND_WCB_SUB_Visibility(args) EXPAND_WCB_ONE args
204 #define EXPAND_WCB_SUB_Keyboard(args) EXPAND_WCB_THREE args
205 #define EXPAND_WCB_SUB_KeyboardUp(args) EXPAND_WCB_THREE args
206 #define EXPAND_WCB_SUB_Special(args) EXPAND_WCB_THREE args
207 #define EXPAND_WCB_SUB_SpecialUp(args) EXPAND_WCB_THREE args
208 #define EXPAND_WCB_SUB_Mouse(args) EXPAND_WCB_FOUR args
209 #define EXPAND_WCB_SUB_MouseWheel(args) EXPAND_WCB_FOUR args
210 #define EXPAND_WCB_SUB_Motion(args) EXPAND_WCB_TWO args
211 #define EXPAND_WCB_SUB_Passive(args) EXPAND_WCB_TWO args
212 #define EXPAND_WCB_SUB_Entry(args) EXPAND_WCB_ONE args
213 #define EXPAND_WCB_SUB_WindowStatus(args) EXPAND_WCB_ONE args
214 #define EXPAND_WCB_SUB_Joystick(args) EXPAND_WCB_FOUR args
215 #define EXPAND_WCB_SUB_OverlayDisplay(args) EXPAND_WCB_ZERO args
216 #define EXPAND_WCB_SUB_SpaceMotion(args) EXPAND_WCB_THREE args
217 #define EXPAND_WCB_SUB_SpaceRotation(args) EXPAND_WCB_THREE args
218 #define EXPAND_WCB_SUB_SpaceButton(args) EXPAND_WCB_TWO args
219 #define EXPAND_WCB_SUB_Dials(args) EXPAND_WCB_TWO args
220 #define EXPAND_WCB_SUB_ButtonBox(args) EXPAND_WCB_TWO args
221 #define EXPAND_WCB_SUB_TabletMotion(args) EXPAND_WCB_TWO args
222 #define EXPAND_WCB_SUB_TabletButton(args) EXPAND_WCB_FOUR args
223 #define EXPAND_WCB_SUB_Destroy(args) EXPAND_WCB_ZERO args
224 #define EXPAND_WCB_SUB_MultiEntry(args) EXPAND_WCB_TWO args
225 #define EXPAND_WCB_SUB_MultiButton(args) EXPAND_WCB_FIVE args
226 #define EXPAND_WCB_SUB_MultiMotion(args) EXPAND_WCB_THREE args
227 #define EXPAND_WCB_SUB_MultiPassive(args) EXPAND_WCB_THREE args
228 #define EXPAND_WCB_SUB_InitContext(args) EXPAND_WCB_ZERO args
229 #define EXPAND_WCB_SUB_AppStatus(args) EXPAND_WCB_ONE args
230
231 #endif
232
233 #endif /* FREEGLUT_CALLBACK_MACROS_H */
234
235 /*** END OF FILE ***/