Some things are standard and don't need enforcing, some are standard but unenforced, some are enforced but nonstandard. Bobby explains which is which.
Copyright © 1999 Robert H. Schmidt
To ask Bobby a question about C or C++, send email to cujqa@mfi.com, use subject line: Questions and Answers, or write to Bobby Schmidt, C/C++ Users Journal, 1601 W. 23rd St., Ste. 200, Lawrence, KS 66046.
I lied.
Okay, perhaps "lied" is too strong. Let's just say I overcommitted. When I started this column, I thought I'd be able to answer each question reasonably quickly, even if that answer were brief or vague. Unfortunately, I both underestimated the number and complexity of questions I'd get, and overestimated my available time to produce answers.
Given this reality, here's a more credible charter:
- If you need an answer immediately, you're better off asking a peer or teacher, or trying your luck on Usenet.
- If I decide to use your question in print, I'll let you know, but I won't promise when the answer will show up. (For editorial reasons, I sometimes delay publishing answers a month or two.)
- Once I've written up an answer even if that answer won't show up in print for a while I'll send you a draft.
- Unless/until you hear from me, don't assume I'll ever answer your question. I'd love to answer every one of them, truly, but I simply don't have time. (Of course, if CUJ paid me the Big Bucks to play help desk...)
Finally, please remember that I'm constraining topics to Standard C and C++. I keep getting questions tied to specific platforms, usually some Windows variant. (I assume this is testament to both the ubiquity and the difficulty of Windows programming.) I recommend that you forward such questions to a Windows-specific publication or newsgroup, or give Microsoft's Knowledge Base a whirl.
Func-y Cold Medina
Q
Dear Sir,
I'm a Ph.D student of The Mechanical Engineering Department in the University of North Carolina at Charlotte.
I want to call a member function that has another member function as a parameter. My file (included here) compiles with errors. Can you help me out with this problem?
Thank you very much for your time. Son Phan
A
(Diligent Readers will recall that, in my final "Learning C/C++urve," I promised to bring up member functions in an early Q&A installment. I now make good on that promise.)
I've reduced your original example to
class heat { public: float sr(float); float func(float (*fx)(float), float k) { return k + fx(3.0); } }; int main() { heat obj; obj.func(sr, 5.1); // error here }What you want is to pass the member function heat::sr as an argument to heat::func. Unfortunately, as you discovered, your original code doesn't work. The compiler complains that sr is not defined.
The fundamental problem is that you are trying to pass an instanced member function as a real function pointer. I've sketched out several solutions, in order of increasing complexity. See Figure 1.
Each subsequent numbered solution is really a variation of the one that came before, viz:
1. Since you aren't referencing any per-instance data, you can make func and sr static. (I'm calling the static versions func1 and sr1 here.) This yields the shortest and fastest code.
2. Similar to #1, except member functions are instanced (not static). Adds the slight overhead of passing an implied this pointer.
3. Same as #2, except instead of calling this.sr from within func, you call the function pointer parameter fx. The actual argument passed into fx must be either a static member function or a non-member function.
4. Same as #3, except you are now passing into fx a pointer to a non-static member function sr. The called func assumes you want to reference the current object (*this) to get the proper instance of sr.
5. Same as #4, except func doesn't assume that you want to dereference this. Instead you pass in the instance of heat containing the sr you actually want to call.
I'm guessing solution #4 is what you were trying to create originally, although I'd promote solution #1 if you can live within its constraints.
You Take the HIWORD...
Q
Bobby Schmidt,
I never miss your column in the C/C++ Users Journal. You have provided some very good insight into many things. Now I hope you can shed some light on the following.
The Microsoft Windows API contains a macro HIWORD, which is defined as
#define HIWORD(x) \ ((WORD) \ (((DWORD) (x) >> 16) & 0xFFFF))I think I understand that the macro returns a 16-bit int which is the 16 high bits (MSBs) of a 32-bit int by shifting the 16 high bits to the 16 low bits. If I do the same thing with Borland C++ like this:
int i = 583; // 32 bit int short hbyte; // 16 bit int hbyte = (i >> 16);I get the exact same results as when I do:
hbyte = (number >> 16) & 0xFFFF;So why does the Windows API also AND the shifted 16 bits with all ones? Any insight would help me understand it. Tom Parke
A
At first this looks like one of those Windows-specific questions I swore I wouldn't answer. But this is really a generic question about bit shifting and type casting.
Let's decompose HIWORD to see what's really going on:
1. The argument x is cast to a 32-bit unsigned integer (DWORD).
2. That 32-bit unsigned integer is shifted right 16 bits.
3. The post-shift result, still a 32-bit unsigned integer, is bit-anded with 0xFFFF. This has the effect of clearing the high 16 bits.
4. The masked result is cast (chopped) to a 16-bit unsigned integer (WORD).
You are alleging that step #3 is redundant, implying that the shifted value from step #2 fills in all 16 high bits with zeroes. Is this implication valid?
As usual, we appeal to the Standards for help. Fortunately, the Standards for C89, C9X, and C++ all say about the same thing:
The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type ... the value of the result is the integral part of the quotient of E1 divided by the quantity 2 raised to the power E2.
Since E2 in this case is 16, and 2 raised to the power of 16 is 65,536 decimal or 10000 hex, our right shift is effectively
(DWORD) x / (DWORD) 0x10000If I'm reading the Standards right, such unsigned integer division suffers no overflow, and the result is always non-negative. This means the sign bit and unused high bits are zeroes. But how many zeroes?
The largest possible value of this division is the largest DWORD divided by 0x10000, or
(DWORD) 0xFFFFFFFF / (DWORD) 0x10000which results in an unsigned integer quotient of
(DWORD) 0xFFFFThis value and all others below it will safely fit into a 16-bit WORD. Therefore no significant bits are left in the chopped-off high WORD, which contains all zero bits.
Conclusion: step #3 indeed appears to be redundant.
If the original argument x were not cast to a DWORD, I could understand the need for masking. For example, if x were signed and negative, the result of right shifting would be implementation defined, with possibly ones left in the (unmasked) high bits [1].
I'm guessing the masking is either protection against implementations that don't do unsigned casting and shifting right, or in-code documentation making the intent more apparent.
The Name of the Game
Q
Hello Mr. Schmidt,
I was just reading your article (magazines move slowly through our development and technical support divisions), and was curious about your Underscores article.
Specifically, what kinds of symptoms would using ac__me and _acme display? Would you just see compiler warnings if our implementation of certain names conflicted with the compiler's implementation?
We have numerous functions declared as _FunctionName, and some of the resource identifiers are defined as IDC__RESOURCENAME. Thank you. Dustin Friel
A
(Dustin is referring to my discussion of reserved identifiers in the October 1998 issue of CUJ.)
All the Standards say is that identifiers following certain patterns are reserved for the translator implementation. The Standards don't say what happens if you use those names. While I suppose your translator could specifically diagnose all your uses of such reserved names, I'm guessing that the translator is more likely to treat such name collisions as it does any others.
For example, if your translator declares function _Abba in a header, and you define another function _Abba with a different signature or in a different scope, you'll probably never become aware of the "collision." But if the translator's _Abba is a macro, your _Abba will be masked, and you'll probably get translation-time errors.
So really, the results you'll get are no different than for any other name collision. The snag is that the implementation may use any of these reserved names in any way it sees fit. That your code works today is no guarantee it will work tomorrow on a new version of your implementation.
Your safest path is to avoid such names completely, following the guidelines from either my column or the Standards. If you can't or won't do that, hedge your bets. My experience suggests that:
- The translator-provided header files often define macros and global types starting with __ or _A, where A is any upper-case letter. These headers may contain identifiers with embedded __ like your IDC__RESOURCENAME, but I find this to be less common.
- These headers often also define global objects or functions starting with _a, where a is any lower-case letter. Again, embedded __ is unusual, although leading or trailing __ is common.
- Your translator may support extra keywords starting with underscores, a practice for which Microsoft is notorious [2]. The list of such keywords should be documented, so you can avoid them. Even if you accidentally use one, the translator should catch the conflict, unless you use the name as a macro.
- The compiler may embed hidden references to run-time support functions that use reserved names. (Floating-point operations often result in such library calls.) If you accidentally use these names, the compiler may not complain but the linker could get confused.
Remember, these are just heuristics based on long experience they are not guarantees. If at all practical, I suggest you either use non-reserved identifiers, or move those identifiers out of global scope. At the very least, don't use such names as macros; that way, the translator has a better chance of catching any conflicts.
Mars and Venus
Q
Dear Bobby Schmidt,
I'm sure you are busy and may not have the time or inclination to answer this but, in case you do, here is my problem.
I was reading and am referring to your article in the December issue of C/C++ Users Journal. The section that interests me most is sub-titled Russian Dolls and refers to Paul Kerslake's question. I don't understand why the overloaded operator-> works as it does.
It appears (to me) that, when used in Test2, operator-> should return an object of type Test1 and not the dereferenced Test1. It would appear that the overloaded operator-> does something. I guess it returns an object of Test1 and then does a factory-built dereference which continues the cascade.
How can I explain this to my wife? Is this overloaded operator somehow different then others or are the others somehow different than my current perception.
And by the way, I enjoy your column.
Sincerely vjv
A
Don't feel bad about your confusion. When I first learned user-defined operators, I found operator-> to be about the most confusing. Plus, as another reader suggested, I probably should have been more clear in my original explanation. (At least we can't define our own operator. to add even more confusion.)
As a general rule, if object x supports a user-defined operator->, the expression
x->mis really
(x.operator->())->mNotice how the single token -> in the original expression actually maps to two instances of ->:
- the call to operator-> following x
- the -> just before m
If (x.operator->()) yields a real pointer, then the -> before m is a real point-to operator, and the "cascade" halts. But if (x.operator->()) yields an object supporting operator->, the recursive pattern continues. In the original example, the recursion went five levels before finally halting on a real pointer.
So in the end, things are as you suggest: the presence of operator-> "does something," which may include "a factory-built dereference." If you still aren't convinced, I encourage you to play around with it some more, especially by tracing within a debugger.
By the way, if this is the worst thing you ever have to explain to your wife, count your blessings.
Every Rose City Has Its Thorn
Q
My abuse at the words of your December 1998 CUJ column reminded me that you have too much free time or too little stuff to write about probably both. But Bobby, Bobby, Bobby, must you keep inserting not-quite-truths, or are you just seeing if Herb Sutter and I are paying attention?
Consider your remark that "Unfortunately, there's no way to say the equivalent of unusing namespace std;." Well, unfortunate that would be were it completely true, but it just so happens that your statement here is only mostly true [A].
A using declaration or directive remains in force until the end of the scope in which it occurs. Because using declarations/directives are typically employed at file scope, they usually remain in force until the end of the file containing them. In that case, you're right, no "unusing" is available.
But using declarations and directives can be sensibly employed within blocks, generally including classes, functions, and namespaces [B]. In that case, you spell "unusing" like this: }.
Of course, perhaps you meant to limit your comments to MSVC and you simply forgot to mention what the Standard says. If so, never mind :-)
Notes
[A] The Princess Bride, allusion to.
[B] The word "generally" is important here. Using declarations can be employed at class scope, but using directives cannot be. Details are available in sections 7.3.3 and 7.3.4 of the thrilling new standard for C++ [C].
[C] At least those are the section numbers in the FDIS. I don't have a copy of the final standard yet.
Have a nice day. Scott Meyers
A
So much for garlic.
Okay yes, I admit it, I made an unstated assumption in my response. Since the original question apparently involved a file-scope reference to vector, my answer also assumed file scope. In that context, the using directive would stay in effect through the end of the translation unit.
I didn't mean to imply that you could never turn off a using directive only that, in this particular example, you couldn't turn it off. So as usual, we are both right in our own ways.
And you can rest easy: the section numbers for using declarations and using directives are the same in the final Standard as in the FDIS.
Inspector Detector
Q
While doing a code inspection, I found code similar to this:
char string1[6]; char string2[6]; string1 = string2;When I brought it up to the author, we discovered the statements were working as he intended, copying the contents of string2 to string1. This behavior works in GNU g++, but not in CodeWarrior (latest version) or Solaris SPARCompiler 4.2 (older version, Oct 96).
In further review of the problem, we wrote and tested the attached program [not shown], which also works as it would appear, with integer arrays also! It, however, does not work in gcc, nor does it allow arrays of different sizes to be assigned to each other, giving an appropriate error message.
It appears that the GNU library is treating the core language array ([]) as a type of class, and of course does a default member-wise copy of the contents. This is either a bug, a GNU extension, or part of the new Standard, but we cannot determine which explanation applies.
Could you please explain what is the correct interpretation of the C++ Standard? Ron Hamann
A
Your code samples shouldn't compile, in either C or C++. Real arrays are not modifiable lvalues, meaning (among other things) that you can't assign to them. You can of course initialize them:
char string2 = "abcde";but as seasoned readers know, initialization is not the same as assignment (especially in C++).
If your original code works, it represents either a bug in your translator or an extension that ought to be documented. I just took a glance through all the relevant Standards, and none of them appear to allow this behavior, even for arrays of the same type.
Erratica
Last month I ran a "five liner" from Diligent Reader Andrei Alexandrescu. As is my convention, I let Andrei know I'd be publishing his letter. After I'd submitted my column, I got this reply from him:
I've made some "progress" up to the point that you'd ruin my reputation by publishing the to_string function :o). The correct version of it is:
template <typename T> std::string to_string(const T &a) { std::ostringstream Stream; Stream << a; return Stream.str(); }Compared to the initial version, please remark on the usage of ostringstream instead of the deprecated ostrstream, with the associated benefits.
As to your comments, the first problem is mitigated, the second remains, and maybe one can get rid of the third by having a static ostringstream that is cleared upon the entry of to_string. Unfortunately, here I have absolutely no means to write and check any code.
Given the lag time between the moment I send you messages and the moment you answer them, it seems like you won't read this until you will already have published the "wrong" code and have me lynched by the C++ community.
(The three "comments" to which Andrei refers are included with my answer last month.) As Andrei suggests, this new solution is indeed superior. I especially like the abandonment of magic-sized buffers, thanks to ostringstream using a self-adjusting string object instead of a char array.
Notes
[1] I say "possibly" because the behavior can be anything. In practice, every implementation I've seen propagates either all zeroes or all ones into the "empty" high-order bit slots.
[2] But wait, it gets better. Thanks to my new gig at Microsoft, I've discovered that MSVC supports the custom preprocessor directive #import. Not a #pragma like you might expect, but a whole new directive.
Bobby Schmidt is a freelance writer, teacher, consultant, and programmer. He is also an alumnus of Microsoft, a speaker at the Software Development and Embedded Systems Conferences, and an original associate'' of (Dan) Saks & Associates. In other career incarnations, Bobby has been a pool hall operator, radio DJ, private investigator, and astronomer. You may summon him on the Internet via rschmidt@netcom.com.