[comp.lang.c++] Some Questions

kempf@hplabsc.UUCP (02/06/87)

I am posting these questions for a friend of my, who is very shy.
Please reply via. e-mail, unless the responses may be of general
interest. Thank you.

		Jim Kempf	kempf@hplabs.hp.com

----------------------------------------------------------------------
I have three questions about c++ (in particular version 1.1).

Question 1:
Is there any reason that initialization of static members couldn't be
allowed.  The version 1.1 compiler gives an error message when an
initializer is present for a static member, and "THE BOOK" claims (page
275):

	No initializer can be specified for a static member, and it
	cannot be of a class with a constructor.

What is the technical reason for this?  I have not been able to think of
any reason that this shouldn't be legal given that static variables can
be initialized.

Question 2:
What are the semantics of a private constructor?  It seems to me that a
private constructor should be invisible outside of members (and friends)
of a class.  Yet the compiler (version 1.1) does not seem to enforce
this.  Is this a bug in the compiler, or am I missing something?

Question 3:
The following file compiles without error under version 1.1 on our
machine.  It seems to me that it should complain about the arguments to
the constructor since no constructor that accepts arguments has been
declared.  Am I missing something or is this a bug in the compiler?

#include <stream.h>

class foo {
	char c;
	long l;
	float f;
    friend ostream& operator<<(ostream&, foo&);
};

void bar()
{
    foo* p = new foo('a', 13, 99.9);
    cerr << *p;
}

mikem@otc.UUCP (02/09/87)

The first question asked by the "shy" person was:

> Is there any reason that initialization of static members couldn't be
> allowed.  The version 1.1 compiler gives an error message when an
> initializer is present for a static member, and "THE BOOK" claims (page
> 275):
> 
> 	No initializer can be specified for a static member, and it
> 	cannot be of a class with a constructor.
> 
> What is the technical reason for this?  I have not been able to think
> of any reason that this shouldn't be legal given that static variables
> can be initialized.

The reason is that include files containing the class definition could appear
in multiple .c files, and there is no easy way of resolving which file
actually "owns" the static member. (For ordinary static variables, they clearly
only exist in a single file.)

E.g:  Consider the following include file  "foo.h", and the two .c files
   "bar1.c", "bar2.c"

----------------------------------------------------------------------------
// foo.h
class SomeClass {
	int i;
    public:
	SomeClass(int ii) { i = ii; }

	int show()	 { return i; }
	void set(int ii) { i = ii; }
};

class Foo {
	static SomeClass  sc(1);
    public:
	int  show() { return sc.show(); }
	void set(i) { sc.set(i); }
};
// (end foo.h)
----------------------------------------------------------------------------
// bar1.c
#include "foo.h"

 .....

// (end bar1.c)
----------------------------------------------------------------------------
// bar1.c
#include "foo.h"

 .....

// (end bar1.c)
----------------------------------------------------------------------------

The way that ctors on static variables are made to work is by generating a
function like:

    _STI_whatever()	// or something like that
    {
	// code to actually call the constructor for the variable.
    }

At link time, the program "munch" examines the namelist, and if there are
any _STI_* it puts these names into a variable "_ctors" which is an array
of ptrs-to-fns. _main() then arranges to call everything appearing in _ctors.
(_main() is called automatically at the start of main()).

So the problem is that the #include "foo.h" in will result in _STI_* fns
within bar1..c and bar2..c and _main() will therefore call the ctor twice,
which is wrong.

It may be possible to do it by having cfront generate the _ST functions
with redundancy-resolving information built into the name: E.g:

 In bar1..c:

    _STS_7_bar1_c_Foo_sc()
    {
	// call ctor for Foo::sc
    }

 In bar2..c:

    _STS_7_bar2_c_Foo_sc()
    {
	// call ctor for Foo::sc
    }

(where the "7" indicates where the filename information finishes, and the
 variable name starts, i.e: specifies where to look in the name for
 redundancy-resolving information.)

