patterncppMinor
Converting a string to various number formats
Viewed 0 times
numberformatsconvertingstringvarious
Problem
I'm working on a set of PostgreSQL helper classes, and am representing each PostgreSQL data type as objects. Instead of creating a class for each number format, I've created a template so the user can create a
The problem I'm facing is I have the need to convert a string representing the value of the number (which is returned from libpq) to its relevant format (the
```
template
class Number : public spg::datatypes::BaseDataType
{
public:
Number() : BaseDataType(false) { checkTIsNumeric(); }
Number(T val) : BaseDataType(false), val(val) { checkTIsNumeric(); }
Number(const std::string& s) : BaseDataType(false) { checkTIsNumeric(); fromString(s); }
std::string toString() const { return std::to_string(val); }
int getParamCount() { return 1; }
T val;
protected:
void parseString(std::vector& splitted)
{
// see http://www.cplusplus.com/reference/string/
size_t hc = typeid(T).hash_code();
// TODO maybe these should be wrapped inside try/catch because if string doesn't contain T, it will throw
if (hc == typeid(int).hash_code())
val = std::stoi(splitted[0]);
else if (hc == typeid(double).hash_code())
val = std::stod(splitted[0]);
else if (hc == typeid(long double).hash_code())
val = std::stold(splitted[0]);
else if (hc == typeid(float).hash_code())
val = std::stof(splitted[0]);
else if (hc == typeid(long).hash_code())
val = std::stol(splitted[0]);
else if (hc == typeid(unsigned long).hash_code())
Number, Number etc as necessary, which is awesome as it saves me lots of coding!The problem I'm facing is I have the need to convert a string representing the value of the number (which is returned from libpq) to its relevant format (the
fromString method). - It's kinda messy. Is there a neater solution than a lot of if / else's?
- I also have the problem that if the string doesn't contain type
T, it causes an exception, so, to be safe, I'd have to wrap everything inside a try/catch making it even more messier! So, is there a way around doing this?
```
template
class Number : public spg::datatypes::BaseDataType
{
public:
Number() : BaseDataType(false) { checkTIsNumeric(); }
Number(T val) : BaseDataType(false), val(val) { checkTIsNumeric(); }
Number(const std::string& s) : BaseDataType(false) { checkTIsNumeric(); fromString(s); }
std::string toString() const { return std::to_string(val); }
int getParamCount() { return 1; }
T val;
protected:
void parseString(std::vector& splitted)
{
// see http://www.cplusplus.com/reference/string/
size_t hc = typeid(T).hash_code();
// TODO maybe these should be wrapped inside try/catch because if string doesn't contain T, it will throw
if (hc == typeid(int).hash_code())
val = std::stoi(splitted[0]);
else if (hc == typeid(double).hash_code())
val = std::stod(splitted[0]);
else if (hc == typeid(long double).hash_code())
val = std::stold(splitted[0]);
else if (hc == typeid(float).hash_code())
val = std::stof(splitted[0]);
else if (hc == typeid(long).hash_code())
val = std::stol(splitted[0]);
else if (hc == typeid(unsigned long).hash_code())
Solution
-
First things first: Export all the things which are different for different types into a policy-class, like
The main advantage is being actually able to have types with different interfaces without running into compilation-errors or at least warnings:
Add specializations as needed for efficiency / versatility / correctness / fun.
-
Eliminate runtime-checks for having selected an acceptable type, powered by the prior change.
That also allows you to use the same basic C++ type with completely different IO-behavior:
First things first: Export all the things which are different for different types into a policy-class, like
std::basic_string and many standard-library classes do.The main advantage is being actually able to have types with different interfaces without running into compilation-errors or at least warnings:
template
struct standard_number_traits {
static T parse(const std::string& in) {
T out;
std::stringstream{in} >> out;
return out;
}
using std::to_string;
};Add specializations as needed for efficiency / versatility / correctness / fun.
-
Eliminate runtime-checks for having selected an acceptable type, powered by the prior change.
That also allows you to use the same basic C++ type with completely different IO-behavior:
template >
struct Number : spg::datatypes::BaseDataType
{
Number(T val = T{}) : val(val) {}
// Or is BaseDataType(false) not default-init?
Number(const std::string& s) : Number(traits_type::parse(s)) {}
std::string toString() const { return traits_type::to_string(val); }
int getParamCount() { return 1; }
T val; // Shall that really be public? Well, might be ok.
};- Profit?
Code Snippets
template<class T>
struct standard_number_traits {
static T parse(const std::string& in) {
T out;
std::stringstream{in} >> out;
return out;
}
using std::to_string;
};template <class T, class traits_type = standard_number_traits<T>>
struct Number : spg::datatypes::BaseDataType
{
Number(T val = T{}) : val(val) {}
// Or is BaseDataType(false) not default-init?
Number(const std::string& s) : Number(traits_type::parse(s)) {}
std::string toString() const { return traits_type::to_string(val); }
int getParamCount() { return 1; }
T val; // Shall that really be public? Well, might be ok.
};Context
StackExchange Code Review Q#60038, answer score: 4
Revisions (0)
No revisions yet.