Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!microsoft!jimad From: jimad@microsoft.UUCP (Jim ADCOCK) Newsgroups: comp.std.c++ Subject: Re: Conversions to/from void*, redux Keywords: Sun, C++ Message-ID: <71206@microsoft.UUCP> Date: 11 Mar 91 22:56:53 GMT References: <70935@microsoft.UUCP> <27D18D22.1608@tct.uucp> <71031@microsoft.UUCP> <27D5708A.29CF@tct.uucp> Reply-To: jimad@microsoft.UUCP (Jim ADCOCK) Organization: Microsoft Corp., Redmond WA Lines: 126 In article <27D5708A.29CF@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes: |According to jimad@microsoft.UUCP (Jim ADCOCK): |>Operator new and delete are *special* functions. Even if specified by the |>user, there is no reason why a compiler couldn't generate two versions of |>operator new and delete from the user specification. | |I see no reason to set "operator new" and "operator delete" aside into |a set of "special" functions. They are "special" only in that the |compiler calls them in contexts where their names are not explicitly |mentioned, and in C++ that's not anything special. They're just plain |functions: you can take their address and call them indirectly, etc. The reason to make them "really special" [they are already special -- see ARM page 261] is for the same reason that constructors and destructors are special -- that implementations can reasonably be expected to exist that don't use normal function calls for implementing such things. I'm proposing as a general rule that programmers not be able to take the address of any special function directly. If they need to take the address of special functions, then they can explicitly lay down a normal function that invokes the special function, and take the address of the normal function. Quote ARM Page 265: "The reason one cannot take the address of a constructor is that constructors have semantics that are closely tied to the semantics of memory allocation in all its varieties ...." ....Sounds to me like an equally valid reason to not allow taking the address of new/delete. |But let's assume that such an implementation existed, and let's assume |that there's a good reason for double compilation. Now, put yourself |in the shoes of the implementor, and answer me this question: "If you |can compile my one function two ways, thus adapting for alignment |restrictions, why can't you make other casts of |void*| to |T*| work |by using the same trick?" Because to do so assumes that the method required for going from a T* to a void* is easily invertable. If it is not easily invertable, then one cannot expect compilers/implementations to do it. |It's not so much that I insist on _doing_ such pointer conversions; if |they were non-portable, I would avoid them. What I insist is that |there be a recognition that the ARM already implicitly constrains |implementations to be capable of them, unless the implementor is just |being contrary. If you do not _insist_ on _doing_ such pointer conversions, then there is no reason to _require_ all C++ compilers to support them. Rather, on un*x-like systems where such conversions are easy to do, compiler can continue to support them, and on non-un*x-like systems where such conversions are difficult, compilers could choose not to support them. Thus, conversion from primitive type to non-primitive type would become implementation- dependent. This wouldn't break any existing code [which runs on un*x-like machines] |>|As has been mentioned before, ANSI C requires that |void*| and |char*| |>|have the same representation. If ANSI C++ does not conform to this |>|requirement, then compatibility with C libraries is compromised. |>|That may or may not matter to you, of course, but it matters to me. This is rightly a quality-of-implementation issue. Vendors who have an existing "Language-X" implementation that they wish to be compatible with, can choose to implement C++ to be compatible with that implementation. Vendors who do not have an existing "Language-X" implementation that they want to be compatible with should not have to be so restricted. Why should the ANSI-C++ committee "mandate" conformance with any particular "Language-X"? What would it *mean* to be "Language-X" conformant on a system that doesn't even *support* "Language-X" ? |>It is acceptible to me that void* and char* have "the same representation" |>[whatever that means -- hopefully the ANSI-C committee were not trying |>to specify *implementation* choices] | |In fact, they _do_ specify this particular implementation choice. In |particular, the following code must output "yes" in an ANSI C |environment: | | #include | #include | #include | main() | { | char c; | char *cp = &c; | void *vp = &c; | assert(sizeof(cp) == sizeof(vp)); | puts(memcmp(&cp, &vp, sizeof(cp)) == 0 ? "yes" : "no"); | exit(0); | } | |Since ANSI C++ will be based on ANSI C, I find myself hard-pressed to |imagine a rationale for breaking it under ANSI C++. Imagine this rationale: The ANSI-C++ committee see the light and chooses not to follow the ANSI-C mistake of trying to specify *implementation,* but rather restricts themself to specifying *language*. What does it mean to the ANSI-C *Language* to say that void*'s and char*'s have the "same representation" ??? I claim: nothing. Likewise, for example, I claim it means nothing to the C++ *Language* to say that member addresses within a labeled section must have ordered addresses -- there's no way to write a strictly conforming program that makes use of this "feature." |> -- as long as C++ then does not *require* that it be possible to convert |>from char* to class X*. | |But it already does require that conversino to be possible! Remember |that the definition of "object" is "region of storage" (section 3), |and read from ARM section 5.4, with my comments in [brackets]: | | It is guaranteed that a pointer to an object of a given size | [such as class T] may be converted to a pointer to an object | of the same or smaller size [such as char] and back again | without change. I know what it says. What *I* said is that I would support the idea of requiring void* and char* to have the same representation iff it is not *required* to be able to convert from char* to class X*. In general, I do not think C++ should *require* that pointers to primitive objects be convertable to pointers to objects of class type. This in no way prevents un*x-like C++ compilers from supporting such casting. It just leaves the door open for non-un*x-like implementations to exist.