|
|
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.