[comp.lang.c++] operator++

kt@msor.UUCP (Keith Tizzard) (09/25/90)

Two questions about C++

Can anyone please help.

1   operator++()

I fully understand that if I write my own function for operator++() in
a class, I cannot detect in that function whether the operator is being
used in a prefix form or a postfix form.  However when it is, in fact,
used why does the postfixing and prefixing not take place in the usual way?

class X{
    ....
    public:
        X& operator=(x&);
        X& operator++();
        . . .
};


X   a,b;

a = ++b;
a = b++;

Is it not possible for the usual arrangement whereby the ++ is performed before
= in the first, and after it in the second?





2   Testing of and assigning to 'this'

Before version 2.0 if one wanted to allocate space for an object, one way 
was to test the value of 'this' within the constructor to detect whether 
the object was to be on the free store or not

    if(this == 0) this = malloc( ....
    else  this = this;


In V2.0 this is not longer needed since overloading operator new and 
operator delete is preferred.

Is is still possible to assign to 'this' in V2.0?

Can one rely on 'this' having the value zero one entry to the constructor
is the object is allocated on the free store?

Is there any neeed to do it at all?


-- 
Keith Tizzard
MSOR Dept, University of Exeter, Streatham Court, EXETER EX4 4PU, UK
tel: (+44) 392 264463    email: kt@msor.exeter.ac.uk     kt@msor.UUCP 

jgro@lia (Jeremy Grodberg) (10/03/90)

In article <706@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
>Can anyone please help.
>
>1   operator++()
>
>I fully understand that if I write my own function for operator++() in
>a class, I cannot detect in that function whether the operator is being
>used in a prefix form or a postfix form.  

In E&S, pp 338-339, they explain that you now can tell which call is being
made, becuase when operator++ (or operator --) is called as a postfix
operator, the function will be called with an int argument of 0.

>However when it is, in fact,
>used why does the postfixing and prefixing not take place in the usual way?
>
>class X{
>    ....
>    public:
>        X& operator=(x&);
>        X& operator++();
>        . . .
>};
>
>
>X   a,b;
>
>a = ++b;
>a = b++;
>
>Is it not possible for the usual arrangement whereby the ++ is performed before
>= in the first, and after it in the second?
>

The postfix ++ simply means that the value of the expression is the value
before incrementing the variable.  Take you favorite ANSI C compiler and
try

int x = 1;

x = x++;
printf("x = %d\n", x);	

If I recall the ANSI spec correctly, the final value of x is allowed to be
either 1 or 2, because the only guarantee about when the increment takes
place is that it takes place before the next "sequence point," which is 
basically the next function call, conditional, or comma operator.

-- 
Jeremy Grodberg      "I don't feel witty today.  Don't bug me."
jgro@lia.com          

jfischer@sco.COM (Jonathan A. Fischer) (10/10/90)

In article <706@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
>Two questions about C++
>
>1   operator++()
>
>a = ++b;
>a = b++;
>
>Is it not possible for the usual arrangement whereby the ++ is
>performed before = in the first, and after it in the second?

	To answer your question: Yes!  Now in cfront 2.1, if you define

	X::operator++()

it's a prefix ++.  If you define

	X::operator++(int dummy)

it's a postfix ++.  (If the second form isn't defined, the first form will
be used for both).  I'm not sure why they leave it up to the programmer to
handle the pre-/post-ness, rather than just having the compiler translate
	a = b++;
into
	a = b; (void)b.operator++();
Anyone, anyone...?

>2   Testing of and assigning to 'this'
>
>Is it still possible to assign to 'this' in V2.0?

	Technically, you can't assign to this, since it's "X * const this".
BUT, it's listed in Appendix B.3.3 as an "anachronism," something that is
provided for compatibility with earlier releases of cfront.  I.e., you can do
it, but technically you shouldn't.  Also, there is no requirement for a
compiler vendor to implement these anachronisms, so you are tainting your
portability by using them.

>Can one rely on 'this' having the value zero on entry to the constructor
>if the object is allocated on the free store?

	Yep, according to the same anachronisms section.

sking@nowhere.uucp (Steven King) (10/15/90)

