C++ Overloading - How do I call (int, ...) instead of (int, int, ...)?

Discussion in 'Mac Programming' started by ArtOfWarfare, Jun 18, 2012.

  1. ArtOfWarfare, Jun 18, 2012
    Last edited: Jun 18, 2012

    macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #1
    I'm in a class on C++. We've learned about overloading functions, and I decided to mix it with varying numbers of arguments (not a topic we covered in class; something I just know from studying C on my own.)

    Anyways, I have a stack class with an overloaded constructor. Here is most of the class:

    Code:
    class Stack {
    private:
    	int *data;
    	unsigned int max;
    	unsigned int size;
    	int top;
    public:
    	Stack(unsigned int, unsigned int, ...);
    	Stack(unsigned int, ...);
    	Stack();
    	~Stack();
    	void push(int);
    	int pop();
    };
    
    Stack::Stack (unsigned int maximum, unsigned int count, ...)
    {
    	cout << "Stack::Stack (int maximum, int count, ...)" << endl;
    	if (max < count) max = maximum = count;
    	data = new int[max];
    	size = count;
    	va_list values;
    	va_start(values, count);
    	for (int i = 0; i < count; i++)
    	{
    		data[i] = va_arg(values, int);
    	}
    	va_end(values);
    	top = data[count-1];
    }
    
    Stack::Stack (unsigned int count, ...)
    {
    	cout << "Stack::Stack (int count, ...)" << endl;
    	max = (MAX_SIZE < count)?count:MAX_SIZE;
    	data = new int[max];
    	size = count;
    	va_list values;
    	va_start(values, count);
    	for (int i = 0; i < count; i++)
    	{
    		data[i] = va_arg(values, int);
    	}
    	va_end(values);
    	top = data[count-1];
    }
    
    Stack::Stack ()
    {
    	cout << "Stack::Stack ()" << endl;
    	data = new int[MAX_SIZE];
    	max = MAX_SIZE;
    	size = top = 0;
    }
    My issue is that I'd like to call Stack::Stack (int count, ...), but every time I try that, it's calling Stack::Stack (int maximum, int count, ...) instead. Here's the code:

    Code:
    int main (int argc, char* argv[])
    {
    	Stack a (5, 4, 3, 2, 1, 0);
    	cout << a.pop() << a.pop() << a.pop() << a.pop() << a.pop() << endl;
    	return 0;
    }
    Here's the output:
    Code:
    Stack::Stack (int maximum, int count, ...)
    32100
    The output I'd rather have would be:
    Code:
    Stack::Stack (int count, ...)
    43210
    Edit: This also brings up an interesting order of operations issue... I just realized the numbers were printing in an order opposite of what I was expecting... a little research seems to reveal that << evaluates from right to left, but then prints from left to right?
     
  2. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #2
    How do you expect the compiler to distinguish between the two Stack functions? It looks like it is just using the first function that matches the call. Unless you call Stack() with a single argument, the second Stack function will never be used.
     
  3. thread starter macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #3
    I recognize that that is the issue. My question is more... how do I get around it?

    I decided to just completely change how memory allocation is handled by my class so it no longer has a maximum... (thus Stack(unsigned int maximum, unsigned int size, ...) isn't necessary) instead it just allocates as much as it needs initially, and then allocates a new, slightly bigger chunk of memory each time push is called, copies everything from the old memory, copies in the newly pushed value, and deletes the old memory. So no longer actually need an answer to my question.

    But I'm curious if there are any good ways to get around the issue or if I'd have to go with a more Obj-C style tactic and have verbose names (i.e., in obj-c, it's not uncommon to have multiple init methods... like for NSNumber, there is initWithInt:, initWithDouble:, etc...)
     
  4. macrumors 603

    Joined:
    Aug 9, 2009
    #4
    It seems you can't.

    Well, maybe you can, but it looks like you'll have to trick the compiler into not matching (int,int,...). To do that, you must use an arg type other than int for one of the args, possibly with a cast.


    I knew nothing about this, so I googled:
    overload c++ function

    which showed this as top result:
    http://publib.boulder.ibm.com/infoc.../com.ibm.xlcpp8l.doc/language/ref/cplr312.htm

    This described what I already knew, but there's a link at the bottom:
    Restrictions on overloaded functions (C++ only)

    Then on the left we see:
    Overload resolution (C++ only)

    which links to:
    Implicit conversion sequences (C++ only)

    where we read that ellipsis conversions have the worst matching.


    So assuming that ellipsis conversions are the worst match, there is no way to convince the compiler to call your 1-definite-type function in favor of the 2-definite-type function, except by tricking it with specific arg types. You have to make the arg types be a worse match for the 2-definite-type function. Even then, I'm not entirely sure it will call the 1-definite-type function, because the compiler might decide to promote the type rather than match ellipses.

    Also, it's unclear to me what compiler you're using, what its standards compliance is, or whether the described matching of ellipses is dictated by a standard.


    I think the short answer to all this is simply, "Don't do that".

    I can't see a good reason for doing it the way you've done it, other than maybe testing the limits of what the compiler will recognized.

    It would be a lot simpler API to understand if the constructor just defined the max size, and there was another func that did a multi-item push (if that's even necessary).
     
  5. thread starter macrumors 603

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #5
    Sounds good.

    Given I found a few ways around it, at this point, that's basically all I was doing. It seems odd that the compiler doesn't give any warnings about the fact that the one method will never be called.

    Would it be possible to define my own type... like a special int that is identical to a normal int except it'll allow the compiler to know the difference between the methods?

    (I'm using g++ 4.2)
     
  6. macrumors 603

    Joined:
    Aug 9, 2009
    #6
    See the links I posted, and look for the information on how typedefs are matched.
     
  7. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #7
    Seems to me that a stack object should be somewhat transparent in operation. If the caller over-runs the top of a stack, it should throw an exception. The exception could either expand the stack's capacity or cause torpor or death to the caller. Pushing an uncertain amount of data seems like a very bad idea to me.

    I would be inclined to include an exception vector in the object which the constructor would either default to a grow function or something like a SIGABRT. You would then add a method to set the exception vector so that the creator could handle the fault, either by suspending the faulting process until space can be cleared or by shutting that run down entirely. Obviously, this would not be quite the same as a try/catch construct.
     
  8. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #8
    operator<< doesn't evaluate in any particular order. You have a long list of method calls, and arguments (each a.pop () is an argument to an operator<< method call). These arguments are evaluated in unspecified order. So actually the numbers could be printed in _any_ order. They could be called in different order on two consecutive calls (unlikely, but legal).
     

Share This Page