It may then be possible for munch to recognize _STS prefixes and resolve the
ambiguity so that only one of _STS_7_bar1_c_Foo_sc() and _STS_7_bar2_c_Foo_sc()
are puts into _ctors.

The trouble with this is that the current operation of munch (using nm) can't
be guaranteed to work in all possible c linker environments. But this may
be acceptable (C++ in some environments would then still have to generate a
"sorry, not implemented" message). Personally, I do find it annoying not to
have static class members with ctors. It's something that tends to arise
in applications, and not so much when you're just writing libraries.

Since I'm on the subject of munch, I may as well post a minor
improvement to the version in 1.1: Currently, where the constructor for
one static object uses another static object that also has a
constructor, munch will tend to arrange that the constructors get
called in the wrong order, with resulting inexplicable core dumps. This
is because the order that the names appear in the namelist is in the
CORRECT order, but munch uses a simple stacking arrangement when making
_ctors, so the order gets reversed. (Note that this behaviour is
correct for dtors, however.)

The fix for munch.c is as follows:

Add a declaration:

	sbuf* tail;

where  sbuf *ctor,*dtor are declared.

Replace case 'I' in the switch by:

	case 'I':	// ctors are handled fifo
		if (tail)
		    tail = (tail->next = new sbuf(NULL,st));
		else
		    tail = ctor = new sbuf(NULL,st);

Note that this cannot be considered a general answer to the problem, since
the "munch" method as it stands depends heavily on the linker environment.

			Mike Mowbray
			Systems Development
			Overseas Telecommunications Commission (Australia)

UUCP:   {seismo,mcvax}!otc.oz!mikem              ACSnet: mikem@otc.oz

mikem@otc.UUCP (02/09/87)

(I decided to post the answers instead of direct e-mail, since there are
 obviously lots of people going through similar learning curves, and the
 traffic on comp.lang.c++ has been a bit thin of late anyway...)

The second question was:

> Question 2:
> What are the semantics of a private constructor?  It seems to me that a
> private constructor should be invisible outside of members (and friends)
> of a class.  Yet the compiler (version 1.1) does not seem to enforce
> this.  Is this a bug in the compiler, or am I missing something?

Yes, a private constructor must mean that nothing except other
instances or friends can make one of the things. E.g:

	// privctor.c

	class Crap {
		int i;
		Crap(int ii) { i = ii; }

		friend void func();
	};

	void func()
	{
	    Crap c(1);	// ok, since func is friend of Crap
	}

	main()
	{
	    Crap c(1);  // not ok.
	}

   $ CC privctor.c
     CC  privctor.c:
     "privctor.c", line 18: error:  Crap::Crap() is private
     1 error
   $

If the compiler isn't enforcing this, there's probably a bug in the port.

The third question was:

> The following file compiles without error under version 1.1 on our
> machine.  It seems to me that it should complain about the arguments to
> the constructor since no constructor that accepts arguments has been
> declared.  Am I missing something or is this a bug in the compiler?
> 
> #include <stream.h>
> 
> class foo {
>        char c;
>        long l;
>        float f;
>    friend ostream& operator<<(ostream&, foo&);
> };
> 
> void bar()
> {
>    foo* p = new foo('a', 13, 99.9);
>    cerr << *p;
> }

This is a bug that occurs for me too. (There seem to be a few of them
around when something slightly out of the ordinary is asked of "new".)
The translator totally ignores the arguments, generating something
like:

int bar()
{
    struct foo *_auto_p;

    _auto_p = (struct foo*) _new((long)12);
}

Note that the generated code should still work in this case, which is probably
a fluke. cfront seems to be simply throwing everything it doesn't need away.

I've also had bugs emerge for invocations of new where an array of things
with constructors was asked for, but I don't have the example handy.

Hopefully, release 1.2 may be out soon. Maybe this is one of the bugs
that got fixed?

			Mike Mowbray
			Systems Development
			Overseas Telecommunications Commission (Australia)

UUCP:   {seismo,mcvax}!otc.oz!mikem              ACSnet: mikem@otc.oz