[comp.lang.c++] porting tips for c++

jmd@swbatl.sbc.com (Jim Doherty 5-0804 11-Y-03) (08/06/90)

I am curious if anyone has some general porting tips for c++, we have run into a
few items such as suns wisdom in moving the include files to a directory called
/usr/CC/incl, but we are interested in more gotchas that we might avoid down the
line, we currently in the middle of development and would like to avoid having
to discover things many of you have already run into !!

any help would be appreciated !
thanks...
-- 
James M Doherty  - SWBT - Advanced Technology Planning
One Bell Center Room 11-Y-03 St. Louis, Mo. 63101
UUCP: { pyramid, ihnp4, bellcore }...!swbatl!jmd
PHON: 314-235-0804 FAX: 314-235-0727

jimad@microsoft.UUCP (Jim ADCOCK) (08/08/90)

Try reading "Portable C" by Rabinowitz and Schraam?  [I think] as a 
starting point.

My only complaint is that the "Portable C" rules are written, true to 
C programming "style", to be as permissive as possible while still giving 
you reasonably portable C code.  Good C++ style would be much more 
restrictive.

Still, this book contains a wealth of information about writing portable
C [and therefor C++] code.

gwu@nujoizey.tcs.com (George Wu) (08/08/90)

     Here's something I wrote up for our people when we switched from GNU
C++ 1.36.2 to Sun C++ (AT&T Cfront 2.0).

                                                        George


                                MEMORANDUM

FROM:           George Wu

TO:             User Interface Team

DATE:           June 27, 1990

SUBJECT:        Switching from GNU C++ to Sun C++

_______________________________________________________________________________


     This memorandum summarizes the necessary procedures used to switch from
GNU's C++ compiler to Sun's C++ compiler (AT&T Cfront 2.0).  Two areas are
addressed: general code changes; and environmental changes specific to the IMS
project.  There are several attachments to this memorandum: a description of CC
and various C++ tools; a list of compiler implementation specific behavior; and
a list compiler "not implemented" error messages with translations to English
(known to be incomplete).


                        GENERAL C++ CODE MODIFICATIONS
                        ==============================

The following discoveries were made in porting IMS UI code the Sun C++:

        o Constructors cannot have default argument values.  The compiler does
          not always complain about this problem.  Consider the following
          example:

                class C {
                public:
                    C(int x = 0);
                    C(int x, int y = 0);
                };

          In order to compile this code with Sun's C++ compiler, the following
          re-arrangement may be necessary:

                class C {
                public:
                    C(int x);
                    C(int y, int y);
                private:
                    init(int x = 0, int y = 0);
                };

                C::C(int x) { init(x); }
                C::C(int x, int y) { init(x, y); }

        o Inline functions cannot contain loops.  The fix is obvious: make
          the function an ordinary one.

        o Default argument values can only be declared in one place.  Declare
          default argument values in the class definition, not in the function
          definition.

        o Values of type void* can only be assigned to variables of type void*.
          The C++ permits any pointer value to be automatically cast to a
          void* where necessary, but this relationship is not symmetrical.
          Expressions of type void* must therefore be explicitly cast to the
          appropriate pointer type.  For example, given char* s and void* p,
          s = p becomes s = (char *) p.


        o The name of a function is not automatically converted to the address
          of the function when assigned to a function pointer.  Explicitly use
          the address of operator (&).  The Sun C++ compiler appears to only
          complain about this under certain, indeterminate conditions.  When
          it doesn't complain, the Sun C++ compiler sometimes generates bad
          code.

        o The type of a pointer to a function includes the function argument
          types, ie. the function prototyping must also be specified when
          using function pointer.

        o When a pointer to a class member function is used, explicitly
          resolve the class scope.  Even in cases where there is no possibilty
          of confusion, C++ requires the scope.  Therefore, instead of:

                fp = &init;

          you must state:

                fp = &C::init;

          Failure to specify the class will result in the C++ compiler
          generating bad code, as evidenced by a core dump when the function
          pointer is dereferenced.

        o Default argument values which require run-time calculations may
          cause the C compiler (to which Cfront outputs C code) to complain
          about undefined temporary variables.  For instance:

                class C {
                public:
                        C(int size = round(0.5 * inches));
                };

          will cause the C compiler to generate an error message complaining
          about an undefined variable.  The variable name will not only be
          mangled, but will fail to unmangle to any C++ variable.




