From: ebarton@world.std.com (Edward Barton)
Subject: Help with BC++ 3.0
Organization: The World Public Access UNIX, Brookline, MA
Distribution: comp
Date: Mon, 6 Apr 1992 17:21:45 GMT


As I have begun attempting to use C++ classes in nontrivial ways, I I
have encountered what appear to be various bugs in Borland C++ 3.0.
The major bugs that I describe here are:

	- destructors called out of order, sometimes before
	  constructors, when returning classes by value from
	  a function

	- GP FAULT in the compiler when certain template classes
	  are used

I would like to know if anyone has seen patches or workarounds for
these problems.  I have a letter in to Borland technical support, but
they appear to be swamped and have not yet responded.  Also, I tried
comp.os.msdos.programmer because this problem is occurring with a DOS
implementation of C++, but no one there had seen these more
C++-specific problems.

The test programs below demonstrate some of the problems I have had.
(Some of the test programs won't compile, so those test programs are
more likely to contain errors on my part.)  The first test program
runs three slightly different functions that should do the same
thing.  I will discuss the three runs one by one; here is the first
output (compiled with -O -Oebcvlsi -Z -ml -V -d):

not-inline test:
creating Value @ 0x10120fd4
creating Value @ 0x10120f40
copying Value from 0x10120f40 to 0x10120fce
value copied was 3
destroying Value @ 0x10120f40
value is currently 3 ... zeroing
plus: 1+2 = [value 3]
destroying Value @ 0x10120fce
value is currently 3 ... zeroing
destroying Value @ 0x10120fd4
value is currently 1 ... zeroing

Here, everything works as expected, and the value 3 is computed by a
somewhat roundabout method.  However, in the second test, the
same semantics are not preserved:

inline test:
creating Value @ 0x10120fb6
creating Value @ 0x10120fb8
destroying Value @ 0x10120fb8
value is currently 3 ... zeroing
copying Value from 0x10120fb8 to 0x10120fa2
value copied was 0
plus_inline: 1+2 = [value 0]
destroying Value @ 0x10120fa2
value is currently 0 ... zeroing
destroying Value @ 0x10120fb6
value is currently 1 ... zeroing

Here, the Value at 0x10120fb8 is destroyed before it is copied to
the Value at 0x10120fa2.  Therefore, the wrong value is copied.
(In my actual application, pointers and "delete" are involved, so
the results are catastrophic.)

In the third test ("inline #2"), the correct value is computed, but
destructors are called out of order, again with catastrophic results
in my actual application.

inline test #2:
creating Value @ 0x10120f80
destroying Value @ 0x10120f7a
value is currently 29698 ... zeroing
creating Value @ 0x10120f7a
creating Value @ 0x10120f6a
plus_inline_2: 1+2 = [value 3]
destroying Value @ 0x10120f6a
value is currently 3 ... zeroing
destroying Value @ 0x10120f80
value is currently 1 ... zeroing

Here the destructor for the Value at 0x10120f7a was called before
the constructor for that Value, when the memory just contained
garbage.  Again, this produces hopeless results when the destructor
involves pointers.

My second test program produces a "GP FAULT" whenever I try to
compile it.  I have tried running with different memory allocations,
and under both EMM386 and QEMM386, with no difference; I don't think
the problem is anything in my environment.

My third test program also produces a "GP FAULT" whenever I try to
compile it.

All three of these programs are stripped-down test programs based on
what I am trying to do in my application.

// first test program -- destructor bugs
#include <iostream.h>

class Value {
public:
  int getX() { return x; }
  Value(int init = 0) : x(init) {
    cout << "creating Value @ " << (void *) this << "\n";
  }
  Value(const Value& other) : x(other.x) {
    cout << "copying Value from " << (void *) &other 
	 << " to " << (void *) this << "\n";
    cout << "value copied was " << x << "\n";
  }
  ~Value() {
    cout << "destroying Value @ " << (void *) this << "\n";
    cout << "value is currently " << x << " ... zeroing\n";
    x = 0;
  }
  Value plus(int y);
  inline Value plus_inline(int y);
  inline Value plus_inline_2(int y);
  Value operator+(int y) { return Value(x+y); }

private:
  int x;
  Value& operator=(Value& other); // undefined

};

ostream& operator<< (ostream& s, Value& v) {
  s << "[value " << v.getX() << "]";
  return s;
}

Value Value::plus(int y) {
  Value t(y);
  t.x += x;
  return t;
}

inline Value Value::plus_inline(int y) {
  Value t(y);
  t.x += x;
  return t;
}

inline Value Value::plus_inline_2(int y) {
  return Value(y) + x;
}

main(int argc, char *argv[]) {
  cout << "not-inline test:\n";
  cout << "plus: 1+2 = " << Value(1).plus(2) << "\n";
  cout << "\n";
  cout << "inline test:\n";
  cout << "plus_inline: 1+2 = " << Value(1).plus_inline(2) << "\n";
  cout << "\n";
  cout << "inline test #2:\n";
  cout << "plus_inline_2: 1+2 = " << Value(1).plus_inline_2(2) << "\n";
}

// second test program -- GP FAULT in compiler

// program may contain bugs, since it can't be compiled and tested

#include <iostream.h>

