2

A simplified example:

//...

std::chrono::milliseconds _delay; //field in question

unsigned long getDelay() const
{
    return _delay.count();
}

void setDelay(unsigned long delay)
{
    _delay = std::chrono::milliseconds(delay);
}

json::value toJson() const
{
    using namespace web;
    json::value obj;
    obj[delay] = json::value::number(_delay.count());

    return obj;
}

bool fromJson(web::json::value value)
{
    for (auto it = value.as_object().cbegin(); it != value.as_object().cend(); ++it)
    {
        const std::string& str = it->first;
        const json::value& v = it->second;

        if (str == "delay")
        {
            if (v.is_integer())
            {
                _id = v.as_number().to_uint64(); //was v.as_integer(); - thx Nicol Bogas
                continue;
            }
            else
            {
                return false;
            }
        }

        //...
    }
}

My class contains a bunch of std::chrono::milliseconds fields which represent corresponding delays. I want to store these values in a JSON representation of the class. Most JSON values only operate with standard internal types, but std::chrono::milliseconds implements std::chrono::duration template. It has a count() method that returns an amount of ticks as a variable of rep type which on my system is a typedef to long long

The code is required to be portable. How safe is it, from a practical view, to convert the result of count() to simple long and that pass it to JSON library? Have I implemented accessors correctly (with unsigned long type)? In practice, I usually store delay values in a range from 0 to 5000, but nothing stops other people from editing configuration files and writing incorrect values there, which might cause runtime errors and strange behavior.

P.S. Just to be clear - it's not a Rubber Duck Debugging thread. I've never dealt with "might be very big" values before and C++ with multiple libstdc++ implementations and typedefs makes it difficult. I am worried about potential bugs and pitfalls. Thanks.

CorellianAle
  • 645
  • 8
  • 16
  • I think the better question is what does `as_integer` return? That's far more likely to be a limitation than the size of `long`. – Nicol Bolas Feb 09 '18 at 15:40
  • Do you have to convert it? Can't you just store it as a `long long`? – NathanOliver Feb 09 '18 at 15:46
  • @NicolBolas Yes, I've rechecked the [source](https://github.com/Microsoft/cpprestsdk/blob/master/Release/include/cpprest/json.h) and found that it returns an `int`. I've changed it to `v.as_number()` to `v.as_number().to_uint64()` – CorellianAle Feb 09 '18 at 15:46
  • 1
    @CorellianAle, That's not really going to do anything. It's still going through the presumably smaller type first. – chris Feb 09 '18 at 15:53
  • `uint64_t` is ok. Looks like https://json.org/ spec says number is `+-digit1-9 digits` so 64-bits should be enough. Just have to take care of the signed/unsigned. You could alwasy put and `static_assert(sizeof(uint64_t) == sizeof(long long), "should fit");` for the chrono `long long` fit – Mihayl Feb 09 '18 at 16:01
  • 1
    @A.A: The spec is that the FIRST digit can be 1-9, not that there are up to 9 digits. There could be more than 9 digits. – John Zwinck Feb 09 '18 at 16:07

2 Answers2

0

In practice, most JSON implementations use 64-bit IEEE float, aka double in C++, for all numbers.

So you have a bit of a mismatch: C++ milliseconds can store +/- 9e18 with full precision, but JSON numbers can store +/- 1.8e308 (but with only 53 bits of precision).

Therefore, if your C++ value is more than 285 kiloyears (2^53 ms), it will lose precision. It looks like this will be no problem for your application, since the value is a "delay" and ain't nobody got time for that. If your JSON parsing logic reads a value larger than INT64_MAX, it could simply throw an exception.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
0

I would suggest defining your own duration type "double precision seconds". This can then be extracted as the count, and passed to other languages with a clear and unambiguous meaning. Some sample code (nothing to do with json):

 TimePoint nowTime(std::chrono::system_clock::now());
 typedef std::chrono::duration<double>       FSeconds;
 FSeconds delta = std::chrono::duration_cast<FSeconds>(nowTime - startTime);
 ofs << "@" << std::fixed << delta.count() //...

So here, delta.count is seconds, but it is real rather than integer, so the miliseconds, or whatever, are preserved in the decimal.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27