|
|
For technical reasons, initial capacity specifications in G2++ record definitions are sometimes ignored. Fortunately, g2++comp(CP) warns about these cases and even suggests corrective action.
Specifically, initial capacity specifications in either of the two forms discussed in the last section, ``Adding Builtin C Types to the Repertoire of G2++ Types'', are honored only for strings or arrays that occur as members of an immediately enclosing structure. This condition guarantees that the string or array has a name (names act as "carriers" of initial capacity information).
Each of the following four record definitions fails to satisfy the condition:
case_1.g case_1a 100 case_1b 100 LONG case_1c *(100) case_1d *(100) LONG
In the code generated by g2++comp(CP), CASE_1A and CASE_1C are strings, and CASE_1B and CASE_1D are arrays; since none of these strings or arrays is the member of an immediately enclosing structure, their initial capacity specifications are ignored. The following code is therefore problematic:
#include "case_1.h" main(){ CASE_1A a; CASE_1B b; CASE_1C c; CASE_1D d; a.pad(100,'x'); inefficient b[99] = 99; core dump? c.pad(100,'x'); inefficient d[99] = 99; core dump? }
The corrective action is suggested by the compiler:
g2++comp: file 'case_1.g': path 'case_1a.100' warning: 100 will not be used as the initial string size; for proper preallocation, use a constructor argument, e.g., CASE_1A x(Stringsize(100)); g2++comp: file 'case_1.g': path 'case_1b.100' warning: 100 will not be used as the initial array size; for proper preallocation, use a constructor argument, e.g., CASE_1B x(100); g2++comp: file 'case_1.g': path 'case_1c.*(100)' warning: 100 will not be used as the initial string size; for proper preallocation, use a constructor argument, e.g., CASE_1C x(Stringsize(100)); g2++comp: file 'case_1.g': path 'case_1b.*(100)' warning: 100 will not be used as the initial array size; for proper preallocation, use a constructor argument, e.g., CASE_1D x(100);
Here's the client code after taking the corrective action:
#include "case_1.h" main(){ CASE_1A a(Stringsize(100)); CASE_1B b(100); CASE_1C c(Stringsize(100)); CASE_1D d(100); a.pad(100,'x'); efficient b[99] = 99; OK c.pad(100,'x'); efficient d[99] = 99; OK }
The same problem can occur at deeper levels within a structure:
case_2.g case_2a x LONG y * 100 case_2b x LONG y 50 100 LONG
In the code generated by g2++comp(CP), the elements of the arbitrary size array CASE_2A::y are anonymous; the 100 will therefore be ignored as an initial capacity specification. In CASE_2B::y, the elements of the array y, themselves arrays, will not have 100 elements preallocated for the same reason. The following client code therefore exhibits problematic behavior:
#include "case_2.h" main(){ CASE_2A a; CASE_2B b; a.y[10].pad(100,'x'); inefficient b.y[49][99] = 99; core dump? }
The earlier corrective action -- specifying a constructor argument -- is not available in this case; instead, we must modify the record definition itself. This is suggested by the compiler:
g2++comp: file 'case_2.g': path 'case_2a.y.0' warning: 100 will not be used as the initial string capacity; consider redefining the element type as a structure g2++comp: file 'case_2.g': path 'case_2b.y.0' warning: 100 will not be used as the initial array size; consider redefining the element type as a structure
Taking the suggested action gives the following record definitions:
case_2.g case_2a x LONG y * x 100 case_2b x LONG y * x 100 LONG
Here is the corrected client code:
#include "case_2.h" main(){ CASE_2A a; CASE_2B b; a.y[10].x.pad(100,'x'); efficient b.y.[49].x[99] = 99; OK }
These record definitions entail a slight runtime overhead which will be more than offset by the efficiency gained by honoring initial capacity specifications.