Curious : What is the dreaded elipsis construct?

Started by
22 comments, last by Jan Wassenberg 18 years, 7 months ago
I've seen some posts with the mention of the "dreaded elipsis construct". What is it, and why is it 'dreaded'? --random [Edited by - random_thinker on August 26, 2005 3:27:00 PM]
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
Advertisement
It's vararg functions, such as printf, which take an arbitrary number of arguments. And it's dreaded in C++ because it completely discards type-safety in favor of a bizarre, complex, and unintuitive system of type coercion and unsafe getter functions.
The ellipsis construct is used in varidic functions like printf(). You using it like:

int my_function(int some_argument, ...);

And the ellipsis construct sucks up all the parameters after the initial arguments. This is one of the legacies C++ has inherited from C. The down side is that the ellipsis completely destroys type safety. There is no knowledge of type except what the function can guess from the non-ellipsis arguments. This construct does not interact well with pretty much any C++ features. In particular trying to pass a class type to a function with ellipsis arguments is bad bad mojo, since the constructor will probably be called when placing it on the stack and the destructor will not be called since the varidic function won't know how to clean it up.
Always thought it should have been ellipsis but I was never sure.
I've been wondering about this myself but never brought myself to asking. Now I know. Thanks guys! [smile]
Hack my projects! Oh Yeah! Use an SVN client to check them out.BlockStacker
Thanks Chaps..

When you mentioned variadic functions, I looked this up on the GNU.org website:

#include <stdarg.h>#include <stdio.h>     intadd_em_up (int count,...){    va_list ap;    int i, sum;      va_start (ap, count);         /* Initialize the argument list. */         sum = 0;    for (i = 0; i < count; i++)      sum += va_arg (ap, int);    /* Get the next argument value. */         va_end (ap);                  /* Clean up. */    return sum;}     intmain (void){   /* This call prints 16. */   printf ("%d\n", add_em_up (3, 5, 5, 6));        /* This call prints 55. */   printf ("%d\n", add_em_up (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));      return 0;}


I understand what you mean now...passing objects here could be a disaster!

Thanks Chaps!

--random
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
Quote:Original post by random_thinker
Thanks Chaps..

When you mentioned variadic functions, I looked this up on the GNU.org website:

*** Source Snippet Removed ***

I understand what you mean now...passing objects here could be a disaster!

Thanks Chaps!

--random


Try passing a std::string by value to printf. The results may surprise you.

MSN
Quote:In particular trying to pass a class type to a function with ellipsis arguments is bad bad mojo, since the constructor will probably be called when placing it on the stack and the destructor will not be called since the varidic function won't know how to clean it up.

I think this reflects lack of understanding of the C calling convention. Despite the presence of "RET n" instructions that return from callee and adjust the stack (-> smaller code), the caller is responsible for cleanup. This is precisely because variadic functions do not know what was passed, but the caller (who pushed those params) does.

And I feel obligated to mention this every time the topic comes up: 1) printf has power unmatched by cout << and is quite concise 2) non-crappy compilers do type checking on known variadic functions!
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
Quote:Original post by Jan Wassenberg
Quote:In particular trying to pass a class type to a function with ellipsis arguments is bad bad mojo, since the constructor will probably be called when placing it on the stack and the destructor will not be called since the varidic function won't know how to clean it up.

I think this reflects lack of understanding of the C calling convention. Despite the presence of "RET n" instructions that return from callee and adjust the stack (-> smaller code), the caller is responsible for cleanup. This is precisely because variadic functions do not know what was passed, but the caller (who pushed those params) does.

And I feel obligated to mention this every time the topic comes up: 1) printf has power unmatched by cout << and is quite concise 2) non-crappy compilers do type checking on known variadic functions!


Jan,

Would you have a code example of how you'd clean up after using an elipsis construct on C++ objects? I'm curious as to how this should be done.

--random
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
Quote:Original post by Jan Wassenberg
I think this reflects lack of understanding of the C calling convention. Despite the presence of "RET n" instructions that return from callee and adjust the stack (-> smaller code), the caller is responsible for cleanup. This is precisely because variadic functions do not know what was passed, but the caller (who pushed those params) does.


Whether or not it's supposed to happen. I've seen the kind of lack of destructor call problem happen in real code. IMO, it's not worth the risk to assume that the calling code will clean up properly.

This topic is closed to new replies.

Advertisement