----
George J Wu                           | gwu@tcs.com or ucbcad!tcs!gwu
Software Engineer                     | 2121 Allston Way, Berkeley, CA, 94704
Teknekron Communications Systems, Inc.| (415) 649-3752

keith@csli.Stanford.EDU (Keith Nishihara) (08/09/90)

One correction and a few additional points on this memo.

In <943@tcs.tcs.com> gwu@nujoizey.tcs.com (George Wu) writes:
>     Here's something I wrote up for our people when we switched from GNU
>C++ 1.36.2 to Sun C++ (AT&T Cfront 2.0).

>FROM:           George Wu
>DATE:           June 27, 1990
>SUBJECT:        Switching from GNU C++ to Sun C++

>        o Constructors cannot have default argument values.  The compiler does
>          not always complain about this problem.  Consider the following
>          example:
>                class C {
>                public:
>                    C(int x = 0);
>                    C(int x, int y = 0);
>                };

It certainly does allow default values in constructors (as in other functions)
but it does not have a rule preferring a non-default argument to a default
argument while handling overloading.  This applies to all functions, not
just constructors.  Thus with the definitions above, a call `new C(1)'
is ambiguously C(1) or C(1, 0).  Gnu resolves this to C(1), preferring
the non default argument case.  I ran across this problem with varargs
optional arguments:
	void Message(const char *string);
	void Message(const char *fmt ...);
	Message("hello");		// Ambiguous.

* Sometimes cfront becomes confused about functions and objects having
the same name:
	class Message // etc.
	class Console { public: void Message(const char *) // etc.
Now in functions which are members of Console, attempts to create
a new Message objects generate a series of several error messages:
	void Console::Message(const char *string) { m = new Message(string); }
		// Error.

One fix is to wrap the object in a renamed derived class:
	class RenamedMessage : public Message {};
	void Console::Message(const char *string)
	  { m = new RenamedMessage(string); }	// OK.

* Friend class declarations are not accepted until the friend class has
been seen.

* `delete' does not accept pointers to built in types.  They must be cast
to void *.
	char *p = new char[20];
	delete p;			// Bad argument 1 type for op delete.
	delete (void *)p;		// OK.

* Casts are not allowed on lvalues.
	#define IncPtrBytes(p, inc)	((int)(p) += (inc)	// Error.

* Furthermore the fix I adopted for the above (using overloaded inline
functions) compiled, but appears not to work -- the pointer is never
incremented at all:
	inline void IncPtrBytes(char *p, int inc)
	  { p = (char *)((int)p + inc); }
	inline void IncPtrBytes(short *p, int inc)
	  { p = (short *)((int)p + inc); }
	etc.
I discovered this during a demo on a customer site, when I no longer
had the compiler available to investigate.  I may be wrong.

* If code defining a varargs function is optimised (-O), the varargs stuff
no longer works.  At best, arguments are not passed.  At worst, core dumps
result from attempts to call such functions.
This problem is potentially the most serious.  However it may be that it
is due to inclusion of a wrong header file or similar -- I did have some
include path confusion when I was trying to do my port, and I did not
have time to fix the problem.
I had no problems when I left the code unoptimised.  Note that the code
calling the varargs function may be optimised or not with no change
in bahaviour.

Neil/.

cline@cheetah.ece.clarkson.edu (Marshall Cline) (08/10/90)

In article <943@tcs.tcs.com> gwu@nujoizey.tcs.com (George Wu) writes:
>... This memorandum summarizes the necessary procedures used to switch from
>GNU's C++ compiler to Sun's C++ compiler (AT&T Cfront 2.0)....

Summary: Most of the things cfront wouldn't accept are in fact wrong C++.

>	o Constructors cannot have default argument values.  The compiler does
>	  not always complain about this problem.  Consider the following
>	  example:
>		class C {
>		public:
>		    C(int x = 0);
>		    C(int x, int y = 0);
>		};

A good reason that cfront wouldn't accept this is that it's ambiguous!  Ex:
C c(10); //Should `c' be constructed with C::C(int) or with C::C(int,int=0)?

>	o Default argument values can only be declared in one place.  Declare
>	  default argument values in the class definition, not in the function
>	  definition.

