OpenSees Cloud
OpenSees AMI
Pleasing the Compiler
Original Post - 08 Nov 2023 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
Around the transition from G3 to OpenSees, the Visual Studio (VS) compiler was not fully compliant with the then current ISO C++ standard.
There were perhaps many items out of compliance, but the non-compliance
that drove us nuts was the scoping rule on variables defined in the
initialization statement of for
loops. The VS compiler kept these
variables in scope after the loop body instead of limiting the scope to
only inside the loop body (the ISO compliant behavior).
As a result, consecutive for
loops like shown below would be perfectly
fine on Linux compilations but would fail in VS due to multiple
definitions of the i
variable.
for (int i = 0; i < N; i++) {
// Do some stuff
}
for (int i = 0; i < N; i++) {
// Do some other stuff
}
To please the VS compiler, we modified consecutive for
loops to define i
before the first loop and remove the variable declaration from the loop
initialization.
int i;
for (i = 0; i < N; i++) {
// Do some stuff
}
for (i = 0; i < N; i++) {
// Do some other stuff
}
Perhaps there was a VS compiler setting for compliant loop initializations, but StackOverflow and StackExchange didn’t come around until about 10 years later, so finding obscure programming solutions was next to impossible.
Today, you can find artifacts of us pleasing the VS compiler back in 1999.
For example, in the ZeroLength::setUp() method you’ll see two for
loops in the same scope with i
defined out front.
int i;
for (i=0; i<2; i++)
theNodes[i] = 0;
//
// Set up the local x, y, z axes
//
// create transformation matrix of direction cosines
for (i=0; i<3; i++) {
transformation(0,i) = x(i)/xn;
transformation(1,i) = y(i)/yn;
transformation(2,i) = z(i)/zn;
}
Several other methods in the OpenSees framework define loop variables
outside the first for
loop–it’s not some quirk of the ZeroLength
element.
However, there are instances where defining the loop variable before the
first for
loop is the desired implementation. For example, in
SectionAggregator::getStressResultant() and comparable methods in the
SectionAggregator class, you’ll see that the loop variable i
is carried
over from one for
loop to the next.
int i = 0;
int theSectionOrder = 0;
if (theSection) {
const Vector &sSec = theSection->getStressResultant();
theSectionOrder = theSection->getOrder();
for (i = 0; i < theSectionOrder; i++)
(*s)(i) = sSec(i);
}
int order = theSectionOrder + numMats;
for ( ; i < order; i++)
(*s)(i) = theAdditions[i-theSectionOrder]->getStress();
return *s;
In this case, we weren’t trying to please the VS compiler–we were putting section forces into the proper entries of the concatenated section force vector. Sure, we could have coded these loops a little differently. But it ain’t broke, so we ain’t gonna fix it. Not this one.