Creating integer64 and nanotime vectors in C++

Motivation: More Precise Timestamps

R has excellent facilities for dealing with both dates and datetime objects.
For datetime objects, the POSIXt time type can be mapped to POSIXct and
its representation of fractional seconds since the January 1, 1970 “epoch” as
well as to the broken-out list representation in POSIXlt. Many add-on
packages use these facilities.

POSIXct uses a double to provide 53 bits of resolution. That is generally
good enough for timestamps down to just above a microsecond, and has served
the R community rather well.

But increasingly, time increments are measure in nanoseconds. Other languages uses a (signed)
64-bit integer to represent (integer) nanoseconds since the epoch. A bit over a year I realized
that we have this in R too—by combining the integer64 type in thebit64 package by Jens Oehlschlaegel with theCCTZ-based parser and formatter in myRcppCCTZ package. And thus thenanotime package was created.

Here we used a single element with value 42, and created a nanotime vector from it—which is
taken to me 42 nanoseconds since the epoch, or basically almost at January 1, 1970.

Step 1: Large Integer Types

So more recently I had a need to efficiently generate such integer vector from int64_t data.
Both Leonardo and Dan helped with
initial discussion and tests. One can either use a reinterpret_cast<> or a straight memcpy as
the key trick in bit64 is to use the underlying 64-bitdouble. So we have the space, we just need to ensure we copy the bits rather than their values.
This leads to the following function to create an integer64 vector for use in R at the C++ level:

#include Rcpp::NumericVectormakeInt64(std::vector<int64_t>v){size_tlen=v.size();Rcpp::NumericVectorn(len);// storage vehicle we return them in
// transfers values 'keeping bits' but changing type
// using reinterpret_cast would get us a warning
std::memcpy(&(n[0]),&(v[0]),len*sizeof(double));n.attr("class")="integer64";returnn;}

This uses the standard trick of setting a class attribute to set an S3 class. Now the values inv will return to R (exactly how is treated below), and R will treat the vector as integer64
object (provided the bit64 package has been loaded).

Step 2: Nanotime

A nanotime vector is creating using an internal integer64 vector. So the previous functions
almost gets us there. But we need to set the S4 type correctly. So that needed some extra work.
The following function does it:

#include Rcpp::S4makeNanotime(std::vector<int64_t>v){size_tlen=v.size();Rcpp::NumericVectorn(len);// storage vehicle we return them in
// transfers values 'keeping bits' but changing type
// using reinterpret_cast would get us a warning
std::memcpy(&(n[0]),&(v[0]),len*sizeof(double));// do what needs to be done for the S4-ness: class, and .S3Class
// this was based on careful reading of .Internal(inspect(nanotime(c(0,1))))
Rcpp::CharacterVectorcl=Rcpp::CharacterVector::create("nanotime");cl.attr("package")="nanotime";n.attr(".S3Class")="integer64";n.attr("class")=cl;SET_S4_OBJECT(n);returnRcpp::S4(n);}

This creates a nanotime vector as a proper S4 object.

Step 3: Returning them R via data.table

The astute reader will have noticed that neither function had an Rcpp::export tag. This is
because of the function argument: int64_t is not representable natively by R, which is why we
need a workaround. Matt Dowle has been very helpful in providing
excellent support for nanotime in data.table
(even after we, ahem, borked it by switching from S3 to S4). This support was of course relatively
straightforward because data.table already had
support for the underlying integer64, and we had the additional formatters etc.

#include // Enable C++11 via this plugin (Rcpp 0.10.3 or later)
// [[Rcpp::plugins("cpp11")]]
Rcpp::NumericVectormakeInt64(std::vector<int64_t>v){size_tlen=v.size();Rcpp::NumericVectorn(len);// storage vehicle we return them in
// transfers values 'keeping bits' but changing type
// using reinterpret_cast would get us a warning
std::memcpy(&(n[0]),&(v[0]),len*sizeof(double));n.attr("class")="integer64";returnn;}Rcpp::S4makeNanotime(std::vector<int64_t>v){size_tlen=v.size();Rcpp::NumericVectorn(len);// storage vehicle we return them in
// transfers values 'keeping bits' but changing type
// using reinterpret_cast would get us a warning
std::memcpy(&(n[0]),&(v[0]),len*sizeof(double));// do what needs to be done for the S4-ness: class, and .S3Class
// this was based on careful reading of .Internal(inspect(nanotime(c(0,1))))
Rcpp::CharacterVectorcl=Rcpp::CharacterVector::create("nanotime");cl.attr("package")="nanotime";n.attr(".S3Class")="integer64";n.attr("class")=cl;SET_S4_OBJECT(n);returnRcpp::S4(n);}// [[Rcpp::export]]
Rcpp::DataFramegetDT(){std::vector<int64_t>d={1L,1000L,1000000L,1000000000L};std::vector<int64_t>ns={1510442294123456789L,1510442295123456789L,1510442296123456789L,1510442297123456789L};Rcpp::DataFramedf=Rcpp::DataFrame::create(Rcpp::Named("int64s")=makeInt64(d),Rcpp::Named("nanos")=makeNanotime(ns));df.attr("class")=Rcpp::CharacterVector::create("data.table","data.frame");return(df);}