Xref: utzoo comp.lang.c++:10870 comp.std.c++:501 Path: utzoo!attcan!utgpu!news-server.csri.toronto.edu!clyde.concordia.ca!thunder.mcrcim.mcgill.edu!snorkelwacker.mit.edu!apple!usc!zaphod.mps.ohio-state.edu!tut.cis.ohio-state.edu!purdue!decwrl!infopiz!lupine!rfg From: rfg@NCD.COM (Ron Guilmette) Newsgroups: comp.lang.c++,comp.std.c++ Subject: Re: the SUN way.. *&$^#%) Keywords: Sun, C++, F***-up Message-ID: <3069@lupine.NCD.COM> Date: 22 Dec 90 06:39:39 GMT References: <1990Dec20.114821@roadster.bellcore.com> Followup-To: comp.lang.c++ Organization: Network Computing Devices, Inc., Mt. View, CA Lines: 148 In article <1990Dec20.114821@roadster.bellcore.com> garnett@thumper.bellcore.com writes: + +I'm P***ed! (excuse the language!) Grrrr.... + +This is one stupid thing: +char *bsearch (const char*, char*, unsigned, int, int (*)(...)) ); + +It should be like this: +void *bsearch(const void *, void *, unsigned, unsigned, + int(*)(const void *, const void *)); + +Do they mean to tell me that I really have to deal with stdargs/varargs when +I want to write a quick compare routine for my data type!? + +Does anyone else see this is a VERY STUPID move on Sun's Part? + +HEY SUN: LISTENING? + DON'T RESIST THE ANSI C SPEC! void*s are NICE. You're 'cc' even + supports them (but does not use them). How about fixing these + header files?? huh?? I think that you are blaming Sun for a problem in the language which is not their fault. (Actually, I think that this is an outstanding problem in both C++ and in ANSI C.) The problem arises for all sorts of functions designed to accept function pointers. I seem to recall asking Bjarne about this problem some long time ago with respect to the UNIX(tm) qsort() function. Both qsort() and bsearch() have a similar need to be fed a pointer to a function which itself takes two parameters of type pointer-to-T, where the type T is the same for both of the two parameters (but is never actually the type `void'). For ANSI C, your proposed soultion would just about work, but not quite. Here is the problem. I have some existing ANSI C code which looks like: #include typedef /* ??? */ T; int compar (const T* left, const T* right); ... bsearch (key, base, nel, sizeof (*key), compar) ... int compar (const T* left, const T* right) { /* ... */ } Now if we put the declaration of bsearch() that you have suggested into then my code (which used to compile cleanly) would cease compiling. That would make me get mad, and then I'd be cursing you instead of you cursing Sun. Now I know what you are going to tell me. You are going to suggest that I start to get *really* type-safe, and that I re-write my compar() routine to look like: int compar (const void *__left, const void*__right) { const T* left = (T*) __left; const T* right = (T*) __right; /* ... */ } But what if that compare routine is stashed away in a precompiled library? In other words, what if I just plain don't have the option of changing it? Now I'm REALLY going to get mad at you! In C++ the situation is even worse because in C++ I don't even have the option of rewriting my compar() function as shown above! Why? Well, in C++ it is always legal to cast any pointer-to-T to a void* however for some T's it is illegal to cast backwards from a void* to a T*. Thus, in cases like qsort() and bsearch() the ultimate effect of all of C++'s rules (intended to enforce type-safety) is to force us to totally abandon type-safety for the pointer-to-function argument(s)!!! There are two possible solution to this problem. The first is to leave things just as they are. That means that there will be no cross-checking whatsoever on the types of the parameters which the (user-supplied) compar() function accepts (or even on the number of parameters which it expects). The second solution is more radical. It involves enhancing the language so that we can express the idea of a type which has a name but which is otherwise undefined (and undefinable). One possibility would be: typedef ? T; // a generic unknown/unknowable type void *bsearch(const void *, const T *, unsigned, unsigned, int(*)(const T *, const T *)); In this case, the argument matching rules would have to be made smart enough to insist on proper matching all the way down to the point where we hit the mystery type T. In this case, the benefit (relative to the current loose-typing situation) would be that: comparison functions would be forced to take exactly two parameters each parameter of each comparison function would be forced to be of some pointer type each parameter of each comparison function would be forced to be of some pointer-to-CONST type both parameters of any given compare function would be forced to be of the same type the type of the second argument to bsearch would be forced to be the same as the type of the parameters to the comparison function (and vise-versa) Obviously, we could add one hell of a lot of type-safety (for situations like these) with only a very minor enhancement to the language. But wait! Before you rush off to phone your local implementor to demand the addition of this feature, be advised that even this (cute?) idea will not entirely solve the problems associated with functions like qsort() and bsearch(). To understand the *REAL* problem, just try writing your own implementation of qsort() entirely in ANSI C (or C++). Now try porting it to some machine for which there is more than one representation for pointer values (e.g. DG Eclipse, HP 3000). Keep in mind that on machine like this there is actually some shifting involved when you assign a void* to an int* (and vise-versa). In short, IT IS IMPOSSIBLE TO WRITE QSORT() ENTIRELY IN ANSI C OR IN C++ IN SUCH A WAY THAT IT IS COMPLETELY PORTABLE. This is the *real* problem. Perhaps the only truly clean solution (in C++) is to recognize qsort() and bsearch() for what they really are (i.e. generic functions) and to implement them accordingly (as templates). Any volunteers? -- // 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.