It turns out that #include
ing iostream can get you a bonus abs function. That is, the following won’t compile in GCC (4.8) or Clang (3.4):
but the following will (on my machine), if (and only if) compiled with -std=c++11
:
A compiler’s implementation of <iostream>
may depend on (and include) <cstdlib>
, and there’s a bit in the standard that explicitly allows implementations to import C standard library functions into the global namespace in addition to making them available in namespace std
. In this case, the following declaration is introduced when including <iostream>
:
/* Return the absolute value of X. */
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
Only this declaration of abs
is made available, so abs(-4.8)
compiles without so much as a warning with GCC (Clang gives a warning about the implicit conversion of the argument to int
). If the call is qualified (std::abs
), the code fails to compile as other declarations (for long
and long long
) are available in namespace std
.
Subtle Bugs
A while back, I wrote a ray tracer for a computer graphics class assignment, and came across a rendering bug visible when using any compiler other than MSVC. For my first non-trivial C++ program, I was amazed things would render correctly using any compiler, so I just continued along with MSVC until the assignment was submitted.
A couple of renders as produced by the program compiled with MSVC (the way they were supposed to look):
and those same scenes rendered by the ray tracer compiled with GCC 4.8:
Not too far off, but no good. The first scene is particularly glitchy, and in the second you can notice some weirdness with the red cylinder.
Looking into it a while after the fact, I found that the issue came down to a couple member functions of QuadricCollection
(an instance of this class is an object defined by the intersection of quadrics):
bool onSurface(const Eigen::Vector4d &pt) {
bool onBoundary = false;
double distToSurface;
for (unsigned int i = 0; i < quadrics.size(); ++i) {
Eigen::Matrix4d &Q = quadrics[i];
if ((distToSurface = pt.transpose() * Q * pt) > EPSILON)
return false; // pt not in intersection of all quadrics
else if (abs(distToSurface) < EPSILON)
onBoundary = true; // pt on boundary of at least one quadric
}
return onBoundary;
}
virtual Eigen::Vector4d getUnitNormal(const Eigen::Vector4d &point) {
BOOST_ASSERT_MSG(point[3] == 1, "getNormal needs a homogeneous point!");
BOOST_ASSERT_MSG(onSurface(point), "Asked for normal at point not on surface!");
for (unsigned int i = 0; i < quadrics.size(); ++i) {
Eigen::Matrix4d &Q = quadrics[i];
double d = point.transpose() * Q * point;
if (abs(d) < EPSILON) {
// Point is on the surface of the quadric Q
return (2 * Q * point).cwiseProduct(Eigen::Vector4d(1, 1, 1, 0)).normalized();
}
}
}
The use of the unqualified abs
function (lines 8 and 20) opens this code up to inconsistency across implementations. It just so happens that with MSVC 2010, the abs
that is resolved does operate on floating points, and everything renders fine using that compiler.
Comments