Xref: utzoo comp.lang.c++:10952 comp.std.c++:508 Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!cs.utexas.edu!uunet!decwrl!infopiz!lupine!rfg From: rfg@NCD.COM (Ron Guilmette) Newsgroups: comp.lang.c++,comp.std.c++ Subject: Re: the SUN way.. *&$^#%) Message-ID: <3089@lupine.NCD.COM> Date: 25 Dec 90 00:35:12 GMT References: <1990Dec20.114821@roadster.bellcore.com> <3069@lupine.NCD.COM> <246@salt.bellcore.com> Followup-To: comp.lang.c++ Organization: Network Computing Devices, Inc., Mt. View, CA Lines: 157 In article <246@salt.bellcore.com> garnett@shera.UUCP (Michael Garnett) writes: +In article <3069@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes: +>In article <1990Dec20.114821@roadster.bellcore.com> garnett@thumper.bellcore.com writes: +>+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 *)); +>+ +> +> +>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) +> { +> /* ... */ +> } +Q: I have used a "fully ANSI compliant" C compiler (I uassumed they were not + lying). The ANSI C prototype for bsearch looks just like the one I + suggested (and verifyed from the ObjectWorks\C++ search.h). ANSI C + will accept the compar function as is -- C++ will not. For C++ I *must* + accept const void*'s. This means that strcmp is not a valid compare + function for bsearch in C++, but it *IS* in ANSI C. (Hers's the Q): + Is my ANSI C compiler lenient? Am I correct that C++ will NOT accept + compare (const T*, const T*) ? Yes. Your "ANSI" C compiler is being too lenient. This would be unacceptable to most C++ compilers. +Q: If is is "always legal to cast any pointer-to-T to a void*" then why + won't C++ accept compar(const T*, const T*)? (Assuming that my compiler + was correct in telling me that it wouldn't) Why does C++ allow + a call of the form 'free(T*)' when free has a parameter profile of + 'free(void*)'? I see no difference in these 2 cases. Both involve + accepting T* where a void* is wanted. You ask "why?" The answer is "Because those are the rules". There are important differences in the two cases you cite. +I have not checked this further, but SUN C++ will not accept a compar +function with any other prototype than 'int compar(...)'. If the '...' in +the compar prototype is meant to keep C++ from checking the type(s) of the +parameters to compar, SUN C++ is wrong. Right? Hummm... I guess that you have a good point there. In fact, now that you've forced me to think about it, I'd have to say that there simply is no 100% correct way to declare qsort() and/or bsearch() in C++. I guess that given C++'s current (very strict) rules regarding type- matching for formal parameters of "pointer-to-function" types, the closest approximation that you could get that might possibly work for C++ (without causing all sorts of compile-time errors) would be: void *bsearch(const void *, void *, unsigned, unsigned, const void *); In this case we are avoiding the problem of getting the types to perfectly match on the final ("pointer-to-function" type) argument altogether. Unfortunately, the only way that we can do this is to fake out the compiler and to tell it that the final argument is a void* rather than a pointer-to-function. Now for the bad news... even this (cruddy?) solution may not work because in both ANSI C and C++ it is illegal to convert (either explicitly or implicitly) a pointer-to-function type value to a void* type value. So we are back to square one. As I said, you simply cannot correctly declare qsort() and bsearch() in C++. I hope the the x3j16 Library Working Group takes note of this problem and that either (a) they mandate template versions of these (generic) functions, or else (b) that they at least encourage implementors to avoid the mistake of putting (bogus) declarations of these sorts of things into the header files that they ship with their products. +I changed the prototype in SUN'c search.h to the one that I suggested, +and (as I expected) my code compiled again. My compar must accept +2 const void*'s, but I'm used to this from 2 other compilers. That solution is probably the best stop-gap available, but there are two problems: 1) Your code may cease to work if you ever try to port it to certain kinds of machines which have multiple flavors of pointers, and 2) You are now forced to declare all of your comparison routines to accept a pair of void* parameters. Then, within each of these comparison functions, you must convert each of these two void* pointers to some other (useful) type of pointer. At the very least this is an annoyance, but at the very worst, it will not even be legal to do that for some pointer types in C++! +SUMMARY: + is 't1 (*)(...)' supposed to suspend the type-checking of parameter type + matching in pointer-to-function parameters? OR should the compiler + force the actual parameter to have a 't1 (*)(...)' parameter profile? I believe that it's the latter. It is my understanding that the following two types are NOT compatible in C++: typedef void (*elipsis_function_pointer_type) (...); typedef void (*two_arg_function_pointer_type) (void *, void*); I believe that this is a good and necessary rule (i.e. that these must be treated as being INCOMPATIBLE TYPES) and that it should stay in the langauge. + If the 't1 (*)(...)' is forced, then what about ANSI's stdargs where + one needs at least one parameter to find the beginning of the variable + arguments? Good question. C++ (unlike ANSI C) allows both regular (non-member) functions and member functions to be declared with only an ellipsis as the formal argument list. I'm not sure that this was such a hot idea (because it can make stdarg usage problematic) but I believe that this was permitted in C++ in order to help people to transition from (and to interface to) old C programs. + I like the idea of a pointer to an unknown/undeterminable type, but why + can't void* serve this purpose? It can't because void* is an honest to goodness actual type as far as the C++ (and ANSI C) type matching rules are concerned. In particular, the following is REQUIRED to get an error in both C++ and in ANSI C: void (*p1) (void*, void*); void (*p2) (int*, int*); ... p1 = p2; /* must get a type-mismatch error */ What I suggested was allowing: typedef ? T; void (*p3) (T*, T*); ... p3 = p2; /* OK - matches as far as possible */ After some consideration however, I think that I should retract that suggestion and just hope that the really *BEST* solution (i.e. template definitions for qsort and bsearch) gets implemented someday. -- // 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.