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