|
|||
|
At our shop, programming guidelines strongly recommend initializing
variables at the point of declaration, and making them const whenever possible. Some examples are: const int min_hours = 6; const double max_pay = 400.0; etc. So there is great debate when reading and populating variables from an input stream. // Example of code frowned upon. Variable not initialized size_t x; input_stream >> x; // Example acceptable but inefficient code size_t x = 0; // initialized, but immediately over written input_stream >> x; // Possible code const size_t x = Read<size_t>( input_stream ); Is there any other way to declare and initialize variables from an input stream. Especially const variables. The above looks OK until you begin looking at the Read() template< typename T > T Read( std::istream &is ) { T x; // unitialized but unlikely to break during maintenance is >> x; return x; } The template both doesn't initialize x and is inefficient when reading in objects. Think std::string x = Read<std::string>(input_stream); or if you prefer the Stephen Dewherst recomendation std::string x( Read<std::string>(input_stream) ); In the end, the code should be extremely safe and extremely efficient ;^) (sorry for the puns X stream extraction ) [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
||||
|
||||
|
|
|
|||
|
mzdude wrote:
> // Example acceptable but inefficient code > size_t x = 0; // initialized, but immediately over written > input_stream >> x; The CPU time to zero initialise 'x' is negligible compared to the CPU time to read that integer from the string. I wouldn't warry about this. Also, don't forget that the >> operation may fail. > const size_t x = Read<size_t>( input_stream ); And what if the read operation fails? > Is there any other way to declare and initialize variables from an > input stream. Especially const variables. If its a const variable, read in a function and return the value. > template< typename T > > T Read( std::istream &is ) > { > T x; // unitialized but unlikely to break during maintenance The main problem I have with this is the "unlikely" bit. And again, I would warry about the CPU time to initialise a variable. > is >> x; > return x; > } > The template both doesn't initialize x and is inefficient when reading > in objects. This depends on whether your compiler does NRVO. -- Valentin Samko - http://www.valentinsamko.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
mzdude wrote:
> At our shop, programming guidelines strongly recommend initializing > variables at the point of declaration, and making them const whenever > possible. I hope those guidelines also mention that - objects with a proper constructor don't need external initialisation - the reason is that accessing an uninitalized value leads to a) all kinds of behaviour and b) is undefined behaviour by the standard. IOW, use of uninitialized values must be avoided, because it breaks code in unpredictable ways. > // Example of code frowned upon. Variable not initialized > size_t x; > input_stream >> x; > Inacceptable by my standards, you fail to check if reading succeeded. That might be done later in that example, but when I see this: > // Possible code > const size_t x = Read<size_t>( input_stream ); > [...] > > template< typename T > > T Read( std::istream &is ) > { > T x; // unitialized but unlikely to break during maintenance > is >> x; > return x; > } .... I see that it isn't done. Bad idea. Use this body for Read<T>() and you're much safer: T x; if(!(is>>x)) throw_exception(); return x; Looking at the coding guidelines, this also doesn't initialize x, but it is easy to see that either it is initialized by reading from a stream or it is unused because the function leaves via an exception. To me, this is enough justification to not apply that part of the coding guidelines here. Uli [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
A good programmer learns to make the right compromises, and this
attempt at absolute rigour suggests a misguided excess to me. It arbitrarily prioritises const-ness and assignment at definition independent of context. For example... > // Example of code frowned upon. Variable not initialized > size_t x; > input_stream >> x; ...._should_ be frowned upon, but because there's no evidence that the streaming operation is tested for success. In many programs, a clear expression of stream I/O error handling is a bigger concern than const-ness and assignment at definition. size_t x; if (input_stream >> x) ... else ...; Sure, non-localised error handling is possible after setting the stream to throw on error, which again changes the context and balance of priorities. Increasing the number of variables may tip the balance yet again: const size_t x = Read<size_t>( input_stream ); const size_t y = Read<size_t>( input_stream ); captures the I/O intent less succinctly than: size_t x, y; if (input_stream >> x >> y) ...; As for uninitialised variables: it's a matter of localisation. Is the following line (perhaps after a spacing line) local enough to be instinctively reviewed when checking the code? I'd argue "yes": if I see an uninitialised variable the first thing I do is scan the next couple lines to see where it's assigned. I'll be suspicious if it's not set there somewhere. Is the lack of const acceptable? I'd argue it's unfortunate, but sometimes a lesser evil. // Example acceptable but inefficient code size_t x = 0; // initialized, but immediately over written input_stream >> x; This is plain silly, as it implies that there's some point or significance in assigning the 0. In general, if a streaming input operation fails, the value being read into may still be changed. On the other hand, if unchanged and uninitialised, then at least there's a chance of tools like purify and valgrind detecting an eventual read from uninitialised memory. operator>> for inbuilt types is designed to stream into pre-existing objects, and operator>> for user-defined types should conform to expectation. My advice: - _consider_ (but don't jump at the opportunity to) provide a constructor from a stream for non-trivial user-defined types, where there's _significant_ cost in default construction (which is pretty rare, and even rarer if constructor and assignment operator happen to be inline and you're using optimisation) - use your Read<> template where it doesn't obfuscate I/O intent or error handling, - use plain old "input_stream >> " directly to uninitialised, non-const variables otherwise. Cheers, Tony [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
mzdude wrote:
> At our shop, programming guidelines strongly recommend initializing > variables at the point of declaration, and making them const whenever > possible. Some examples are: > > const int min_hours = 6; > const double max_pay = 400.0; > > etc. > > So there is great debate when reading and populating variables from an > input stream. > > // Example of code frowned upon. Variable not initialized > size_t x; > input_stream >> x; > > // Example acceptable but inefficient code > size_t x = 0; // initialized, but immediately over written > input_stream >> x; Alternately: size_t x(); input_stream >> x; > // Possible code > const size_t x = Read<size_t>( input_stream ); > > Is there any other way to declare and initialize variables from an > input stream. Especially const variables. > > The above looks OK until you begin looking at the Read() > > template< typename T > > T Read( std::istream &is ) > { > T x; // unitialized but unlikely to break during maintenance > is >> x; > return x; > } Again, it's unlikely that initialization x would measurably affect performance: template< typename T > T Read( std::istream &is ) { T x(); is >> x; return x; } Note that the () syntax (and even the ~x() syntax) is still legal when T is a POD type. > The template both doesn't initialize x and is inefficient when reading > in objects. Think > std::string x = Read<std::string>(input_stream); > or if you prefer the Stephen Dewherst recomendation > std::string x( Read<std::string>(input_stream) ); > > In the end, the code should be extremely safe and extremely efficient > ;^) (sorry for the puns X stream extraction ) Actually, because the Read() routine always returns the same variable it is eligible for the named Return Value Optimization (NRVO). With NRVO, Read() would be close to optimally efficient - more efficient than a version of Read() that passed in and returned the T object to hold the result, for instance. Greg [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
mzdude wrote: > At our shop, programming guidelines strongly recommend initializing > variables at the point of declaration, and making them const whenever > possible. Some examples are: > > const int min_hours = 6; > const double max_pay = 400.0; > > etc. > > So there is great debate when reading and populating variables from an > input stream. > > // Example of code frowned upon. Variable not initialized > size_t x; > input_stream >> x; Yes, I would consider frowning upon it to, for who's to say the data associated with input_stream is compatible with x, in which case the write to x would fail, leaving it in an uninitialised state. > > // Example acceptable but inefficient code > size_t x = 0; // initialized, but immediately over written > input_stream >> x; Wow, you not gaining much wrt efficiency. How do you know "input_stream >> x" did what it is supposed to - example: console > Please any a number: user > y <cr> App: float fUser; cin >> fUser; //Hmmm, what is fUser now? I do understand your problem though. You don't want to be inefficient and yet you want to remain safe... > > // Possible code > const size_t x = Read<size_t>( input_stream ); > > Is there any other way to declare and initialize variables from an > input stream. Especially const variables. > > The above looks OK until you begin looking at the Read() > > template< typename T > > T Read( std::istream &is ) > { > T x; // unitialized but unlikely to break during maintenance > is >> x; > return x; > } Yes, when calling Read you have to assume it does the right thing. How do we initialise x efficiently in Read? I've had a look at boost intialisers, and it seems that can be used for this case. Read then becomes: template< typename T > T Read( std::istream &is ) { boost::value_intialized<T> x; is >> x; return x; } > > The template both doesn't initialize x and is inefficient when reading > in objects. Think > std::string x = Read<std::string>(input_stream); > or if you prefer the Stephen Dewherst recomendation > std::string x( Read<std::string>(input_stream) ); Now you can be sure that Read does initialize x, and Dewherst's recomendation can be met :-). > > In the end, the code should be extremely safe and extremely efficient Yes, which it now becomes. > ;^) (sorry for the puns X stream extraction ) Regards, Werner [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Tony Delroy wrote:
> A good programmer learns to make the right compromises, and this > attempt at absolute rigour suggests a misguided excess to me. It > arbitrarily prioritises const-ness and assignment at definition > independent of context. For example... > > > // Example of code frowned upon. Variable not initialized > > size_t x; > > input_stream >> x; > > ..._should_ be frowned upon, but because there's no evidence that the > streaming operation is tested for success. In many programs, a clear > expression of stream I/O error handling is a bigger concern than > const-ness and assignment at definition. > Error handling was omitted for brevity. ... And yet so many programmers tend to not check library function call results. It is exactly that reason that I like to init variables on declaration. It avoids all kinds of hard to track bugs. I find that being consistent tends to help avoid whole classes of bugs. It isn't misguided rigour to develop good programming habits. This is one case where perhapse a good habit isn't enough. Or perhaps my habit needs to be modified. :^) [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
mzdude wrote:
> At our shop, programming guidelines strongly recommend > initializing variables at the point of declaration, and making > them const whenever possible. Some examples are: > const int min_hours = 6; > const double max_pay = 400.0; > etc. A good general rule. > So there is great debate when reading and populating variables > from an input stream. > // Example of code frowned upon. Variable not initialized > size_t x; > input_stream >> x; Nothing to frown upon. It's the exception to the rule. > // Example acceptable but inefficient code > size_t x = 0; // initialized, but immediately over written > input_stream >> x; I doubt that it makes a difference in actual run-time, so I don't think ou can talk about inefficient code. On the other hand, the initialization leads one to think that x can have the value 0, and that the value 0 is in some way significant. The code is thus misleading... unless some later code counts on x being zero if the input fails. (I don't like that sort of dependancy. I think that the standard guarantees that x is unchanged if the >> operator fails, but that wasn't the case in the classical iostream; in the classical iostream, the value of x was undefined in case of input failure.) > // Possible code > const size_t x = Read<size_t>( input_stream ); > Is there any other way to declare and initialize variables > from an input stream. Especially const variables. No. It's pretty easy to create a Read function like the above, but I don't see the point in it. > The above looks OK until you begin looking at the Read() > template< typename T > > T Read( std::istream &is ) > { > T x; // unitialized but unlikely to break during maintenance > is >> x; > return x; > } > The template both doesn't initialize x and is inefficient when > reading in objects. Think > std::string x = Read<std::string>(input_stream); > or if you prefer the Stephen Dewherst recomendation > std::string x( Read<std::string>(input_stream) ); I'm afraid I don't see the inefficiency. On the other hand, I don't see the problem with simply writing: int x ; input >> x ; The variable isn't anything that the compiler can treat as const (regardless of how it is initialized), and since the scope of local variables is so small (a couple of lines, at most), the human reader should have no problem keeping it straight. > In the end, the code should be extremely safe and extremely > efficient ;^) (sorry for the puns X stream extraction ) In the end, the code should be safe enough and efficient enough, and extremely simply to understand and to modify. Going into contortions to avoid violating a rule is missing the point of the rule, which should be to make the code simpler to understand and to modify. The reason for the rule of initializing in the definition is, of course, that it makes it much easier to see where and with what the variable is initialized, and to ensure that a later modification doesn't accidentally use the variable before it is initialized. In this regard, the advantage is the physical proximity, not the fact that it is a single statement, and there is not much difference between: int i = 1 ; and int i ; i = 1 ; except that it is much easier to write the first. When, however, there is no way to write the first (e.g. when the initialization is a >> operator), there's no problem with the second. The important point is that there is nothing that occurs between the two. -- James Kanze mailto: james.kanze@free.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34 [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
I noticed two mistakes in my earlier post. Lest anyone become confused,
here are the corrections: > > Alternately: > > size_t x(); > input_stream >> x; I should have typed: size_t x = size_t(); input_stream >> x; Similarly, the Read function > > template< typename T > > T Read( std::istream &is ) > { > T x(); > is >> x; > return x; > } > should have been: template< typename T > T Read( std::istream &is ) { T x = T(); is >> x; return x; } Greg [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Tony Delroy wrote:
> // Example acceptable but inefficient code > size_t x = 0; // initialized, but immediately over written > input_stream >> x; > > This is plain silly, as it implies that there's some point or > significance in assigning the 0. In general, if a streaming input > operation fails, the value being read into may still be changed. This is not true for arithmetic types. It's not self-evident from the standard, but it's a consequence of the following statement of the num_get<> facet (22.2.2.1.2/1): "Effects: Reads characters from in, interpreting them according to str.flags(), use_facet<ctype<charT> >(loc), and use_facet<numpunct<charT> >(loc), where loc is str.getloc(). If an error occurs, val is unchanged; otherwise it is set to the resulting value." For std::string it's a bit different (21.3.7.9/1): "Begins by constructing a sentry object k as if k were constructed by typename basic_istream<charT,traits>::sentry k(is). If bool(k) is true, it calls str.erase() and then extracts characters from is and appends them to str as if by calling str.append(1,c). [...]" If failure is detected by the sentry object, the target string remains unchanged. However, if failure occurs after checking the sentry, the string might indeed be modified. Notice that this kind of failure may occur only if someone (for example underflow()) throws an exception, with the end result of setting badbit but not failbit. For char* the situation is similar to std::string. > operator>> for inbuilt types is designed to stream into pre-existing > objects, and operator>> for user-defined types should conform to > expectation. As shown above, apart from a very exceptional situation regarding strings, operator>> is designed to leave the object unchanged on failure, so operator>> for user-defined types should conform to *that* expectation, IMHO. That means operator>> for UDTs should *not* stream into pre-existing objects, because it would potentially leave an object in an inconsistent state. Such inauspicious situation should be avoided at all costs, even if doing so requires making a copy. HTH, Ganesh [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Alberto Ganesh Barbati wrote:
> Tony Delroy wrote: > > // Example acceptable but inefficient code > > size_t x = 0; // initialized, but immediately over written > > input_stream >> x; > > This is plain silly, as it implies that there's some point > > or significance in assigning the 0. In general, if a > > streaming input operation fails, the value being read into > > may still be changed. > This is not true for arithmetic types. It's not self-evident > from the standard, but it's a consequence of the following > statement of the num_get<> facet (22.2.2.1.2/1): > "Effects: Reads characters from in, interpreting them > according to str.flags(), use_facet<ctype<charT> >(loc), and > use_facet<numpunct<charT> >(loc), where loc is str.getloc(). > If an error occurs, val is unchanged; otherwise it is set to > the resulting value." Note that this guarantee was not present in pre-standard implementations (although they may have worked like this). Which means that old fogeys like myself have gotten into the habit of not counting on it. It's also worth asking your self what happens if at some later date, you replace the int with a BigInteger class. Can you count on the implementation of >> for BigInteger not modifying the value in case of error? > For std::string it's a bit different (21.3.7.9/1): > "Begins by constructing a sentry object k as if k were > constructed by typename basic_istream<charT,traits>::sentry > k(is). If bool(k) is true, it calls str.erase() and then > extracts characters from is and appends them to str as if by > calling str.append(1,c). [...]" > If failure is detected by the sentry object, the target string > remains unchanged. However, if failure occurs after checking > the sentry, the string might indeed be modified. Notice that > this kind of failure may occur only if someone (for example > underflow()) throws an exception, with the end result of > setting badbit but not failbit. > For char* the situation is similar to std::string. > > operator>> for inbuilt types is designed to stream into > > pre-existing objects, and operator>> for user-defined types > > should conform to expectation. > As shown above, apart from a very exceptional situation > regarding strings, operator>> is designed to leave the object > unchanged on failure, so operator>> for user-defined types > should conform to *that* expectation, IMHO. That means > operator>> for UDTs should *not* stream into pre-existing > objects, because it would potentially leave an object in an > inconsistent state. Such inauspicious situation should be > avoided at all costs, even if doing so requires making a copy. I agree with the "should", and that's the way I write my >>. Given that historically, this wasn't a requirement, and that the fact that it now a requirement doesn't seem to be widely known, I'm not sure that I can count on all code actually being written the way it should be. I might add that I've never had any difficulty writing code which didn't count on it. Most of the time, if the input didn't succeed, I won't use the value anyway, and in the rare cases where I might, it's pretty trivial to add the assignment of the default value to the error handling branche. All in all, I'd say that this is a case where the first rule of the Internet applies: be liberal in what you accept, and conservative in what you send. In this case, ensure that any >> operators you write yourself obey the rule, but don't count on it from other >> operators. -- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Re: String search and extraction | Choate, Paul@DDS | Newsgroup comp.soft-sys.sas | 0 | 02-15-2006 03:52 PM |
| Re: String search and extraction | Droogendyk, Harry | Newsgroup comp.soft-sys.sas | 0 | 02-15-2006 03:45 PM |
| Re: Ideas for soft-coding extraction macro | Dunn, Toby | Newsgroup comp.soft-sys.sas | 1 | 12-10-2004 03:11 PM |
| how can i use SAS extraction data from CRSP | =?big5?b?qkywfbth?= | Newsgroup comp.soft-sys.sas | 0 | 10-26-2004 10:17 AM |
| how can i use SAS to extraction data from CRSP. | =?big5?b?qkywfbth?= | Newsgroup comp.soft-sys.sas | 0 | 10-26-2004 10:07 AM |