This is not a bug in cfront: ``A default argument cannot be redefined
by a later declaration (not even to the same value).''  [ARM, p.141].
However ``early versions of C++ allowed multiple default initializers
for a single argument, as long as they were equivalent.  Defining
"equivalent" proved problematic, however.''  [ARM, p.144].
Note however that subsequent declarations can *add* default args in a
right-to-left fashion...

>	o Values of type void* can only be assigned to variables of type void*.
>	  The C++ permits any pointer value to be automatically cast to a
>	  void* where necessary, but this relationship is not symmetrical.
>	  Expressions of type void* must therefore be explicitly cast to the
>	  appropriate pointer type.  For example, given char* s and void* p,
>	  s = p becomes s = (char *) p.

Again cfront is correct (I'd be surprised if GNU C++ accepted this either!).
ANSI C allows automatic coersion from void* to anything*, but this is a BIG
hole in the type system, since the anything* to void* cast (which is allowed
in both C and C++) would allow any pointer type to be silently coersed to any
other pointer type by going through void*.

>	o The type of a pointer to a function includes the function argument
>	  types, ie. the function prototyping must also be specified when
>	  using function pointer.
	  ^^^^^-- I assume you mean `declaring' -- M.Cline

Yes, this too is correct.  The type of a pointer-to-function not only
includes the number and types of the function's parameters, it also
has the function's return value.

Marshall Cline
--
==============================================================================
Marshall Cline / Asst.Prof / ECE Dept / Clarkson Univ / Potsdam, NY 13676
cline@sun.soe.clarkson.edu / Bitnet:BH0W@CLUTX / uunet!clutx.clarkson.edu!bh0w
Voice: 315-268-3868 / Secretary: 315-268-6511 / FAX: 315-268-7600
Career search in progress; ECE faculty; research oriented; will send vita.
PS: If your company is interested in on-site C++/OOD training, drop me a line!
==============================================================================

pcg@cs.aber.ac.uk (Piercarlo Grandi) (08/10/90)

On 8 Aug 90 17:25:15 GMT, keith@csli.Stanford.EDU (Keith Nishihara) said:

keith> [ ... a couple of bad problems with cfront ... ]

keith> * Casts are not allowed on lvalues.
keith> 	#define IncPtrBytes(p, inc)	((int)(p) += (inc)	// Error.

This is not allowed. Technically a cast is (like) an unary operator (in
C as in C++, actually in C++ even more), and its result is not an
lvalue, it is a value (which means that you can use a cast to force the
order of a computation, incidentally). To obtain the effect of an
lvalue-cast you can use something like

 	#define IncPtrBytes(p, inc)	(*(int *)&(p) += (inc))	// OK

except that this does not work (but some compilers allow it!) if 'p'
has storage class register.

keith> * Furthermore the fix I adopted for the above (using overloaded inline
keith> functions) compiled, but appears not to work -- the pointer is never
keith> incremented at all:
keith> 	inline void IncPtrBytes(char *p, int inc)
keith> 	  { p = (char *)((int)p + inc); }
keith> 	inline void IncPtrBytes(short *p, int inc)
keith> 	  { p = (short *)((int)p + inc); }
keith> 	etc.
keith> I discovered this during a demo on a customer site, when I no longer
keith> had the compiler available to investigate.  I may be wrong.

You are wrong, I guess. In your example 'p' is passed by value. You
should have declared 'p' as a reference to a char pointer (it's not the
only problem with your example, but the others are matters of style --
for example, are you sure that pointers on your machine are int when
seen as a numeric value?). One could rewrite your example (more
portably) as:

	inline void DoIncPtrBytes(void *ptr,unsigned inc,size_t bytes)
	{
		ptr = (char *) ptr + inc*bytes;
	}
	#define IncPtrBytes(ptr,inc) DoIncPtrBytes((ptr),(inc),sizeof *(ptr))

There are other (better) variations.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

gwu@nujoizey.Berkeley.EDU (George Wu) (08/14/90)

In article <CLINE.90Aug9135536@cheetah.ece.clarkson.edu>,
cline@cheetah.ece.clarkson.edu (Marshall Cline) writes:
|> In article <943@tcs.tcs.com> gwu@nujoizey.tcs.com (George Wu) writes:
|> >... This memorandum summarizes the necessary procedures used to switch from
|> >GNU's C++ compiler to Sun's C++ compiler (AT&T Cfront 2.0)....
|> 
|> Summary: Most of the things cfront wouldn't accept are in fact wrong C++.
|> 
|> >	o Constructors cannot have default argument values.  The compiler does
|> >	  not always complain about this problem.  Consider the following
|> >	  example:
|> >		class C {
|> >		public:
|> >		    C(int x = 0);
|> >		    C(int x, int y = 0);
|> >		};
|> 
|> A good reason that cfront wouldn't accept this is that it's ambiguous!  Ex:
|> C c(10); //Should `c' be constructed with C::C(int) or with C::C(int,int=0)?

     Yes, you can see the weasel wording I used where I didn't really
understand why the compiler was complaining (only sometimes).

|> 
|> >	o Default argument values can only be declared in one place.  Declare
|> >	  default argument values in the class definition, not in the function
|> >	  definition.
|> 
|> This is not a bug in cfront: ``A default argument cannot be redefined
|> by a later declaration (not even to the same value).''  [ARM, p.141].
|> However ``early versions of C++ allowed multiple default initializers
|> for a single argument, as long as they were equivalent.  Defining
|> "equivalent" proved problematic, however.''  [ARM, p.144].
|> Note however that subsequent declarations can *add* default args in a
|> right-to-left fashion...

     Apparently, GNU C++ 1.36.2 was such an "early" compiler.

|> 
|> >	o Values of type void* can only be assigned to variables of type void*.
|> >	  The C++ permits any pointer value to be automatically cast to a
|> >	  void* where necessary, but this relationship is not symmetrical.
|> >	  Expressions of type void* must therefore be explicitly cast to the
|> >	  appropriate pointer type.  For example, given char* s and void* p,
|> >	  s = p becomes s = (char *) p.
|> 
|> Again cfront is correct (I'd be surprised if GNU C++ accepted this either!).
|> ANSI C allows automatic coersion from void* to anything*, but this is a BIG
|> hole in the type system, since the anything* to void* cast (which is allowed
|> in both C and C++) would allow any pointer type to be silently
coersed to any
|> other pointer type by going through void*.

     We in fact knew about this problem, and have discussed it to some
degree with Michael Tiemann.  GNU C++ does indeed permit the automatic
coercion from void* to any other pointer type, at least in 1.36.2.  I
was not involved in the discussion with Michael Tiemann, and therefore
do not know the resolution, if any.  Michael, if you care to reply, the
person who complained to you about this from our company was Chris Bedford.

|> 
|> >	o The type of a pointer to a function includes the function argument
|> >	  types, ie. the function prototyping must also be specified when
|> >	  using function pointer.
|> 	  ^^^^^-- I assume you mean `declaring' -- M.Cline
|> 
|> Yes, this too is correct.  The type of a pointer-to-function not only
|> includes the number and types of the function's parameters, it also
|> has the function's return value.

     In general, most of our bugs were indeed because our code was wrong.
I'd even recommend porting between the two compilers even if you don't
need to.  We found quite a few bugs that wouldn't have arisen until much
later if we hadn't, epecially since not a few of our programmers were
writing their very first C++ code, often using outdated texts or examples.

							George

----
George J Wu                           | gwu@tcs.com or ucbcad!tcs!gwu
Software Engineer                     | 2121 Allston Way, Berkeley, CA, 94704
Teknekron Communications Systems, Inc.| (415) 649-3752

rfg@NCD.COM (Ron Guilmette) (08/15/90)

In article <957@tcs.tcs.com> gwu@nujoizey.Berkeley.EDU (George Wu) writes:
>
>     In general, most of our bugs were indeed because our code was wrong.
>I'd even recommend porting between the two compilers even if you don't
>need to.  We found quite a few bugs that wouldn't have arisen until much
>later if we hadn't, epecially since not a few of our programmers were
>writing their very first C++ code, often using outdated texts or examples.

This advice is quite sound.  If you want to end up with "correct" C++ code
it won't hurt to have multiple compilers checking your work along the way.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

rfg@NCD.COM (Ron Guilmette) (08/16/90)

In article <PCG.90Aug10152935@athene.cs.aber.ac.uk> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>
>This is not allowed. Technically a cast is (like) an unary operator (in
>C as in C++, actually in C++ even more), and its result is not an
>lvalue,...

Just for the sake of clarity, I though I should point out that this is *not*
a hard and fast rule.

E&S notes (on page 69):

	"The result of a cast to a reference type is an lvalue; the results
	of other casts are not."
-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.