lexical_cast


Header: "boost/lexical_cast.hpp"

Lexical conversions are performed in virtually all applications. We convert strings to numeric values and vice versa. Many user-defined types can be converted to strings or created from strings. It is all too common to write the code for these conversions each time you need it, which suggests that it is very much suited for a reusable implementation. That's lexical_cast's purpose. Think of lexical_cast as using a std::stringstream as an interpreter between the string and other representation of a value. That means that it will work for any source with an appropriate output operator<< and any target with an appropriate operator<<. That's true for all of the built-in types and many user-defined types (UDTs).

Usage

lexical_cast makes a conversion between types look like any other type-converting cast. Of course, there must be a conversion function somewhere to make it work, but conceptually, it can be thought of as a cast. Rather than calling one of a number of conversion routines, or even coding the conversion locally, lexical_cast does that job for any types that meet its requirements. The source type must be OutputStreamable and the destination type must be InputStreamable. In addition, both types need to be CopyConstructible, and the target also DefaultConstructible and Assignable. OutputStreamable means that there's an operator<< defined for the type, and InputStreamable mandates an operator>>. This is true for many types, including the built-in types and the string classes from the Standard Library. To use lexical_cast, include "boost/lexical_cast.hpp".

Putting lexical_cast to Work

I won't bore you by producing conversion code manually to show how much code lexical_cast saves you, because I'm sure you've written these conversions yourself, and quite probably done so more than once. Instead, the example just uses lexical_cast for a number of common (lexical) type conversions.

 #include <iostream> #include <string> #include "boost/lexical_cast.hpp" int main() {   // string to int   std::string s="42";   int i=boost::lexical_cast<int>(s);   // float to string   float f=3.14151;   s=boost::lexical_cast<std::string>(f);   // literal to double   double d=boost::lexical_cast<double>("2.52");   // Failed conversion   s="Not an int";   try {     i=boost::lexical_cast<int>(s);   }   catch(boost::bad_lexical_cast& e) {     // The lexical_cast above will fail,     // and we'll end up here   } } 

This example shows only a few of many scenarios where lexical conversion are performed, and I think you'll agree that it usually takes a few more lines of code than this to get the job done. Whenever there's uncertainty that the conversion is valid, the lexical_cast should be protected by a TRy/catch block, as you see in the preceding example. You'll note that there is no way of controlling the formatting of these conversions; if you need that level of control, use std::stringstream!

If you were to manually convert between types, you'd need to handle the conversions and possible failures in different ways for different types. This is not only inconvenient; it also stands in the way of any attempt to perform the conversions in generic code. We'll see how lexical_cast can help with that in just a moment.

The conversions in the example are fairly simple to do by hand, and although lexical_cast makes it look that much simpler, there's a chance that you missed the beauty and elegance of this cast. But it's there. Consider again the simple requirements that need to be fulfilled for any class to work with lexical_cast. Think about the fact that a conversion can be done in one line for all of those classes meeting the requirements. Combine this with the fact that the implementation relies on the Standard Library's stringstream to do the grunt work,[15] and you can see that lexical_cast is more than a convenient way of performing lexical conversions; it's also a display of the art of programming in C++.

[15] Actually, there are optimizations that avoid the overhead of using std::stringstream for some conversions. Indeed, you can customize its behavior for your own types, if necessary.

Generic Programming with lexical_cast

As a simple example of using lexical_cast for solving generic programming tasks, consider what it would take to create a to_string function. The function would accept any type of argument (adhering to certain requirements, of course) and return a string representing the value. Users of the Standard Library would no doubt be able to do this in a few lines of code, with the help of std::stringstream. In this case, we'll just use lexical_cast for most of the implementation, with just a forwarding function and some error handling.

 #include <iostream> #include <string> #include "boost/lexical_cast.hpp" template <typename T> std::string to_string(const T& arg) {   try {     return boost::lexical_cast<std::string>(arg);   }   catch(boost::bad_lexical_cast& e) {     return "";   } } int main() {   std::string s=to_string(412);   s=to_string(2.357); } 

This handy function is not only easy to implement, it also adds value, elegantly enabled by virtue of lexical_cast.

Enabling Classes for Use with lexical_cast

Because lexical_cast only requires that operator<< and operator>> be suitably defined for the types it operates on, it's straightforward to add support for lexical conversions to user-defined types. A simple UDT that can be both the target and source when used with lexical_cast might look like this:

 class lexical_castable { public:   lexical_castable() {};   lexical_castable(const std::string s) : s_(s) {};   friend std::ostream operator<<     (std::ostream& o, const lexical_castable& le);   friend std::istream operator>>     (std::istream& i, lexical_castable& le); private:   virtual void print_(std::ostream& o) const {     o << s_ <<"\n";   }   virtual void read_(std::istream& i) const {     i >> s_;   }   std::string s_; }; std::ostream operator<<(std::ostream& o,   const lexical_castable& le) {   le.print_(o);   return o; } std::istream operator>>(std::istream& i, lexical_castable& le) {   le.read_(i);   return i; } 

The lexical_castable class can now be used like so:

 int main(int argc, char* argv[]) {   lexical_castable le;   std::cin >> le;   try {     int i = boost::lexical_cast<int>(le);   }   catch(boost::bad_lexical_cast&) {      std::cout << "You were supposed to enter a number!\n";   } } 

Of course, the input and output operators allow the class to be used with other streams as well. If you're using IOStreams from the Standard Library, or another library that uses operator<< and operator>>, you probably have many classes that are ready for lexical_cast in place. These do not have to be modified at all. Just lexically cast them!

Summary

lexical_cast is a reusable and reasonably efficient tool for lexical conversions, those between string and other types. With its combination of functionality and elegance, it is a great example of what a creative programmer can do.[16] Rather than implementing small conversion functions whenever the need arises, or worse, implementing that logic directly in other functions, a generic tool like lexical_cast should be used. It helps make the code clearer and allows programmers to focus on solving the problem at hand.

[16] I've always feltpresumptuously, I knowthat we, The Programmers, work simultaneously with mathematics, physics, engineering, architecture, sculpturing, and a few other arts and disciplines. This is daunting, but also endlessly rewarding.

When to use lexical_cast:

  • For conversions from string types to numeric types

  • For conversions from numeric types to string types

  • For all lexical conversions that are supported by your user-defined types



    Beyond the C++ Standard Library(c) An Introduction to Boost
    Beyond the C++ Standard Library: An Introduction to Boost
    ISBN: 0321133544
    EAN: 2147483647
    Year: 2006
    Pages: 125

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net