lexical cast
From Cppwiki
Standard C++ does not provide a general-purpose utility function to convert between disparate data types. The Boost library provides boost::lexical_cast, which is perhaps the canonical solution to this problem. You should use it if you can.
Alternatively, you can use the C++ stringstream class to perform the conversion, although it's a bit tricky. This is the simplest thing that could possibly work, if you don't care at all about error handling:
#include <sstream>
#include <string>
#include <cassert>
const int answer = 42;
int main() {
std::string answer_s;
{ std::stringstream ss; ss << answer; ss >> answer_s; }
int target;
{ std::stringstream ss; ss << answer_s; ss >> target; }
assert(target == answer);
}
You could then wrap the interesting bits into a function:
#include <sstream>
template <class Target, class Source>
Target lexical_cast(const Source &source) {
Target target;
std::stringstream ss;
ss << source;
ss >> target;
return target;
}
#include <string>
#include <cassert>
const int answer = 42;
int main() {
const std::string answer_s = lexical_cast<std::string>(answer);
assert(lexical_cast<int>(answer_s) == answer);
}
This approach has some problems:
- Behavior of conversion to pointer-to-char is undefined -- it may crash your program.
lexical_cast<char *>(answer) // KABOOM!
- That's because it ends up trying to store input into some random address in memory:
char *target; // 'target' refers to some (unknown) address in memory ss >> target; // read from 'ss' and store result into memory at that (unknown) address
- There's no way to tell if the conversion was successful:
std::cerr << lexical_cast<int>("3.14") << '\n'; // output: 3
std::cerr << lexical_cast<int>("not an int") << '\n'; // output: unknown
- You might consider the conversion as a failure if it only read part of the data (e.g. the "3" in "3.14").
A better implementation would have the following properties:
- It uses concept checking to generate a C++ compilation error if the target is a pointer.
- It throws an exception if the conversion failed, including if any data (excluding whitespace) is left over.
- Hint: (ss >> std::ws).ignore().good() skips whitespace, then tries to ignore a character, then checks if it worked. If it did, then something's wrong. :)
#include <sstream>
#include <typeinfo>
/** Disable lexical_cast to certain types, such as pointers. */
template <typename> struct LexicallyCastableTo { };
template <typename Target> struct LexicallyCastableTo<Target *>;
/** Thrown by lexical_cast if conversion fails. */
class bad_lexical_cast : public std::bad_cast {
public:
explicit bad_lexical_cast(const char *msg) : m_msg(msg) { }
const char *what() const throw() { return m_msg; }
private:
const char *m_msg;
};
/** Similar to "target = lexical_cast<Target>(source)", but doesn't initialize nor copy an object of type Target.
* @throw bad_lexical_cast Failure converting Source to string or string to Target
*/
template <typename Source, typename Target>
void lexical_assign(const Source &source, Target& target) {
std::stringstream ss;
if (!(ss << source)) {
throw bad_lexical_cast("failed to convert from source to string");
} else if (!(ss >> target) || (ss >> std::ws).ignore().good()) {
throw bad_lexical_cast("failed to convert from string to target");
}
}
/** Use std::stringstream to convert @c source to type Target.
* @throw bad_lexical_cast Failure converting Source to string or string to Target
*/
template <typename Target, typename Source>
Target lexical_cast(const Source &source) {
LexicallyCastableTo<Target>();
Target target;
lexical_assign(source, target);
return target;
}
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
const int answer = 42;
int main() {
const std::string answer_s = lexical_cast<std::string>(answer);
assert(lexical_cast<int>(answer_s) == answer);
std::vector<const char *> bad_ints;
bad_ints.push_back("not an int");
bad_ints.push_back("3.14");
bad_ints.push_back("true");
for (unsigned i = 0; i < bad_ints.size(); ++i) {
try {
lexical_cast<int>(bad_ints[i]);
} catch(const bad_lexical_cast &) {
continue;
}
assert(false && "should have thrown, caught, and continued");
}
#if 0
// invalid use of undefined type `struct LexicallyCastableTo<char*>'
lexical_cast<char *>(answer);
#endif
}