[Insight-developers] Intel C++ and vnl_*_ref

Brad King brad . king at kitware . com
Mon, 18 Aug 2003 14:52:11 -0400 (EDT)


Hello,

Recently Bill Hoffman and I tracked down some test failures in ITK when
built with the Intel C++ Compiler for Linux (icc70).  The problem turned
out to be a compiler bug that is exposed by passing a vnl_matrix_fixed
instance to a function that takes a vnl_matrix argument.  The intermediate
vnl_matrix_ref that is created does not destruct properly and ends up
trying to free memory owned by the vnl_matrix_fixed instance.

The program included below duplicates the problem in a small test case.
Classes map as

  A -> vnl_matrix
  B -> vnl_matrix_ref
  C -> vnl_matrix_fixed

When built with icc70, it produces this output:

operator B()
A(): 0xbffff8f8
B(): 0xbffff8f8
A(const A&): 0xbffff908
B(const B&): 0xbffff908
f()
~A(): 0xbffff908
~B(): 0xbffff8f8
~A(): 0xbffff8f8

By carefully tracing the program, one finds that the argument to f() is
created by copy-constructing a B, but it is destroyed by calling only ~A()
and not ~B().

Using CMake, it is easy to detect this bug in the C++ compiler with a
TRY_RUN command (the same bug exists in the Comeau C++ compiler from
www.comeaucomputing.com because it is also based on the EDG compiler).

For ITK, I'm going to add the following work-around when the bug is
detected:

// Use a base class to ensure the ownership flag
// is initialized to 1 by every constructor.
class vnl_matrix_base_own
{
public:
  vnl_matrix_base_own(): vnl_matrix_own_data(1) {}
protected:
  int vnl_matrix_own_data;
};

template<class T>
class vnl_matrix: public vnl_matrix_base_own
{
  ~vnl_matrix() { if (data && vnl_matrix_own_data) destroy(); }
};

template <class T>
class vnl_matrix_ref : public vnl_matrix<T>
{
  vnl_matrix_ref(/* ...args... */)
    {
    // ...
    // Tell the vnl_matrix destructor we do not own the memory.
    vnl_matrix_own_data = 0;
    }
};

Obviously this work-around implementation could be tweaked for
size/alignment of the vnl_matrix_own_data member.

I've reported this problem to the VXL people to see if they can find a
better work-around and fix it in VXL proper.  I'm also planning to submit
the bug to Intel to get the compiler fixed.

Thoughts?
-Brad

#include <stdio.h>

struct A
{
  A() { printf("A(): %p\n", this); }
  A(const A&) { printf("A(const A&): %p\n", this); }
  ~A() { printf("~A(): %p\n", this); }
};

struct B: public A
{
  B() { printf("B(): %p\n", this); }
  B(const B& b): A(b) { printf("B(const B&): %p\n", this); }
  ~B() { printf("~B(): %p\n", this); }
};

struct C
{
  operator B () { printf("operator B()\n"); return B(); }
};

void f(A) { printf("f()\n"); }

int main()
{
  C c;
  f(c);
  return 0;
}