In article <1990Oct9.144542.19983@sco.COM> jfischer@sco.COM (Jonathan A. Fischer) writes:
>In article <706@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
>>Two questions about C++
>>
>>2   Testing of and assigning to 'this'
>>
>>Is it still possible to assign to 'this' in V2.0?
>
>	Technically, you can't assign to this, since it's "X * const this".
>BUT, it's listed in Appendix B.3.3 as an "anachronism," something that is
>provided for compatibility with earlier releases of cfront.  I.e., you can do
>it, but technically you shouldn't.  Also, there is no requirement for a
>compiler vendor to implement these anachronisms, so you are tainting your
>portability by using them.
>
>>Can one rely on 'this' having the value zero on entry to the constructor
>>if the object is allocated on the free store?
>
>	Yep, according to the same anachronisms section.

	And only if the object isnt a member (or base) of another object.
Even tho' the translator knows this information (it does pass a flag to
destructors) there is NOT a reliable mechanism to determine this from within
a constructor.

	I wonder if there is enough of a need for this to consider adding it
to the language...

-- 
-------------------------------------------------------------------------------
new && improved:			 			 sking@nowhere
old && reliable:			...!cs.utexas.edu!ut-emx!nowhere!sking
-------------------------------------------------------------------------------

ark@alice.att.com (Andrew Koenig) (10/15/90)

In article <1990Oct14.224706.1934@nowhere.uucp>, sking@nowhere.uucp (Steven King) writes:
> 
> In article <1990Oct9.144542.19983@sco.COM> jfischer@sco.COM (Jonathan A. Fischer) writes:

> >>Can one rely on 'this' having the value zero on entry to the constructor
> >>if the object is allocated on the free store?

> >	Yep, according to the same anachronisms section.

Not quite.

First, I should point out that everything I am about to describe
is an anachronism.  You shouldn't use this stuff.  I'm only describing
it to make it easier for you to understand existing programs that do
use it.  It will go away in time.  You will see why when you see
how scruffy it is.

A constructor has different semantics depending on whether or not
it contains an ASSIGNMENT to `this.'  Here, `contains' means `textually
contains' -- that is, an actual assignment expression with `this'
as its left-hand side.

If a constructor doesn't assign to `this,' then the value of `this'
at entry to the constructor is the value of the object being constructed,
period.  This is true whether or not the object is allocated on the
free store.  Thus, for example:

	struct A {
		A() { printf("%lx\n", (long) this); }
	};

will print a nonzero value every time an A object is created, regardless
of whether it is an automatic variable, a static variable, or on
the free store.

If a constructor DOES assign to `this,' then the value of `this' may
or may not be zero at entry.  If the value of `this' is zero, it means
that the object is being allocated on the free store AND is not part
of a larger object.  In that case it is the constructor's responsibility
to allocate an appropriate amount of memory and assign it to `this.'

If the value of `this' at entry is nonzero, then memory has already been
allocated for the object.  Nevertheless, the constructor is expected to
assign a value to `this' ANYWAY -- the act of doing so causes the base
class(es) to be constructed and the initializers to be executed.

In other words, a constructor that assigns to `this' must do so in every
path through that constructor.  That means it is usually necessary to
say `this = this;' somewhere, because if memory has already been allocated
then it is impossible to reallocate it somewhere else (think of the case
where the object is part of a larger object that has already been partly
constructed).

Do you see why this usage has been deprecated?
-- 
				--Andrew Koenig
				  ark@europa.att.com

vaughan@mcc.com (Paul Vaughan) (10/16/90)

   From: sking@nowhere.uucp (Steven King)

   In article <1990Oct9.144542.19983@sco.COM> jfischer@sco.COM (Jonathan A. Fischer) writes:
   >In article <706@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
   >>Two questions about C++
   >>
   >>2   Testing of and assigning to 'this'
   >>
   >>Is it still possible to assign to 'this' in V2.0?
   >
   >	Technically, you can't assign to this, since it's "X * const this".
   >BUT, it's listed in Appendix B.3.3 as an "anachronism," something that is
   >provided for compatibility with earlier releases of cfront.  I.e., you can do
   >it, but technically you shouldn't.  Also, there is no requirement for a
   >compiler vendor to implement these anachronisms, so you are tainting your
   >portability by using them.
   >
   >>Can one rely on 'this' having the value zero on entry to the constructor
   >>if the object is allocated on the free store?
   >
   >	Yep, according to the same anachronisms section.

	   And only if the object isnt a member (or base) of another object.
   Even tho' the translator knows this information (it does pass a flag to
   destructors) there is NOT a reliable mechanism to determine this from within
   a constructor.

	   I wonder if there is enough of a need for this to consider adding it
   to the language...

