mikeash.com pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html commentshttp://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsmikeash.com Recent CommentsThu, 28 Mar 2024 18:49:29 GMTPyRSS2Gen-1.0.0http://blogs.law.harvard.edu/tech/rssmikeash - 2009-10-28 15:04:49http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsDefinitely not missing a comma. Imagine this usage of the macro: <br /> <br /><code>DEBUG_LOG("the answer is %d", fortyTwo);</code> <br /> <br />With the comma, the line would expand to: <br /> <br /><code>fprintf(stderr, "Debug log:", "the answer is %d", fortyTwo);</code> <br /> <br />With the result that it will print only the text, "Debug log:", and nothing else. <br /> <br />Without the comma, it expands to this: <br /> <br /><code>fprintf(stderr, "Debug log:" "the answer is %d", fortyTwo);</code> <br /> <br />The adjacent string literals get concatenated, and so it prints, "Debug log:the answer is 42". <br /> <br />It ought to have a space after the colon, though....24fe5c2efa0e1db3b7960714698eaf9cWed, 28 Oct 2009 15:04:49 GMTJeanne d'Arc - 2009-10-28 10:41:28http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsThere's probably a missing ',' here: <br /> <br />fprintf(stderr, "Debug log:" __VA_ARGS__); \a65dd339f860523b6530e9a2c7ed9e8fWed, 28 Oct 2009 10:41:28 GMTmikeash - 2009-08-25 14:07:38http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsIt's likely that clang supports such devices as well. Their stated goal is to support nearly all gcc language extensions so that clang is able to build nearly all existing gcc-targeted code. <br /> <br />Your ARRAY macro ought to work for empty arrays as well, but you have to pass an explicit nil parameter instead of just leaving the list empty. If you really wanted to you could pair it with a custom vararg function/method that allowed an empty parameter list in the macro.1dcd9b21fca02d5bbd9445de3387745aTue, 25 Aug 2009 14:07:38 GMTVasi - 2009-08-25 07:31:09http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsA drawback of using do/while is that your macro can't return a value. (I also find do/while in macros aesthetically unpleasant, but that's just me.) An alternative is to use GCC's multi-statement expressions: <a href="http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Statement-Exprs.html#Statement-Exprs">http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Statement-Exprs.html#Statement-Exprs</a> . This lets you do things like: <br /> <br /><div class="blogcommentquote"><div class="blogcommentquoteinner"> <br />#define ARRAY_DEBUG(...) ({ <br />&nbsp;&nbsp;&nbsp;&nbsp;NSArray *_a = [NSArray arrayWithObjects: __VA_ARGS__]; <br />&nbsp;&nbsp;&nbsp;&nbsp;if (gDebug) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dumpArray(_a); <br />&nbsp;&nbsp;&nbsp;&nbsp;_a; <br />}) <br /> <br />... <br /> <br />NSArray *myArray = ARRAY_DEBUG(@"foo", @"bar", nil); <br /></div></div> <br /> <br />The downside is of course that this is non-portable. I have no idea whether or not clang supports this extension. <br /> <br /> <br />On a different note, one very common use for vararg macros is to abbreviate nil-terminated vararg functions. Eg: <br /> <br /><div class="blogcommentquote"><div class="blogcommentquoteinner"> <br />#define ARRAY(...) [NSArray arrayWithObjects: __VA_ARGS__, nil] <br /></div></div> <br /> <br />This has the drawback of not working for empty arrays, but there are easier ways to create those. Jens Alfke's utilities may be better <a href="https://bitbucket.org/snej/myutilities/src/tip/CollectionUtils.h">https://bitbucket.org/snej/myutilities/src/tip/CollectionUtils.h</a> <br /> <br />6b0112967c4da22490bab174f9ce5620Tue, 25 Aug 2009 07:31:09 GMTmikeash - 2009-08-24 15:41:27http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsHeh. Whoops. Thanks Peter! I've fixed all three now.e14fa5ef3121ff4159f316307687bda7Mon, 24 Aug 2009 15:41:27 GMTPeter N Lewis - 2009-08-24 14:01:38http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsRegardless of whether the DEBUG_LOG uses { } or do while, it really should have an equal number of opening and closing currly brackets. <br /> <br />It is also definitely worth mentioning the NS_REQUIRES_NIL_TERMINATION macro! <br />7858b3a2fbb0c623fdae5ade96f150d9Mon, 24 Aug 2009 14:01:38 GMTgabe - 2009-08-23 09:24:24http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsSome more detail on multi statement macros from comp.lang.c faq, <a href="http://c-faq.com/cpp/multistmt.html">http://c-faq.com/cpp/multistmt.html</a> <br /> <br />319c2a2f3266b0eebf835f90594290f4Sun, 23 Aug 2009 09:24:24 GMTmikeash - 2009-08-22 23:06:44http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsRegardless of the actual calling conventions, you should never write code that calls functions without prototypes. Aside from being non-conforming code even if it does happen to work on your particular platform, it's also all too easy to screw up your types. For example, it is impossible to correctly call a function which takes a float parameter in this way, because a float passed to a prototype-less function will always be promoted to double. It's also easy to accidentally pass 0 instead of 0.0, which would normally cause a harmless implicit conversion but in the absence of a prototype will screw you over hard.8709f47dee0bf723211e3a78e6c06fa9Sat, 22 Aug 2009 23:06:44 GMTJean-Daniel Dupas - 2009-08-22 22:31:59http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#comments<div class="blogcommentquote"><div class="blogcommentquoteinner">In particular, on platforms that use registers to pass arguments they tend to shove all of the args into GPRs even though the standard calling convention might have you place singles and doubles in FPRs</div></div> <br /> <br />That not quite true. See for example on PPC: <br /> <br />printf("%f", 10.0); <br /> <br />Generate this assembly: <br />=&gt; fmr f1,f0 <br />&nbsp;&nbsp;&nbsp;&nbsp;bl _fprintf$LDBL128 <br /> <br />And it uses mmx register on x86_64. <br /> <br />So you can see that even on arch that uses register to pass argument, floating point register are use to pass floating point values. <br /> <br />f109b654765abdbcbf5e9ccb7994cb0cSat, 22 Aug 2009 22:31:59 GMTLouis Gerbarg - 2009-08-22 19:41:38http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsIt might be worth mentioning that on a number of architectures variadic functions have substantially different calling conventions that non-variadic functions. In particular, on platforms that use registers to pass arguments they tend to shove all of the args into GPRs even though the standard calling convention might have you place singles and doubles in FPRs, since the compiler doesn't have any type info for the va_args. <br /> <br />This manifests itself in several ways. The first is slightly less efficient code (copying stuff back and forth from fprs to gprs, or extra copies to the stack), the second is that if you ignore missing prototype warnings (everyone compiles with -Wall=error, right) you may get code that links correctly but crashes because the calling function doesn't put things where the called function expects them.445cb2f3c5ac5f2d1ba214f0466d8e3dSat, 22 Aug 2009 19:41:38 GMTJonas Witt - 2009-08-22 01:30:50http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsOk, so you were referring to the semicolon which is required after the do/while but won't work after the { } block. Thanks for the explanation.6d81fe157aa0128bb660ff13b7257a9eSat, 22 Aug 2009 01:30:50 GMTmikeash - 2009-08-22 01:07:54http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsNo, curly braces do <i>not</i> achieve the same effect. Go look at my example if/else code and consider what happens if you use your style in there. It will not compile.73f2b32a65933179b2fa47b4805ee9e0Sat, 22 Aug 2009 01:07:54 GMTCedric Luthi - 2009-08-22 00:42:55http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsA nice complement to this article: Variable argument lists in Cocoa <br /><a href="http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html">http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html</a> <br /> <br />Worth a read.96b4a941cfcfae82b58b60c983ab0fbbSat, 22 Aug 2009 00:42:55 GMTJonas Witt - 2009-08-22 00:35:36http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsYou say that using do/while(0) is a great way to put multiple statements in a macro (if you don't want it to fail in places where a single statement is expected), but the same can be achieved simply by using a pair of curly braces (which you use for while anyway since while expects a single statement/block) to create a new scope/block: <br /> <br />#define FOO { method1(); method2(); } <br /> <br />IMO this is idential to the while construct in every aspect (variable scope, ...). <br /> <br />0ed2f551b3e49708158e3f4aaa523e53Sat, 22 Aug 2009 00:35:36 GMTmikeash - 2009-08-22 00:30:46http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsI actually discuss the 0 problem, in a slightly different context, in the Beware of NULL section of Format Strings Tips and Tricks from a month ago: <br /> <br /><a href="http://www.mikeash.com/?page=pyblog/friday-qa-2009-07-17-format-strings-tips-and-tricks.html">http://www.mikeash.com/?page=pyblog/friday-qa-2009-07-17-format-strings-tips-and-tricks.html</a>85d2fe709230d7d399be4fd9eb6eb190Sat, 22 Aug 2009 00:30:46 GMTChris - 2009-08-22 00:10:24http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsAnother omission is the "gotcha" related to (IIRC) argument promotion and callers passing an integer 0 instead of a zero *pointer* to terminate the argument list. <br /> <br />This causes problems (again, IIRC) on 64-bit systems.1707c16d72731b351635ac674fd483d5Sat, 22 Aug 2009 00:10:24 GMTJordy/Jediknil - 2009-08-21 23:59:28http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-21-writing-vararg-macros-and-functions.html#commentsVery convenient, but you left out the most confusing one: vararg methods! <br /> <br />Like vararg functions, vararg methods require at least one fixed parameter. The va_list/va_start/va_arg API is the same. And they are declared like this: <br /> <br />- (NSString *)stringByAppendingStrings:(NSString *)first, ...; <br /> <br />And the restriction of one fixed argument is a lot more arbitrary here, because, as we well know, an Objective-C method is passed two hidden arguments (self and _cmd). In theory I would expect va_start(_cmd) to work, but I wouldn't use it in practice, especially given potential compiler differences in this field! <br /> <br />Finally, there's a nice macro called NS_REQUIRES_NIL_TERMINATION that expands to a GCC __attribute__ ((sentinel)), which will warn when any invocations of the function aren't nil-terminated (if you have -Wformat turned on). This works for both functions and methods.78653f018a7685adcd4388734c17cbd7Fri, 21 Aug 2009 23:59:28 GMT