Sign in to follow this  

'...' in C++.

This topic is 4336 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Guest Anonymous Poster
You don't, because "..." is not C++. Using the dread ellipses construct "..." in C++ is punishable by death or just a beating handed to you by Fruny or Zahlman. If you want something like that you'll probably have to use streams.

Share this post


Link to post
Share on other sites
To be a little more informative: A function declared this way is called a "variadic" function. To make them work you need to do some work with some rather nasty macros (although it's not a very complicated process). Basically what happens is that all the things in that are passed get tossed onto the stack in order, regardless of their types or sizes, and it's up to the function to figure out how many things are there and what their types and sizes are. Since there isn't a reasonable way to do this in general, usually what these functions do is accept some information in the first argument (such as a printf-style 'format string') that is then interpreted to figure out what needs to go on.

As I'm sure you can imagine, this is tricky to get right, and it also can cause all kinds of undefined behaviour if the format string doesn't properly match the provided data (not to mention the annoyance of having to put it in in the first place, for those functions that *don't* format a string but just want extra arguments). It also just plain doesn't work with lots of C++ types - basically anything that isn't a POD type, because copy constructors aren't going to be invoked properly. Good luck dealing with references, too.

So yeah, as a rule of thumb, just don't freakin' do it, or even think about it. Set up some kind of streaming interface instead. If you do need printf-style formatted I/O (e.g. because you need "compound strings" for localization, where the order of outputted things might vary by language, and you don't want to have to recompile/change code to support it), you should take a look at boost::format.

Share this post


Link to post
Share on other sites
This seems relevant (as found in several signatures):


"Basically whenever you invoke the dread ellipses construct you leave the happy world of type safety." -- SiCrane

It should not be used in c++, and only very carefully in c.

Share this post


Link to post
Share on other sites
read the above first... read it again....



Now, if you are still really curious, here's an example:

void logMessage( const char* szMessage, ... )
{
static I8 szBuffer[4097];
va_list list;
va_start( list, szMessage );
vsprintf( szBuffer, szMessage, list );

if (LogManager::InstancePtr())
LogManager::Instance().logMessage (szBuffer);

va_end( list );
}

Share this post


Link to post
Share on other sites
Now now, the ellipses construct does have a place; so does goto. You just don't want to use it unless you're really, really sure you need it. In short, someone posting in 'For Beginners' with a question of the form 'How do you...?' is probably a good candidate for passing a linked list instead. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by King of Men
Now now, the ellipses construct does have a place; so does goto. You just don't want to use it unless you're really, really sure you need it. In short, someone posting in 'For Beginners' with a question of the form 'How do you...?' is probably a good candidate for passing a linked list instead. :)


Huh?

Oh no no no, I'm not a beginner, definately not.

I have a templated doubly linked list with id's and iterators, and it's pretty good :).

Then, I have SDL/OpenGL graphics classes to handle sprites, text, stuff. I have an egine class, and event class, and so much more.

Share this post


Link to post
Share on other sites
I just love being able to contribute to a thread without ever having posted in it. However...
Quote:
Original post by Zahlman
It also just plain doesn't work with lots of C++ types - basically anything that isn't a POD type, because copy constructors aren't going to be invoked properly.

Actually, what happens on most compilers is the the copy construction of the argument proceeds correctly. It's just that the destructor isn't called. So not only are you leaving the happy world of type safety, you also have a potential memory leak every time you call a function with the ellipses construct, because you may have accidently passed a class type to it. Though I have noticed on one occassion, that had horrible function pointer typecasting involved, the opposite happening: the copy constructor wasn't called and the destructor was called. However, I'm not sure that the ellipses had anything to do with that, since the code was basically a walking undefined behavior.

Share this post


Link to post
Share on other sites
Someone mentioned the "use a linked list" statement above, and I want to reiterate that ...

And in Standard C++ idioms, use an iterator range ...

Just like the standard algorithm functions do. Then you can work in any situation in which they have a proper C++ style container ...

Sure it still doesn't let you do stuff like you can in .NET with an automatically built "params" array. But it gets you 90% of the way there, with almost no pain.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
You don't, because "..." is not C++.


You've overstated to the point of misinformation without disclaimer or clarification. Bad AP.

OP: They're a bad way to create variable-argument functions, this is a reminant of C best avoided - they're basically a big can of "undefined". They enforce no type safety, do not work with objects (any "working" examples are invoking undefined behavior and are not guaranteed to work on other compilers, nevermind other platforms), and are basically evil. Although legal in C++, one of the few things I'd include in any "Best Practices" guide would be to not use them, except prehaps with SFINAE concepts.

Better Alternatives to using Elipsis:
* Containers [ google "Standard C++ Library" ]
* Operator Chaining [ google "C++ Operator Chaining" ]

Better Alternatives to printf:
* Iostreams [ google "Standard C++ Library" ]
* Boost.Format [ navigate http://www.boost/org/ ]

Share this post


Link to post
Share on other sites
Quote:

Huh?

Oh no no no, I'm not a beginner, definately not.

I have a templated doubly linked list with id's and iterators, and it's pretty good :).

Then, I have SDL/OpenGL graphics classes to handle sprites, text, stuff. I have an egine class, and event class, and so much more.


If your not a beginner (and Im sure you are not), you should be aware that mentioning ... and C++ in the same post will piss of SiCrane and Zahlman to the brink of madness.

Share this post


Link to post
Share on other sites
A small question: has anyone got any examples/cases where varargs functions are useful except printf style functions? (and cases where regular method chaining wouldn't work). I'm not being sarcastic, I'd honestly like to know.

Share this post


Link to post
Share on other sites
Quote:
Original post by OrangyTang
A small question: has anyone got any examples/cases where varargs functions are useful except printf style functions? (and cases where regular method chaining wouldn't work). I'm not being sarcastic, I'd honestly like to know.


Sure.
For example, I've got a base class Base_Msg, all other classes need to derive from it to form a communication web between unrelated objects of different classes:
class Base_Msg
{
public:
void ProcessMessage (MessageType const Msg, ...) ;
private:
virtual void DoProcessMessage (MessageType const Msg, ...) ;
} ;


The Msg is an enumeration of type MessageType, how the message will be processed and the amount of arguments need to be passed depends on the message itself.

A little more detailed: In an arbitrary object of type Player that is managed by an object of type PlayerPool, I need a way to notice the parent PlayerPool when the Player is about to die. I may declare them like this:
class PlayerPool : public Base_Msg
{
public:
private:
void DoProcessMessage (MessageType const Msg, ...) ;
} ;

class Player //No need to derive Basg_Msg here
{
public:
//Constructor
explicit Player (Base_Msg *const pOwner) ;
private:
//Member variables
Base_Msg *m_pOwner ; //This is a pointer to the owner of the class
} ;

Can you see the benefit ?. My object can get any information from any other unrelated objects at anytime without the headaches of forward declaration (sometime you just can't forward declare to make them work).

But I use the additional arguments sparingly, most of the time my MessageType enum has single-argument message only. It promotes some useful uses of virtual arguments though.

Share this post


Link to post
Share on other sites
Uh. No. Not being able to figure out how to use include files properly is a pretty bad reason to use variadic functions. Your code has left the happy world of type safety for no appreciable reason.

Share this post


Link to post
Share on other sites
You're right, I said that I use them sparingly. But there are some situations this is one of the very few viable solutions: The parent and the child objects both need to know about each other in my example.

Basically Windows uses the same technique to transfer messages to and from windows in the system, but it is restricted to have two additional arguments. If a window needs more information, the caller has to build a struct to pass it along with the message.

So, one possible alternative is to declare a number of virtual DoProcessMessage () functions that take two (or possibly three) additional arguments. This has the same advantages of freely communicating between objects and has no disadvantages of losing type-safety.

Share this post


Link to post
Share on other sites
Although it _IS_ C and not C++, the CPython API uses vargs for parsing parameters out of a function call (when calling C from Python). For example (randomly from my engine Python binds),


PyArg_ParseTuple(args, "sifii:_Menu.MessageBox", &msg, &mbType, &timeout,
&timeoutAction, &eventListener)



I do however prefer using printf style formatting for error logging, but I'm somewhat pushing towards replacing it with some kind of streaming system (forgetting to use .c_str() when logging a std::string value makes things go boom...done that too many times -_-).

I'll have to look into boost::format to see if I can possible use/borrow that :).

Share this post


Link to post
Share on other sites
Quote:
Original post by Skeleton_V@T
Basically Windows uses the same technique to transfer messages to and from windows in the system, but it is restricted to have two additional arguments. If a window needs more information, the caller has to build a struct to pass it along with the message.

So, one possible alternative is to declare a number of virtual DoProcessMessage () functions that take two (or possibly three) additional arguments. This has the same advantages of freely communicating between objects and has no disadvantages of losing type-safety.


Or you could wrap your MessageType variable up in a Message class from which all message types are derived, so that you only ever pass a single Message* to DoProcessMessage. Derived types then contain all the data one wishes to attach to the message. DoProcessMessage would then look like this:


void MyObject::DoProcessMessage (const Message* Msg)
{
switch(Msg->TypeCode)
{
case MSG_TYPEONE:
const TypeOneMessage* typeOneMsg = static_cast<const TypeOneMessage*>(Msg);
/* ... do stuff with typeOneMsg ... */
break;
case MSG_TYPETWO:
const TypeTwoMessage* typeTwoMsg = static_cast<const TypeTwoMessage*>(Msg);
/* ... do stuff with typeTwoMsg ... */
break;
}
}


(Suffice it to say that if you were working for me and I saw you writing code like what you've presented here, you wouldn't be working for me for very long).

Share this post


Link to post
Share on other sites
Quote:
Original post by Skeleton_V@T
You're right, I said that I use them sparingly. But there are some situations this is one of the very few viable solutions: The parent and the child objects both need to know about each other in my example.

So? You still don't need to use variadic functions to handle that dependency. Your "solution" also obscures intent. You say your parent and child classes need to know about each other, but what information they need from each other enters the black abyss of the ellipses. Remember source code isn't just written to get the computer to do what it wants, source code should also be written to express intent to other human beings.

Quote:

Basically Windows uses the same technique to transfer messages to and from windows in the system, but it is restricted to have two additional arguments. If a window needs more information, the caller has to build a struct to pass it along with the message.

And you will notice that the Windows API is a C API, not a C++ API. Many of the necessary hacks in C should not be translated to C++ application since C++ presents a richer set of semantics to express intent.

Share this post


Link to post
Share on other sites
Quote:
static_cast<const TypeOneMessage*>(Msg);

You meant dynamic_cast<>, didn't you ?.

Although your approach doesn't really fulfill type-safety, it is inherently better than mine :). I believe you don't mind lending me your idea ?. Thanks for the suggestion.

[Edit]: I see, thanks SiCrane.

Share this post


Link to post
Share on other sites
He means static_cast because the exact type is "known" as a result of the type code in this system. Needless to say, this is fragile and rather silly, because it amounts to implementing your own RTTI - for a situation where polymorphism ought to do the trick:


void MyObject::DoProcessMessage (const Message* Msg) {
// for example. Assuming a virtual function
Msg->doProcessingThatCaresAbout(*this);
}


You might want to read this.

Share this post


Link to post
Share on other sites
Aye, double dispatch is an alternative - though I don't much like Zahlman's solution as it makes message-handling behavior part of the message class instead of part of the message-handling object, so if you want to send a message that will change a handler's internal state, the message will need to be a friend of the message-handler. General-purpose messages would end up needed to know about (and access) an awfully large number of different agents.

Typecodes will also become necessary when you want to send messages over the network.

If you really want to perform a double dispatch, I'd do something like this:


class BaseMessageHandler
{
public:
void HandleBaseMessage(const BaseMsg* msg);

virtual void HandleMessage(const TypeOneMsg* msg) {assert(false && "TypeOne message not handled");}
virtual void HandleMessage(const TypeTwoMsg* msg) {assert(false && "TypeTwo message not handled");}
/* ... etc for all message types ... */
};

void BaseMessageHandler::HandleBaseMessage(const BaseMsg* msg)
{
switch(msg->typecode)
{
case MSG_TYPEONE: HandleMessage(static_cast<const TypeOneMsg*>(msg)); return;
case MSG_TYPETWO: HandleMessage(static_cast<const TypeTwoMsg*>(msg)); return;
/* .. etc for all message types ... */
};
}



That way, individual concrete message handlers can override the HandleMessage() function for each of the message types they are interested in, and messages with known types can be sent directly by calling HandleMessage (if coming from a local source) instead of HandleBaseMessage.

I use static_cast because the type is known, as Zahlman says.

Share this post


Link to post
Share on other sites
Quote:
Original post by cow_in_the_well
I do however prefer using printf style formatting for error logging, but I'm somewhat pushing towards replacing it with some kind of streaming system (forgetting to use .c_str() when logging a std::string value makes things go boom...done that too many times -_-).


Doesn't your compiler have a switch to type-check arguments to printf/scanf ???
My compiler (GCC, Apple's variant) doesn't turn this on by default, but it comes in handy.

Share this post


Link to post
Share on other sites

This topic is 4336 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this