Here's a related issue.  I'd like to be able to know if an object is a
member of another object.  For instance, I have a base "container"
class that maintains a table of objects that it owns.  I'd like to
provide automatic deletion of owned objects when the owner object is
destroyed.  But some of the owned objects are actually integral
members (and therefore are already automatically destroyed when the
owner goes) and some are allocated via new.  I'd rather not have to
store this info on the objects or provide for knowing it (although
your suggestion could allow for automatically recording that
information on the object in a transaparent way).  

I've thought about doing some pointer comparisons to detect whether an
owned object is within the address boundaries of the owner object.  I
have (or can automatically provide for) the information required to do
that, but it would surely be nonportable in the theoretical sense.
I'm not sure if its practical given the compiler set {g++, cfront } and
machine set {sun3, sparc, apollo}

 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan

jimad@microsoft.UUCP (Jim ADCOCK) (10/16/90)

In article <1990Oct14.224706.1934@nowhere.uucp> sking@nowhere.uucp (Steven King) writes:
>
>In article <1990Oct9.144542.19983@sco.COM> jfischer@sco.COM (Jonathan A. Fischer) writes:
>>In article <706@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
>>>Two questions about C++
>>>
>>>2   Testing of and assigning to 'this'
>>>
>>>Is it still possible to assign to 'this' in V2.0?
>>
>>	Technically, you can't assign to this, since it's "X * const this".
>>BUT, it's listed in Appendix B.3.3 as an "anachronism," something that is
>>provided for compatibility with earlier releases of cfront.  I.e., you can do
>>it, but technically you shouldn't.  Also, there is no requirement for a
>>compiler vendor to implement these anachronisms, so you are tainting your
>>portability by using them.
>>
>>>Can one rely on 'this' having the value zero on entry to the constructor
>>>if the object is allocated on the free store?
>>
>>	Yep, according to the same anachronisms section.
>
>	And only if the object isnt a member (or base) of another object.
>Even tho' the translator knows this information (it does pass a flag to
>destructors) there is NOT a reliable mechanism to determine this from within
>a constructor.

Good point.  Embedded objects are another good example why C++ compilers 
DO NOT typically support the (this==0) test to see if objects are on the
heap or not.  I haven't seen a C++ compiler released in years that 
supports this test.  Correct me if you find a counter-example.  The reason
that compilers DO NOT support this test is quite simple:  Later changes in
the language require that constructors not be invoked on failed allocations.
So, if a failed allocator returns a null pointer [because its out of memory,
for example], then the constructor must short circuit to a no-op.  
And how can a no-op check to see if (this==0) and take some specified action
if it is???  So, reasonable speaking, compilers have the option of supporting 
today's definitions of how its suppose to work, or yesterday's definition.  
Most compilers choose today's definition, guaranteeing that a constructor
will never be invoked when (this==0), making a (this==0) test inside a
constructor kind of pointless.

>	I wonder if there is enough of a need for this to consider adding it
>to the language...

As mentioned above, it would do nothing to add support for the (this==0)
test inside a constructor, because constructors must short-circuit to a no-op
if (this==0), because constructors are never invoked on a failed allocation.
However, there's other practical reasons why the heap verses stack test
can't be done:  Programmers have the option to dynamically allocate objects
on the stack using the placement version of new:

void doSomethingWithFooBar(int i)  // not intended to be a "good" example
{
    char space[100];
    if (i)
    {
        FOO& foo = *new(space) FOO;
        foo.doIt();
    }
    else
    {
        BAR& bar = *new(space) BAR;
        bar.doit();
    }
}

