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<int>
, Number<double>
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 <typename T>
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<std::string>& 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())
val = std::stoul(splitted[0]);
else if (hc == typeid(long long).hash_code())
val = std::stoll(splitted[0]);
else if (hc == typeid(unsigned long long).hash_code())
val = std::stoull(splitted[0]);
}
private:
void checkTIsNumeric()
{
if (!std::is_arithmetic<T>::value || std::is_class<T>::value)
throw "T is not numeric";
}
};
I'm not sure if the checkTIsNumeric
is necessary, as compilation will fail if a non numeric type is passed as T
because std::to_string
doesn't have a suitable overload.
I did a few tests using the giant if / else above, and using a std::stringstream
. The latter was noticeably slower. E.g. in one benchmark, 14.0781s for the above code vs 19.6901s using std::stringstream
.
std::stringstream
orboost::lexical_cast
. \$\endgroup\$std::to_string
which is a C++11 feature, I would recommend moving your type checking from run time, using exceptions, to compile time usingstatic_assert
. \$\endgroup\$std::stringstream
? that would be for number to string, no? not from string to number? Besides, I would have thought usingval = std::stox(input)
would be faster than creating astd::stringstream
object and then calling methods on it? \$\endgroup\$std::stringstream
can be used both ways. You can set a string on it and read a value as you would read fromstd::cin
. \$\endgroup\$static_assert
usually produces better error messages and should be preferred. \$\endgroup\$