Alpaca
Serialization library written in C++17 - Pack C++ structs into a compact byte-array without any macros or boilerplate code
Install / Use
/learn @p-ranav/AlpacaREADME
Pack C++ structs into a compact byte-array without any macros or boilerplate code.
alpacais header-only serialization library for modern C++, written in C++17- No macros or boilerplate, no source code generation, no external dependencies
- Simple, fast (see benchmarks), and easy to use
- Supports basic data types, STL containers, unique pointers, recursive data structures, optionals, variants and more
- Serialize to C-style arrays,
std::array,std::vector, or even directly to files - Highly configurable at compile time
- Little endian by default. Configurable to use big endian byte order
- Variable-length encoding by default for large integer types. Configurable to use fixed-width encoding
- Optional type hashing and data structure versioning - recursively generates a type hash that is checked during deserialization
- Optional integrity checking - detects data corruption during deserialization using checksums
- Samples here
- Experimental Python support with pybind11-based wrapper module pyalpaca
- MIT license
#include <alpaca/alpaca.h>
struct Config {
std::string device;
std::pair<unsigned, unsigned> resolution;
std::array<double, 9> K_matrix;
std::vector<float> distortion_coeffients;
std::map<std::string, std::variant<uint16_t, std::string, bool>> parameters;
};
// Construct the object
Config c{"/dev/video0", {640, 480},
{223.28249888247538, 0.0, 152.30570853111396,
0.0, 223.8756535707556, 124.5606000035353,
0.0, 0.0, 1.0},
{-0.44158343539568284, 0.23861463831967872, 0.0016338407443826572,
0.0034950038632981604, -0.05239245892096022},
{{"start_server", bool{true}},
{"max_depth", uint16_t{5}},
{"model_path", std::string{"foo/bar.pt"}}}};
// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(c, bytes);
// Deserialize
std::error_code ec;
auto object = alpaca::deserialize<Config>(bytes, ec);
if (!ec) {
// use object
}
The source for the above example can be found here.
Table of Contents
- Usage and API
- Examples
- Backward and Forward Compatibility
- Configuration Options
- Python Interoperability
- Performance Benchmarks
- Building, Installing, and Testing
- CMake Integration
- Supported Toolchains
- Contributing
- License
Usage and API
Serialization
The alpaca::serialize(...) function accepts 2 arguments: an input aggregate class type (typically a struct), and an output container, e.g., std::vector<uint8_t>, std::array<uint8_T, N> etc. Serialization will attempt to pack the aggregate input into the container.
There are two variants to serialize, one of which takes an alpaca::options for additional configuration:
// Serialize a struct T (with N fields) into Container
template <class T, size_t N, class Container>
auto serialize(const T&, Container&) -> size_t /* bytes_written */;
// Serialize a struct T (with N fields) into Container using options O
template <options O, class T, size_t N, class Container>
auto serialize(const T&, Container&) -> size_t /* bytes_written */;
NOTE Under most circumstances, the number of fields in the struct, N, need not be provided. In certain use-cases, e.g., std::optional, the user will need to provide this N for correct operation. More on this here.
Examples of valid serialize calls include:
struct MyStruct {
int value;
};
// Construct object
MyStruct object{5};
// Serialize to a C-style array
uint8_t buffer[10];
auto bytes_written = serialize(object, buffer);
// Serialize to std::array
std::array<uint8_t, 5> bytes;
auto bytes_written = serialize(object, bytes);
// Serialize to std::vector
std::vector<uint8_t> bytes;
auto bytes_written = serialize(object, bytes);
// Serialize to file
std::ofstream os;
os.open("foo.bin", std::ios::out | std::ios::binary);
auto bytes_written = serialize(object, os);
// Serialize with options
std::vector<uint8_t> bytes;
constexpr auto OPTIONS = options::fixed_length_encoding |
options::with_version |
options::with_checksum;
auto bytes_written = serialize<OPTIONS>(object, bytes);
Deserialization
The alpaca::deserialize(...) function, likewise, accepts a container like std::vector<uint8_t> or std::array<uint8_t, N> and an std::error_code that will be set in case of error conditions. Deserialization will attempt to unpack the container of bytes into an aggregate class type, returning the class object.
Deserialization from C-style arrays is supported as well, though in this case, the number of bytes to read from the buffer needs to be provided.
Like serialize(), deserialization has two variants, one of which accepts an alpaca::options template parameter.
// Deserialize a Container into struct T (with N fields)
template <class T, size_t N, class Container>
auto deserialize(Container&, std::error_code&) -> T;
// Deserialize `size` bytes from a Container into struct T (with N fields)
template <class T, size_t N, class Container>
auto deserialize(Container&, const std::size_t, std::error_code&) -> T;
// Deserialize a Container into struct T (with N fields) using options O
template <options O, class T, size_t N, class Container>
auto deserialize(Container&, std::error_code&) -> T;
// Deserialize `size` bytes from a Container into struct T (with N fields) using options O
template <options O, class T, size_t N, class Container>
auto deserialize(Container&, const std::size_t, std::error_code&) -> T;
Examples of valid deserialize calls include:
// Deserialize from flie
std::ifstream is;
is.open("foo.bin", std::ios::in | std::ios::binary);
auto file_size = std::filesystem::file_size("foo.bin");
std::error_code ec;
auto object = deserialize<MyStruct>(is, file_size, ec);
if (!ec) {
// use object
}
// Deserialize from std::array or std::vector
// Default options
std::error_code ec;
auto object = deserialize<MyStruct>(bytes, ec);
if (!ec) {
// use object
}
// Deserialize from std::array or std::vector
// Custom options
std::error_code ec;
constexpr auto OPTIONS = options::fixed_length_encoding |
options::with_version |
options::with_checksum;
auto object = deserialize<OPTIONS, MyStruct>(bytes, ec);
if (!ec) {
// use object
}
Examples
Fundamental types
- Fundamental types, including
char,bool, fixed-width integer types likeuint16_t, and floating-point types are supported by alpaca - For larger integer types including
int32_t, alpaca may use variable-length encoding where applicable. If fixed-width encoding is preferred, this can be changed usingoptions::fixed_width_encoding. - By default, alpaca uses little endian for the byte order. This can be changed to use big-endian byte order using
options::big_endian
struct MyStruct {
char a;
int b;
uint64_t c;
float d;
bool e;
};
MyStruct s{'a', 5, 12345, 3.14f, true};
// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(s, bytes); // 9 bytes
// bytes:
// {
// 0x61 // char 'a'
// 0x05 // int 5
// 0xb9 0x60 // uint 12345
// 0xc3 0
