From: Joseph M. Newcomer on 6 Apr 2010 21:36 See below... On Tue, 6 Apr 2010 12:38:07 -0700 (PDT), "John H." <oldman_fromthec(a)yahoo.com> wrote: >Pete Delgado wrote: >> The above code should crash. You attempt to copy a 14 element (13 characters >> + terminating null) sequence into a 10 element array. Since you are using a >> string literal anyway, why not just set the pszTitle member to the literal >> rather than attempting to copy it into a class member? > >For those looking for a little safer example: > >class CMyPropertyPage : public CPropertyPage >{ > public: > CMyPropertyPage(UINT nIDTemplate, CString csTitle) : **** That would be const CString & csTitle you really have to embrace "const" as a concept. I just rewrote a library, and one of the things I did was add 'const' to all parameters where appropriate (most of them). We uncovered several bugs in the library when I did this! THe use of const is not only good documentation as to what is going on, it often results in vastly more efficient code. For eample, const CString does not require making a copy of the CString contents. joe ***** **** > CPropertyPage(nIDTemplate), > m_csTitle(csTitle) > { > m_psp.pszTitle = m_csTitle; > m_psp.dwFlags |= PSP_USETITLE; > } > private: > CString const m_csTitle; >}; > >So then you can doing something like the following (assuming you have >a dialog template resource IDD_PROPPAGE_SMALL and a string table >resource with an entry IDS_MY_TITLE): > >#include "resource.h" >int main() >{ > CPropertySheet sheet; > CMyPropertyPage page(IDD_PROPPAGE_SMALL, >CString((LPCTSTR)IDS_MY_TITLE)); > sheet.AddPage(&page); > sheet.SetWizardMode(); > sheet.DoModal(); > return 0; >} Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: John H. on 7 Apr 2010 12:14 Joseph M. Newcomer wrote: > That would be > const CString & csTitle > you really have to embrace "const" as a concept. Yes I did think about this some. I read various arguments on Usenet about the pros and cons of passing small strings by reference vs. by value. Arguments include "reference passing means you can avoid a copy", counter arguments I have heard are "this is probably premature optimization" "the optimizing compiler will do this for you anyways" "you risk introducing aliasing problems". Also from an interface perspective, it can leave the caller wondering, "does the created object expect this string to remain valid throughout its life?" This of course can be addressed through documentation, but I have seen systems where things are not documented very well. I guess for this example I landed on the "premature optimization" side (as a title is likely to be no more than a couple dozen characters) coupled with "keep the interface simple". > I just rewrote a library, and one of the > things I did was add 'const' to all parameters where appropriate (most of them). We > uncovered several bugs in the library when I did this! > > THe use of const is not only good documentation as to what is going on, it often results > in vastly more efficient code. For eample, const CString does not require making a copy > of the CString contents. There was a recent thread on comp.lang.c++ on using const for pass-by- value parameters. There wasn't much of a consensus on this either, I guess it comes down to a matter of taste. The pros of having pass-by- value be const is that it can avoid unintended changes to the value (bugs) and may make following logic and maintenance easier, and allow some optimizations. Some cons pointed out were that having const for the argument is just noise and providing unnecessary detail to the caller and that it may make following logic and maintenance harder, and may disallow some optimizations. To help with the "unnecessary detail" problem one could omit const in the function declaration but include it in the function definition. Do you have any thoughts on this stuff? Also now with VC++ 10 supporting rvalue references, that will be another option to consider. Although it's probably not too relevant in the particular case because, as far as I know, CString does not support "move" semantics. I guess I could have implemented the page's title with a string class that does support move semantics, in which case a small optimization might be obtained with a constructor that uses the rvalue references. ---------------------------- John H wrote: > #include "resource.h" > int main() > { > CPropertySheet sheet; > CMyPropertyPage page(IDD_PROPPAGE_SMALL, CString((LPCTSTR)IDS_MY_TITLE)); > sheet.AddPage(&page); > sheet.SetWizardMode(); > sheet.DoModal(); > return 0; > } I forgot the "AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0);" at the start of the program.
From: Joseph M. Newcomer on 7 Apr 2010 16:01 See below.. On Wed, 7 Apr 2010 09:14:54 -0700 (PDT), "John H." <oldman_fromthec(a)yahoo.com> wrote: >Joseph M. Newcomer wrote: >> That would be >> const CString & csTitle >> you really have to embrace "const" as a concept. > >Yes I did think about this some. I read various arguments on Usenet >about the pros and cons of passing small strings by reference vs. by >value. Arguments include "reference passing means you can avoid a >copy", counter arguments I have heard are "this is probably premature >optimization" "the optimizing compiler will do this for you anyways" **** If you think of it as optimization, it is probably the wrong reason. If you think of it as correctly capturing your intent (the string IS a constant!) then it is mandatory. I think of it as capturing the specification correctly. The optimization is a side benefit. ***** >"you risk introducing aliasing problems". **** Not with CString. **** >Also from an interface >perspective, it can leave the caller wondering, "does the created >object expect this string to remain valid throughout its life?" This >of course can be addressed through documentation, but I have seen >systems where things are not documented very well. I guess for this >example I landed on the "premature optimization" side (as a title is >likely to be no more than a couple dozen characters) coupled with >"keep the interface simple". **** No, an interface that is simple but is not correct is not a valid inteface. The use of 'const' captures the purpose of the interface. Once you have the const, it really doesn't matter if you pass by reference or value; you have said somehting correct about your intentions at the interface. ***** > >> I just rewrote a library, and one of the >> things I did was add 'const' to all parameters where appropriate (most of them). We >> uncovered several bugs in the library when I did this! >> >> THe use of const is not only good documentation as to what is going on, it often results >> in vastly more efficient code. For eample, const CString does not require making a copy >> of the CString contents. > >There was a recent thread on comp.lang.c++ on using const for pass-by- >value parameters. There wasn't much of a consensus on this either, I >guess it comes down to a matter of taste. The pros of having pass-by- >value be const is that it can avoid unintended changes to the value >(bugs) and may make following logic and maintenance easier, and allow >some optimizations. Some cons pointed out were that having const for >the argument is just noise and providing unnecessary detail to the >caller and that it may make following logic and maintenance harder, >and may disallow some optimizations. To help with the "unnecessary >detail" problem one could omit const in the function declaration but >include it in the function definition. ***** We found several errors in the library. Since there is *only* pass-by-value in C and C++ ("reference" or "pointer" are VALUES that are passed) I don't buy the arguments about using it for pass-by-value. I believe based on my experience that the use of const results in cleaner code that is more likely to be correct. And if you use Best Practice and use the public header file in your own compilation then the compiler will complain about mismatched parameter types between the declaration and the definition. ***** > >Do you have any thoughts on this stuff? > >Also now with VC++ 10 supporting rvalue references, that will be >another option to consider. Although it's probably not too relevant >in the particular case because, as far as I know, CString does not >support "move" semantics. I guess I could have implemented the page's >title with a string class that does support move semantics, in which >case a small optimization might be obtained with a constructor that >uses the rvalue references. **** rvalue references are really cool, mostly because of what you can do to optimize pass-by-value. Yes, this is an optimization, but it is an important one for performance reasons. Most ot the std:: library is now rewritten to handle rvalue references, and the performance improvements are said to be considerable. It avoids unnecessary copy operations. CString actually does not support move semantics, but doesn't need to, because it manages a reference count. If you try to do something to a string whose refcount is 1, it can use move semantics because it has the one-and-only copy in hand. As you pass a CString around by reference, the refcount is update and copy semantics will come into play when required. It uses copy-on-write semantics (COW), and there are long threads about this in this NG and elsewhere. The real error was using a character array; that is always known to be bad practice in such cases (and was a glaring bug in the example given!). In fact, the use of strcpy, sprintf, and strcat, among other "dangerous" functions, is now a firable offense in many companies because it is well-known that thee operations increase the attack surface of applications. THe other big one is failing to check integer overflow, but that is harder to enforce. joe > >---------------------------- > >John H wrote: >> #include "resource.h" >> int main() >> { >> CPropertySheet sheet; >> CMyPropertyPage page(IDD_PROPPAGE_SMALL, CString((LPCTSTR)IDS_MY_TITLE)); >> sheet.AddPage(&page); >> sheet.SetWizardMode(); >> sheet.DoModal(); >> return 0; >> } > >I forgot the "AfxWinInit(::GetModuleHandle(NULL), >NULL, ::GetCommandLine(), 0);" at the start of the program. Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: John H. on 7 Apr 2010 18:35 Joseph M. Newcomer wrote: > John H. wrote: > >passing small strings by reference vs. by > >value. Arguments include > > "this is probably premature > >optimization" "the optimizing compiler will do this for you anyways" > **** > If you think of it as optimization, it is probably the wrong reason. Agreed. > >Also from an interface perspective, I guess for this > >example I landed on the "keep the interface simple". > **** > No, an interface that is simple but is not correct is not a valid inteface. The use of > 'const' captures the purpose of the interface. Once you have the const, it really doesn't > matter if you pass by reference or value; you have said somehting correct about your > intentions at the interface. I can think of interfaces that have a simple form (non-const parameters) and a more complex form (const correct parameters), and both produce the same desired execution. Am I interpreting you right to by saying that the one that captures "purpose" better is more valid? > Since there is *only* pass-by-value in C and C++ > ("reference" or "pointer" are VALUES that are passed) I was trying to use pass-by-value to mean situations where the value that is copied is not a reference or pointer. For references or pointers I will call that pass-by-reference. > I don't buy the arguments about > using it for pass-by-value. I believe based on my experience that the use of const > results in cleaner code that is more likely to be correct. I am confused. Are you saying that you don't agree with the arguments that I mentioned for const with pass-by-value, but that there are other reasons (that you mentioned) to use const with pass-by-value? Or are you saying that const is only useful with pass-by-reference and not at all with pass-by-value? > And if you use Best Practice and use the public header file in your own compilation then > the compiler will complain about mismatched parameter types between the declaration and > the definition. I don't understand. Is "Best Practice" an option in the compiler or an additional development tool? As far a I know, a conforming compiler and at least a few versions of the VC compiler will know to match the following declaration and definition: // In the foo.h: void print_x(int num_times); // In the foo.cpp: #include "foo.h" #include <iostream> void print_x(int const num_times) { for(int i=0; i<num_times; ++i) { std::cout << 'x'; } } This is the idea I was trying to say when I said put const in the definition but not the declaration. Here, from the caller's point of view, it seems like an irrelevant detail on whether num_times is a const parameter or not. The parameter is copied so whatever they passed cannot be modified anyways. The function should produce the same result either way. The fact that the implementation doesn't modify num_times is really something only of interest to the implementation. A implementation could in fact modify num_times and still be correct, something like the following (untested): void print_x(int num_times) { for(; num_times > 0; --num_times) { std::cout << 'x'; } } I think that using const in pass-by-value (as in pass a copy) parameters has some merits. However looking at most existing code, this seems like a rare practice. > CString ... manages > a reference count. If you try to do something to a string whose refcount is 1, it can use > move semantics because it has the one-and-only copy in hand. As you pass a CString around > by reference, the refcount is update and copy semantics will come into play when required. > It uses copy-on-write semantics (COW)... Nice to know. > The real error was using a character array; that is always known to be bad practice in > such cases (and was a glaring bug in the example given!). In fact, the use of strcpy, > sprintf... As this is an MFC forum, I agree with you, and it was a bad example. Off-topic, I have been doing work lately in an embedded platform where we don't have CString or std::string or smart pointers, and I got a little too compfy with these less robust tools.
From: Joseph M. Newcomer on 7 Apr 2010 22:43 See below... On Wed, 7 Apr 2010 15:35:21 -0700 (PDT), "John H." <oldman_fromthec(a)yahoo.com> wrote: >Joseph M. Newcomer wrote: >> John H. wrote: >> >passing small strings by reference vs. by >> >value. Arguments include >> > "this is probably premature >> >optimization" "the optimizing compiler will do this for you anyways" >> **** >> If you think of it as optimization, it is probably the wrong reason. > >Agreed. > >> >Also from an interface perspective, I guess for this >> >example I landed on the "keep the interface simple". >> **** >> No, an interface that is simple but is not correct is not a valid inteface. The use of >> 'const' captures the purpose of the interface. Once you have the const, it really doesn't >> matter if you pass by reference or value; you have said somehting correct about your >> intentions at the interface. > >I can think of interfaces that have a simple form (non-const >parameters) and a more complex form (const correct parameters), and >both produce the same desired execution. Am I interpreting you right >to by saying that the one that captures "purpose" better is more >valid? **** Yes. I also annotated the library with the __in, __inout, and so on annotations, which are meta-notations which when using /ANALYZE uncovered at least six fundamental errors in the implementation. They really were errors. **** > >> Since there is *only* pass-by-value in C and C++ >> ("reference" or "pointer" are VALUES that are passed) > >I was trying to use pass-by-value to mean situations where the value >that is copied is not a reference or pointer. For references or >pointers I will call that pass-by-reference. **** No, officially, there is no such concept in C or C++. The "pointer" or "reference" (C++) is a concept that is implemented via pass-pointer-by-value. As an old compiler writer, we had to really care about the differences like this.And as someone who was trying to do optimizing compilers and avoiding aliasing problems, 'const' buys a LOT of power for the optimizer. A const declaration of a pass-by-value parameter actually allows an optimization knowns as "value propagation" to occur without having to do complex flow-graph computations. So if I have a const parameter, and the parameter ends up in ECX, then I know that I can just get it by using the ECX value anywhere. It also says that I do not expect this parameter to be modified within the function, that is, it is really a "parameter" and not some funny kind of temporary variable. **** > >> I don't buy the arguments about >> using it for pass-by-value. I believe based on my experience that the use of const >> results in cleaner code that is more likely to be correct. > >I am confused. Are you saying that you don't agree with the arguments >that I mentioned for const with pass-by-value, but that there are >other reasons (that you mentioned) to use const with pass-by-value? >Or are you saying that const is only useful with pass-by-reference and >not at all with pass-by-value? **** I have seen several interesting errors caused by someone modifying the parameter in the code, then someone else later using the parameter as if it was the one passed in from the caller. So under maintenance, even a "const int" parameter makes more sense, because it discourages reuse of the parameter name as if it were a local variable. In fact, I once made a substantial amount of money rewriting a piece of code where the programmer had an EMBEDDED assignment to the parameter variable in an expression, and the consequences were fatal. I discovered this in the rewrite because I wrote 'const int' for the parameter and the compiler complained halfway through the long, complex computation that someone had assigned to a const variable. It discourages sloppy programming, catches errors under maintenance, and can result in smaller, faster code. What's not to like? **** > >> And if you use Best Practice and use the public header file in your own compilation then >> the compiler will complain about mismatched parameter types between the declaration and >> the definition. > >I don't understand. Is "Best Practice" an option in the compiler or >an additional development tool? As far a I know, a conforming >compiler and at least a few versions of the VC compiler will know to >match the following declaration and definition: **** Best Practice is a technique that programmers use to reduce the possibility of errors. **** > >// In the foo.h: >void print_x(int num_times); >// In the foo.cpp: >#include "foo.h" >#include <iostream> >void print_x(int const num_times) **** This normally results in an error from the compiler because the forward reference in the header file conflicts with the definition. That would be 'const int' not 'int const'. **** >{ > for(int i=0; i<num_times; ++i) > { > std::cout << 'x'; > } >} > >This is the idea I was trying to say when I said put const in the >definition but not the declaration. **** I knew that, and that's why I said it wouldn't work. **** >Here, from the caller's point of >view, it seems like an irrelevant detail on whether num_times is a >const parameter or not. The parameter is copied so whatever they >passed cannot be modified anyways. The function should produce the >same result either way. The fact that the implementation doesn't >modify num_times is really something only of interest to the >implementation. A implementation could in fact modify num_times and >still be correct, something like the following (untested): **** Which would be fine if that's the way the language and compilers worked, but that isn't how they work. **** > >void print_x(int num_times) >{ > for(; num_times > 0; --num_times) > { > std::cout << 'x'; > } >} > >I think that using const in pass-by-value (as in pass a copy) >parameters has some merits. However looking at most existing code, >this seems like a rare practice. **** This is the kind of code I believe is properly discouraged by using the const in the parameter. In a large function, someone is going to assume that num_times at some point has the same value passed in, and this assumption is violated by the code that modifies the parameter. **** > >> CString ... manages >> a reference count. If you try to do something to a string whose refcount is 1, it can use >> move semantics because it has the one-and-only copy in hand. As you pass a CString around >> by reference, the refcount is update and copy semantics will come into play when required. >> It uses copy-on-write semantics (COW)... > >Nice to know. > >> The real error was using a character array; that is always known to be bad practice in >> such cases (and was a glaring bug in the example given!). In fact, the use of strcpy, >> sprintf... > >As this is an MFC forum, I agree with you, and it was a bad example. >Off-topic, I have been doing work lately in an embedded platform where >we don't have CString or std::string or smart pointers, and I got a >little too compfy with these less robust tools. **** One of the fascinating phenomena of development history is that there is a large class of errors I NEVER make any longer, because I use CString, std::vector, CArray, and so on, all of which have behavior that is well-managed. So I have never had to worry about tracking down these errors that I used to be so good at finding. I have gotten real comfy with modern programming tools, and would tend to insist on them in any programming environment. joe **** Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 Prev: GetAsyncKeyState(VK_LBUTTON) true after DoModal() Next: DeleteFile shows as DeleteFileA |