I'm writing a scripting language and I'm trying to implement a dynamic number type in C++. Right now, I'm only interested in getting long double
s and long long
s working right. I was able to get this contraption to work. I would like to know if there's an easier, more organized idea than mine.
I don't want the class to have any arithmetic operators, I just want it to be a generic numeric container. Everything else, I want to take advantage of all arithmetic overloads that C++ already has (example: myNum1.data + myNum2.data
) - even though they can be of different numeric types, C++ will work its casting magic.
#include <algorithm>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
using std::cout;
using std::endl;
template <typename t>
class number {
public:
t data {};
number(t const p_data)
: data {p_data}
{}
std::string to_string() const {
std::ostringstream output;
output.precision(std::numeric_limits<t>::max_digits10);
output << std::uppercase << data;
return output.str();
}
};
template <typename t>
number<t> make_number(t const p_input) {
return number<t>(p_input);
}
enum class number_t {
integer_t,
float_t,
error_t
};
number_t validate(std::string const & p_input) {
if (all_of(cbegin(p_input), cend(p_input), isdigit)) {
return number_t::integer_t;
} else {
std::istringstream input {p_input};
double long output;
input >> output >> std::ws;
if (!input.fail() && input.eof()) {
return number_t::float_t;
} else {
return number_t::error_t;
}
}
return number_t::error_t;
}
int main() {
std::string test {"9223372036854775809"};
auto result = make_number(
validate(test) == number_t::integer_t
? (test > "9223372036854775807" ? 9223372036854775807 : stoll(test))
: validate(test) == number_t::float_t
? stold(test)
: 0);
cout << result.data << endl;
std::string test_string {result.to_string()};
cout << test_string << endl;
return 0;
}
1 Answer 1
I think the biggest improvement you can make is to add an operator t()
to your number class so that automatic conversion to t
can take place:
template <typename t>
class number {
public:
t data {};
operator t(){return data;}
// ...
};
Then you can do a lot of work without explicitly referencing data
:
auto result = make_number(1) + make_number(2.0);
std::cout << result << std::endl; // prints '3'
static_assert(std::is_same<decltype(result), double>::value, "Fail");
Or you can wrap the result in a make_number
to get a to_string
:
auto ret = make_number(make_number(1) + make_number(2.0));
std::cout << ret.to_string() << std::endl; // also prints '3'
-
\$\begingroup\$ Thanks! How can I now build a function that accepts a string, validates the string and returns a object of type number? I want to validate the string so that if it's an integer, it can be greater than 9223372036854775807, if it's it would return 9223372036854775807, if not a number, it would return 0. :D \$\endgroup\$Dagoberto Pires– Dagoberto Pires2016年12月09日 20:33:36 +00:00Commented Dec 9, 2016 at 20:33
-
\$\begingroup\$ it can't be greater* \$\endgroup\$Dagoberto Pires– Dagoberto Pires2016年12月09日 20:40:27 +00:00Commented Dec 9, 2016 at 20:40
-
\$\begingroup\$ @DagobertoPires: For validation, outside of leaning on the exceptions that get thrown by
stod
and friends on failed conversion, you would validate the string in a similar manner you are now. Personally, I think stod etc. are a little lenient in that they'll work in a lot of cases where the string contains non-numeric input. You could also look at boost's lexical_cast. Otherwise, I think this is a separate question. You may look into a boost variant. \$\endgroup\$AndyG– AndyG2016年12月09日 20:57:08 +00:00Commented Dec 9, 2016 at 20:57 -
\$\begingroup\$ So, there's no possible way to wrap it all in nicely packaged magical function? I'm trying not to use boost. My validation method works, it would be cool if it could be wrapped around a function, however my biggest problem is the return type which needs to be known in advance ;) \$\endgroup\$Dagoberto Pires– Dagoberto Pires2016年12月09日 21:17:23 +00:00Commented Dec 9, 2016 at 21:17
-
\$\begingroup\$ @DagobertoPires: Yeah that's a common problem. You can get away with using pointers to base classes, though. Consider the Factory pattern. \$\endgroup\$AndyG– AndyG2016年12月09日 22:25:42 +00:00Commented Dec 9, 2016 at 22:25
auto result = make_number
has? Do you thinkresult
could have a different type based on the value in the stringtest
? I suspect your problem is "auto
does not work that way" \$\endgroup\$expected<T,E>
type, or exceptions). \$\endgroup\$