1
\$\begingroup\$

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 doubles and long longs 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;
}
\$\endgroup\$
3
  • \$\begingroup\$ What type do you think auto result = make_number has? Do you think result could have a different type based on the value in the string test? I suspect your problem is "auto does not work that way" \$\endgroup\$
    – Yakk
    Commented Dec 9, 2016 at 20:56
  • \$\begingroup\$ Yeah, I'm noticing lots of problems, but that was just a prototype I've made in a couple minutes, I'll not quit that easily. I'm open to opinions or different approaches to achieve the same goal, that of an object that can behave similarly to how numbers behave in languages like python, ruby or lisp: i'm not focused on big nums yet, only on a way to distinguish between doubles and integers. \$\endgroup\$ Commented Dec 10, 2016 at 1:03
  • \$\begingroup\$ type erasure with enumerated finitle list of primitives and reflectible user objects and operations that can fail (expected<T,E> type, or exceptions). \$\endgroup\$
    – Yakk
    Commented Dec 10, 2016 at 2:20

1 Answer 1

1
\$\begingroup\$

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'

Demo

\$\endgroup\$
5
  • \$\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
    Commented Dec 9, 2016 at 20:33
  • \$\begingroup\$ it can't be greater* \$\endgroup\$
    – Dagoberto Pires
    Commented 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
    Commented 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
    Commented 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
    Commented Dec 9, 2016 at 22:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.