Thus, various flavors of the overloaded "new" operator could actually be
returning space on the stack.  Conversely, it is not uncommon for C++ 
programmers to create "light-weight" processes, whose stack space is not
uncommonly allocated out of heap.  Thus stack space, might actually be 
heap space, or using the above example, heap space might actually be
stack space might actually be heap space....

To my mind, the right thing to do is to keep the language out of these 
non-portable issues.  Usually, programmers can arrange for some simple,
if non-portable test to see if an object is "on the stack"

char* pstackstart;

int main()
{
   char stackstart;
   pstackstart = &stackstart;
   theRealMain();
}

int isOnStack(OBJECT* pobject)
{
    char c; 
    char* pstackend;
    pstackend = &c; 

    // reverse the order of the following test depending on whether your stacks
    // grow up or down:
    return  (pstackstart < (char*)pobject) &&  (pstackend > (char*)pobject);
}

If pstackstart were a process global, rather than a system global, and 
initialized at process startup time, this could also work with lightweight
processes.  Depending of course, on what you really intend by testing
if a object is "on the stack" ????

niklas@appli.se (Niklas Hallqvist) (10/16/90)

jimad@microsoft.UUCP (Jim ADCOCK) writes:

>In article <1990Oct14.224706.1934@nowhere.uucp> sking@nowhere.uucp (Steven King) writes:
>>
>>In article <1990Oct9.144542.19983@sco.COM> jfischer@sco.COM (Jonathan A. Fischer) writes:
>>>In article <706@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
>>>>Can one rely on 'this' having the value zero on entry to the constructor
>>>>if the object is allocated on the free store?
>>>
>>>	Yep, according to the same anachronisms section.
>>
>>	And only if the object isnt a member (or base) of another object.
>>Even tho' the translator knows this information (it does pass a flag to
>>destructors) there is NOT a reliable mechanism to determine this from within
>>a constructor.

>Good point.  Embedded objects are another good example why C++ compilers 
>DO NOT typically support the (this==0) test to see if objects are on the
>heap or not.  I haven't seen a C++ compiler released in years that 
>supports this test.  Correct me if you find a counter-example.

[ reasoning about why this == 0 not is supported deleted ]

>To my mind, the right thing to do is to keep the language out of these 
>non-portable issues.  Usually, programmers can arrange for some simple,
>if non-portable test to see if an object is "on the stack"

Well, as far as I can tell there IS a portable way testing this property.
Please go on and flame me if my reasoning is invalid!  Make a base class
like this:

class Locatable {
private:
  static int aux_is_in_heap; // Needed because we can't set non-static
			     // members before the ctor is called.
  int is_in_heap;
public:
  Locatable() { is_in_heap = aux_is_in_heap; aux_is_in_heap = 0; }
  void* operator new(size_t sz) { aux_is_in_heap = 1; /* real "new" code */ }
  int in_heap() { return is_in_heap; }
};

Locatable::aux_is_in_heap = 0;

then go on deriving publicly from Locatable, you now have the method in_heap
to play with.  Be aware of member objects, they appear as if they are not on
the heap, even if their enclosing object is, since new is only called for the
container object.  I would be interested in hearing about a portable function
to differentiate stack objects & member objects residing on the heap.

Of course, in a multi-tasking environment this scheme has to be refined,
this is left as a trivial exercise to the reader :-).

I would be very happy if you tell me about other flaws you see with this method.

								Niklas

-- 
Niklas Hallqvist                Phone: +46-(0)31-19 14 85
Applitron Datasystem            Fax:   +46-(0)31-19 80 89
N. Gubberogatan 30              Email: niklas@appli.se
S-416 63  GOTEBORG, Sweden             sunic!chalmers!appli!niklas

mike@taumet.com (Michael S. Ball) (10/16/90)

In article <58242@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:

>Good point.  Embedded objects are another good example why C++ compilers 
>DO NOT typically support the (this==0) test to see if objects are on the
>heap or not.  I haven't seen a C++ compiler released in years that 
>supports this test.  Correct me if you find a counter-example.  The reason

Try all cfront versions, Oregon C++, Turbo C++, Zortech (at least earlier
versions).  It's absolutely necessary if assignment to this is allowed.