#pragma option -Jgd

template<class ELT_TYPE> class Counter {

public:

  void Set(ELT_TYPE x) { *cptr = x; }
  ELT_TYPE Get() const { return *cptr; }
  Counter<ELT_TYPE>& operator+=(ELT_TYPE x) { *cptr += x; return *this; }
  void printOn(ostream& s) const;

  Counter(ELT_TYPE init = 0) 
  : cptr(new ELT_TYPE) { *cptr = init; }
  Counter(const Counter<ELT_TYPE>& other) 
  : cptr(new ELT_TYPE) { 
    cout << "copying Counter @ " << (void *) &other << "\n";
    cout << "new Counter has cptr = " << (void *) cptr << "\n";
    *cptr = other.Get();
  }

  ~Counter() { 
     cout << "destroying Counter @ " << (void *) this << "\n";
     cout << "deleting cptr = " << (void *) cptr << "\n";
     delete cptr;
   }

private:
  class Temp {
    friend Counter<ELT_TYPE>;
    Counter<ELT_TYPE> *scratch;
  private:
    Temp() : scratch(0) { cout << "calling default constructor for Temp\n"; }
    Temp(Counter<ELT_TYPE> *init) : scratch(init) {
      cout << "creating initial Temp @ " << (void *) this 
	   << " with scratch @ " << (void *) scratch << "\n";
    }
    Temp(Temp& other) : scratch(other.scratch) { 
      other.scratch = NULL;
      cout << "copying Temp from " << (void *) &other << " to " 
	   << (void *) this << "\n";
      cout << "passed along scratch @ " << (void *) scratch << "\n";
    }
    void operator=(Temp& other); // undefined
  public:
    ~Temp() { 
      cout << "destroying Temp @ " << (void *) this << "\n";
      cout << "scratch = " << (void *) scratch << "\n";
      Counter<ELT_TYPE>::deletePointer(scratch);
    }
    Temp operator+(ELT_TYPE x) { 
      cout << "adding to Temp with scratch = " << (void *) scratch << "\n";
      (*scratch) += x; 
      return *this;
    }
  };

public:
  static void deletePointer(Counter<ELT_TYPE> *p) { delete p; }
  Temp operator+(ELT_TYPE x) {
    cout << "adding to Counter @ " << (void *) this << "\n";
    Temp t1(new Counter<ELT_TYPE>(*this));
    Temp t2(t1 + x);
    return t2;
  }
  Counter(Temp& t) 
  : cptr(t.scratch->cptr) {
      cout << "copying Counter from Temp @ " << (void *) &t << "\n";
      cout << "grabbed cptr = " << (void *) cptr 
	   << " from scratch = " << (void *) t.scratch << "\n";
      t.scratch->cptr = NULL;
      delete t.scratch;
      t.scratch = NULL;
  }

protected:
  ELT_TYPE *cptr;
};

template<class ELT_TYPE> 
ostream& operator<< (ostream& s, const Counter<ELT_TYPE>& c) {
  c.printOn(s);
  return s;
}

template<class ELT_TYPE> void
Counter<ELT_TYPE>::printOn(ostream& s) const {
  s << "[count " << *cptr << "]";
}

typedef Counter<float> FloatCounter;

main(int argc, char *argv[]) {
  // try to get the compiler to generate the << function, which
  // it seems reluctant to do
  static ostream& (*dummy)(ostream& s, const FloatCounter& c) 
   = operator<<;

  cout << FloatCounter(0) + 2 + 2 << "\n";

// third test program -- GP FAULT in compiler

// not a complete functional program -- parts have been snipped out
// this part still gets GP FAULT

template<class ELT_TYPE> class Outer {

public:

  Outer() : cptr(new ELT_TYPE) { }
  ~Outer() { delete cptr; }

private:
  class Inner {
    friend Outer<ELT_TYPE>;
    Outer<ELT_TYPE> *scratch;

  private:
    Inner(Outer<ELT_TYPE> *init) : scratch(init) {
      cout << "creating initial Inner @ " << (void *) this 
	   << " with scratch @ " << (void *) scratch << "\n";
    }
    Inner(Inner& other) : scratch(other.scratch) {
      other.scratch = NULL;
      cout << "copying Inner from " << (void *) &other << " to " 
	   << (void *) this << "\n";
      cout << "passed along scratch @ " << (void *) scratch << "\n";
    }
    void operator=(Inner& other); // undefined
  public:
    inline ~Inner();
  };
public:
  Outer(Inner& t) 
  : cptr(t.scratch->cptr) {
      cout << "copying Outer from Inner @ " << (void *) &t << "\n";
      cout << "grabbed cptr = " << (void *) cptr 
	   << " from scratch = " << (void *) t.scratch << "\n";
      t.scratch->cptr = NULL;
      delete t.scratch;
      t.scratch = NULL;
  }

protected:
  ELT_TYPE *cptr;
};

template<class ELT_TYPE>
Outer<ELT_TYPE>::Inner::~Inner() {
  delete scratch;
}

typedef FloatOuter Outer<float>;

// end of test programs

Both the destructor bugs and the GP FAULT are very disturbing.  I
would appreciate hearing from anyone who has seen a fix, or an
explanation of what program feature triggers the general protection fault.