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.