What has probably misled you is that it only works if you DO assign to
this within the constructor.  If you don't the compiler generates code
to check for a zero value and allocate space for you, thus making the
value non-zero by the time you get to the user code.

Mike Ball
TauMetric Corporation
mike@taumet.com

jimad@microsoft.UUCP (Jim ADCOCK) (10/19/90)

In article <479@taumet.com> mike@taumet.UUCP (Michael S. Ball) writes:
|Try all cfront versions, Oregon C++, Turbo C++, Zortech (at least earlier
|versions).  It's absolutely necessary if assignment to this is allowed.

Hm, I can't get my local copy of cfront to accept assignment to this 
at all.  What version are you referring to?

|What has probably misled you is that it only works if you DO assign to
|this within the constructor.  If you don't the compiler generates code
|to check for a zero value and allocate space for you, thus making the
|value non-zero by the time you get to the user code.

Okay, I stand corrected.  The corrected statement then is:

There does exist some compilers that support the (this==0) test --
provided you also perform the assignment to this hack.  I guess this
is a case where two wrongs *do* make a right!

What do those compilers do for embedded objects? Presumably they always
state (this!=0) even if the embedded object is being created on the heap?
[IE, the (this==0) test, should your compiler decide to accept it, doesn't
really test if an object is on the stack vs heap or not, but rather whether
space has already been allocated for that object or not.]

mike@taumet.com (Michael S. Ball) (10/19/90)

In article <58351@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>In article <479@taumet.com> mike@taumet.UUCP (Michael S. Ball) writes:
>Hm, I can't get my local copy of cfront to accept assignment to this 
>at all.  What version are you referring to?

I use cfront 2.0

>Okay, I stand corrected.  The corrected statement then is:
>
>There does exist some compilers that support the (this==0) test --
>provided you also perform the assignment to this hack.  I guess this
>is a case where two wrongs *do* make a right!

This is the definition in a number of manuals.  Andy gave a complete
rundown of the subject in a recent posting, so I won't repeat it here.
It's NOT a way to determine if something is on the heap, but a tool
to allow the user control over allocation.  Thank goodness it's going
to disappear.

The only standard way to determine this is to overload operator new.
Communicating between operator new and the constructor is left as an
exercise for the reader.  Even then someone could fake you out by using
::new or something similar.

-Mike-

steve@taumet.com (Stephen Clamage) (10/20/90)

jimad@microsoft.UUCP (Jim ADCOCK) writes:

>There does exist some compilers that support the (this==0) test --
>provided you also perform the assignment to this hack.  I guess this
>is a case where two wrongs *do* make a right!

>What do those compilers do for embedded objects? Presumably they always
>state (this!=0) even if the embedded object is being created on the heap?
>[IE, the (this==0) test, should your compiler decide to accept it, doesn't
>really test if an object is on the stack vs heap or not, but rather whether
>space has already been allocated for that object or not.]

You are really making this more complicated than it is.  The assign-to-this
was a hack to allow constructors to do their own allocation.  The
technique was found to be unworkable in the presence of mulitple
inheritance (and was ugly as sin anyway), so was declared obsolescent
in favor of overloading new and delete.  This nicely separates the
orthogonal concerns of object initialization and space allocation.

If a class C has one or more base classes, let's call C a complete object,
and each of the base classes a sub-object.  Data members of C are each
complete objects, and each may in turn have sub-objects.

If a constructor for C contains an assignment to "this" and is invoked
as a result of using "new" to create a complete object of type C, then
"this" will be zero on entry to C's constructor.  In all other cases,
"this" will be the address of the object (or sub-object) being
constructed.

Ordinarily, base classes are constructed prior to executing any of
the user code in the derived class constructor.  Continuing the above
example (C::C() contains an assignment to "this"), a statement like
	C *cp = new C;
means that C::C() is entered with this==0 and no base classes have
been constructed -- and of course no space for the object has been
allocated.  Immediately following each assignment to "this" in C::C(),
the compiler generates code to construct all of the base classes and
perform all of the initializations in the constructor header.  Such
construction and initialization cannot be done sooner than assignment
to "this", and must not be done later.  This is why each possible
execution path through C::C() must contain one and only one assignment
to "this".
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com