Author: Wush Wu

# Rcereal: cereal headers for R and C++ serialization

#### Wush Wu

This package provides R with access to cereal header files. cereal is a header-only C++11 serialization library. cereal takes arbitrary data types and reversibly turns them into different representations, such as compact binary encodings, XML, or JSON.

For more information, please visit the official website of the cereal project: https://uscilab.github.io/cereal/.

The headers in this package can be used via:

- the `LinkingTo:` field in the DESCRIPTION of an R package;
- the `[[cpp11::linking_to("Rcereal")]]` attribute and `cpp11::source` from the cpp11 package, or;
- the `[[Rcpp::depends(Rcereal)]]` Rcpp attribute.

See the official cereal Quick Start guide for further details about using cereal in C++11.

## Usage

### Using cpp11

The following example shows how to use Rcereal alongside cpp11 to serialize and deserialize a user-defined `struct` using raw vectors:

    // path/to/example.cpp
    #include <sstream>

    #include <cpp11/raws.hpp>

    #include <cereal/archives/binary.hpp>

    struct MyClass
    {
        int x, y, z;
      // This method lets cereal know which data members to serialize
        template<class Archive>
        void serialize(Archive & archive)
        {
            archive( x, y, z ); // serialize things by passing them to the archive
        }
    };

    [[cpp11::linking_to("Rcereal")]]
    [[cpp11::register]]
    cpp11::raws serialize_myclass(int x = 1, int y = 2, int z = 3) {
        MyClass my_instance { x, y, z };
        std::stringstream ss;
        {
            cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
            oarchive(my_instance);
        }
        ss.seekg(0, ss.end);
        cpp11::writable::raws result(ss.tellg());
        ss.seekg(0, ss.beg);
        std::copy(std::istreambuf_iterator<char>{ss},
                  std::istreambuf_iterator<char>(),
                  result.begin());
        return result;
    }

    [[cpp11::register]]
    void deserialize_myclass(cpp11::raws src) {
        std::stringstream ss;
        std::copy(src.cbegin(), src.cend(), std::ostream_iterator<char>(ss));
        MyClass my_instance;
        {
            cereal::BinaryInputArchive iarchive(ss); // Read from input archive
            iarchive(my_instance);
        }
        Rprintf("%i,%i,%i\n", my_instance.x, my_instance.y, my_instance.z);
    }

Then, provided C++11 is enabled by default (see this tidyverse post 03/2023), in R:

    cpp11::cpp_source(file='path/to/example.cpp')
    raw_vector <- serialize_myclass(1, 2, 4)
    deserialize_myclass(raw_vector)

### Using Rcpp

The following example shows how to use Rcereal alongside Rcpp to serialize and deserialize a user-defined `struct` using raw vectors:

    // path/to/example.cpp

    //[[Rcpp::depends(Rcereal)]]
    #include <sstream>
    #include <cereal/archives/binary.hpp>
    #include <Rcpp.h>

    struct MyClass
    {
        /* same as cpp11 example above */
    };

    using namespace Rcpp;
    //[[Rcpp::export]]
    RawVector serialize_myclass(int x = 1, int y = 2, int z = 3) {
        MyClass my_instance { x, y, z};
        std::stringstream ss;
        {
            cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
            oarchive(my_instance);
        }
        ss.seekg(0, ss.end);
        RawVector result(ss.tellg());
        ss.seekg(0, ss.beg);
        ss.read(reinterpret_cast<char*>(&retval[0]), result.size());
        return result;
    }

    //[[Rcpp::export]]
    void deserialize_myclass(RawVector src) {
        std::stringstream ss;
        ss.write(reinterpret_cast<char*>(&src[0]), src.size());
        ss.seekg(0, ss.beg);
        MyClass my_instance;
        {
            cereal::BinaryInputArchive iarchive(ss);
            iarchive(my_instance);
        }
        Rcout << my_instance.x << "," << my_instance.y << "," << my_instance.z << std::endl;
    }

Then in R (provided C++11 is enabled):

    Rcpp::sourceCpp("path/to/example.cpp")
    raw_vector <- serialize_myclass(1, 2, 4)
    deserialize_myclass(raw_vector)

## Troubleshooting

C++11 may not be enabled by default for some compilers, if not; ensure that `PKG_CXXFLAGS` contains `-std=c++11`, e.g. if you use `pkgbuild::compile_dll()` to build a package (similarly for `devtools::build`):

    withr::with_makevars(c("PKG_CXXFLAGS"="std=c++11"),
                         pkgbuild::compile_dll(),
                         assignment="+=")

If the compiler reports missing header files, try `Rcereal::update_version()` to update the content of cereal from GitHub. Check that a directory named `cereal` is in the folder `system.file("include", package = "Rcereal")`.