1

I have the following situation

Class A
{

public:
   A(/* x number of arguments */);

};


A::A(/*.....*/)
{
    std::set<__int64>  tmpSet;
    tmpSet.insert( /*<num>*/ );     // repeat statement for some  30 K or more 
    tmpSet.insert( /*<num>*/ ); 

 //-----repeat statement for some  30K or more times

}

main()
{
   A obj(/*arguments */);    // Run time Error : Stack over flow 


}

What I observed , when I reduced the number of inserts in the constructor the Error disappeared . I wonder how did the compiler knew at run time to calculate the size of the container before the object was created .

Constantinius
  • 34,183
  • 8
  • 77
  • 85
sameer karjatkar
  • 2,017
  • 4
  • 23
  • 43
  • The compiler cannot give you runtime errors :) – Armen Tsirunyan Jul 27 '11 at 13:08
  • The stack size is limited. See http://stackoverflow.com/questions/1825964/c-c-maximum-stack-size-of-program. – netcoder Jul 27 '11 at 13:08
  • 1
    did the error occured before creating the object?It seems to me that it is giving the error 'during' creating the object. which is normal. – bliss Jul 27 '11 at 13:09
  • 3
    in all of this, why do you need to `repeat statement for some 30K or more times`? Are you writing that piece of code line by line or using a loop? And what does the `set` statement do? – Sriram Jul 27 '11 at 13:09
  • Maybe post a bit more of the code (and fix your syntax errors). It's not clear why there should be a stack overflow if you're not creating any automatic objects in the constructor. – Kerrek SB Jul 27 '11 at 13:10
  • 1
    It is really strange until you are trying to pass 30K numbers as constructor arguments. – eugene_che Jul 27 '11 at 13:11
  • @bliss Yes the error occured before creating the object – sameer karjatkar Jul 27 '11 at 13:11
  • Aside from the other comments, you really should be using a loop, etc...what is the point of the set in the constructor. Are you doing something else with it in the constructor. Otherwise its just a local that will go out of scope after the object is constructed and you will have done a whole lot of work for nothing (unless you just need to generate heat off of the processor and want to burn some kwh) – pstrjds Jul 27 '11 at 13:18

4 Answers4

2

My guess is that your constructor generates a compiled code that does not fit in your program stack. You should consider exchanging your insert statements with a loop, which drastically reduces the function size on the stack. Another option is to put the data to be inserted into a static array, and then loop over it in the constructor to produce the set.

Constantinius
  • 34,183
  • 8
  • 77
  • 85
2

std::set.insert() returns a std::pair<iterator, bool>. The compiler has to allocate stack memory to store the return value, as it is too large to fit into registers.

A good optimisation would be to reuse this stack memory for the next call. It looks like your compiler does not do so.

Hence, your constructor requires a huge amount of stack space. At run time, it tries to allocate this, and fails.

Solutions:

  • Use a bigger stack. Look for linker options to set this.
  • Turn on a compiler optimisation so it will reuse the stack space for the next insert.
  • Switch to a different compiler that performs this optimisation
  • Put the numbers in a static array, which does not use stack space, and copy them into the set.

Note that it is very dependent on the compiler you use. However, at this time, it's unknown to me which compiler you used.

EDIT: Note that this is pure speculation, as we don't know which compiler was used.

Sjoerd
  • 6,837
  • 31
  • 44
  • Agree - yes, it could be the return values not being popped off individually. A different twist on my answer :-) However, it may be turning an optimization OFF that fixes the problem: Deferring argument cleanup until function end is usually considered an optimization. – Roddy Jul 27 '11 at 13:51
1

it has nothing to do with the container, what's happening is that the 30,000 arguments you're passing take up space on the stack, and you're going over a (reasonable) limit. When you reduce the use of those paramaters inside the constructor, the compiler is optimising away the unused ones.

eg. if you pass (a,b,c) into the constructor but never reference those parameter variables, the compiler can remove them completely - no point having something that's never used. In your case, this reduces the amount of stack used and allows it to work.

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
1
//repeat statement for some  30K or more times

Well, that's probably a bad idea for any number of reasons. Assuming that isn't 30K of hand-typed code, stick the data in a file or resource and read it in a loop.

The actual 'data' for the std::set is heap-allocated rather than stack allocated, so that's unlikely to be the problem.

But, the reason for stack overflow is probably this.

For many compilers (and many calling conventions) parameters are passed on the stack.

The parameters to insert are pushed on the stack as required. But, the compiler optimizes all the pops into a single 'big pop' at the end of the function. Unfortunately, your compiler hasn't realised that the # of pushed arguments is ridiculously big...

Here's pseudo-code for what the compiler does (ignoring results from insert)

Before optimize:

PUSH arg1
CALL tmpset.insert
ADD 4 to SP   // pop arg1
PUSH arg2
CALL tmpset.insert
ADD 4 to SP   // pop arg2
PUSH arg3
CALL tmpset.insert
ADD 4 to SP   // pop arg3

After optimize:

PUSH arg1
CALL tmpset.insert
PUSH arg2
CALL tmpset.insert
PUSH arg3
CALL tmpset.insert
ADD 12 to SP   // pop arg1,arg2,arg3
Roddy
  • 66,617
  • 42
  • 165
  • 277