HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Converting a string to various number formats

Submitted by: @import:stackexchange-codereview··
0
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 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 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.