Code Monkey home page Code Monkey logo

p-ranav / alpaca Goto Github PK

View Code? Open in Web Editor NEW
451.0 451.0 43.0 837 KB

Serialization library written in C++17 - Pack C++ structs into a compact byte-array without any macros or boilerplate code

License: MIT License

C++ 95.86% CMake 0.35% C 3.78% Shell 0.01%
benchmark binary bytes checksum cpp17 cpp17-library efficient endian-independent fast header-only mit-license no-macros pybind11 python-wrapper python3 reflection serialization-library versioning

alpaca's Introduction

build standard license version

Pack C++ structs into a compact byte-array without any macros or boilerplate code.

  • alpaca is 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

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 like uint16_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 using options::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

Source

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 0xf5 0x48 0x40   // float 3.14f
//   0x01                  // bool true
// }

In the above example, c is a uint64_t but its value is only 5. Here, alpaca will pack the value in a single byte instead of taking up 8 bytes. This is the default behavior for larger integer types.

Arrays, Vectors, and Strings

alpaca supports sequence containers including std::array, std::vector, and std::string. Nested arrays and vectors work seamlessly.

Source

struct MyStruct {
  std::array<int, 3> a;
  std::vector<std::vector<float>> b;
  std::string c;
};

MyStruct s{{1, 2, 3}, {{3.14, 1.61}, {2.71, -1}}, {"Hello"}};

// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(s, bytes); // 28 bytes

// bytes:
// {
//   0x01 0x02 0x03            // array {1, 2, 3}
//   0x02                      // 2-element vector
//   0x02                      // 2-element (inner) vector
//   0xc3 0xf5 0x48 0x40       // vector[0][0] = 3.14
//   0x7b 0x14 0xce 0x3f       // vector[0][1] = 1.61
//   0x02                      // 2-element (inner) vector
//   0xa4 0x70 0x2d 0x40       // vector[1][0] = 2.71
//   0x00 0x00 0x80 0xbf       // vector[1][1] = -1
//   0x05                      // start of 5-byte string
//   0x48 0x65 0x6c 0x6c 0x6f  // string "Hello"
// }

For std::string, the general structure is as follows:

  • The first N bytes is a VLQ encoding of the size of the container
  • Then, the byte array is simply bytes of data
  string length    char array -->
+----+----+-----+  +----+----+-----+
| A1 | A2 | ... |  | B1 | B2 | ... |
+----+----+-----+  +----+----+-----+

For std::vector<T>, the general structure is as follows:

  • The first N bytes is a VLQ encoding of the size of the container
  • Then, each value in the vector is encoding accordingly to the rules for value_type T

NOTE alpaca also supports std::list and std::deque with the same structure.

   vector size          value1                value2          value3
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---
| A1 | A2 | ... |  | B1 | B2 | ... |  | C1 | C2 | C3 | ... |  |...
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---

For std::array<T, N>, since the (1) number of elements and (2) type of element in the array is known (both at serialization and deserialization time), this information is not stored in the byte array. Note that, for this reason, deserialization cannot unpack the bytes into an array of a different size. Important: Make sure to use the same array size on both the serialization and deserialization side.

The byte array simply includes the encoding for value_type T for each value in the array.

     value1             value2                value3          value4
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---
| A1 | A2 | ... |  | B1 | B2 | ... |  | C1 | C2 | C3 | ... |  |...
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---

Multi-byte Character Strings

alpaca supports the standard wstring u16string, and u32string variants of std::basic_string type:

struct my_struct {
  std::wstring name;
  std::u16string example;
  std::u32string greeting;
};

std::vector<uint8_t> bytes;

// serialize
{
  my_struct s{L"緋村 剣心", u"This is a string", U"Hello, 世界"};
  serialize(s, bytes);
}

// deserialize
{
  std::error_code ec;
  auto object = deserialize<my_struct>(bytes, ec);
  assert((bool)ec == false);
  assert(object.name == L"緋村 剣心");    
  assert(object.example == u"This is a string");
  assert(object.greeting == U"Hello, 世界");
}

Maps and Sets

For associative containers, alpaca supports std::map, std::unordered_map, std::set, and std::unordered_set.

Source

struct MyStruct {
  std::map<std::string, std::tuple<uint8_t, uint8_t, uint8_t>> a;
  std::set<int> b;
};

MyStruct s{{{"red", std::make_tuple(255, 0, 0)},
            {"green", std::make_tuple(0, 255, 0)},
            {"blue", std::make_tuple(0, 0, 255)}},
           {1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 4}};

// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(s, bytes); // 30 bytes

// bytes:
// {
//   0x03                      // 3-element map
//   0x04                      // start of 4-byte string
//   0x62 0x6c 0x75 0x65       // string "blue"
//   0x00 0x00 0xff            // tuple {0, 0, 255}
//   0x05                      // start of 5-byte string
//   0x67 0x72 0x65 0x65 0x6e  // string "green"
//   0x00 0xff 0x00            // tuple {0, 255, 0}
//   0x03                      // 3-byte string
//   0x72 0x65 0x64            // string "red"
//   0xff 0x00 0x00            // tuple {255, 0, 0}
//   0x04                      // 4-element set
//   0x01 0x02 0x03 0x04       // set {1, 2, 3, 4}
// }

For std::map<K, V> and std::unordered_map<K, V>, the structure is similar to sequence containers:

  • The first N bytes is a VLQ encoding of the size of the container
  • Then, the byte array is K₁, V₁, K₂, V₂, K₃, V₃, ... for each key Kᵢ and value Vᵢ in the map
     map size            key1                  value1         key2
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---
| A1 | A2 | ... |  | B1 | B2 | ... |  | C1 | C2 | C3 | ... |  |...
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---

The format for std::set and std::unordered_set is the same as with std::vector<T>:

  • The first N bytes is a VLQ encoding of the size of the container
  • Then, for each value in the set, is encoding accordingly to the rules for value_type T
     set size            value1              value2           value3
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---
| A1 | A2 | ... |  | B1 | B2 | ... |  | C1 | C2 | C3 | ... |  |...
+----+----+-----+  +----+----+-----+  +----+----+----+-----+  +---

Nested Structures

alpaca works with nested structures and doubly-nested structures seamlessly:

Source

struct MyStruct {
  struct gps {
    double latitude;
    double longitude;
  };
  gps location;

  struct image {
    uint16_t width;
    uint16_t height;
    std::string url;

    struct format {
      enum class type { bayer_10bit, yuyv_422 };
      type type;
    };
    format format;
  };
  image thumbnail;
};

MyStruct s{{41.13, -73.70},
           {480,
            340,
            "https://foo/bar/baz.jpg",
            {MyStruct::image::format::type::yuyv_422}}};

// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(s, bytes); // 45 bytes

// bytes:
// {
//   0x71 0x3d 0x0a 0xd7 0xa3 0x90 0x44 0x40  // double 41.13
//   0xcd 0xcc 0xcc 0xcc 0xcc 0x6c 0x52 0xc0  // double -73.70
//   0xe0 0x01                                // uint 480
//   0x54 0x01                                // uint 340
//   0x17                                     // 23-byte string
//   0x68 0x74 0x74 0x70 0x73 0x3a 0x2f 0x2f  // "https://"
//   0x66 0x6f 0x6f 0x2f                      // "foo/"
//   0x62 0x61 0x72 0x2f                      // "bar/"
//   0x62 0x61 0x7a                           // "baz"
//   0x2e 0x6a 0x70 0x67                      // ".jpg"
//   0x01                                     // enum value 1
// }

Optional Values

alpaca has some difficulty with std::optional. Due to the implementation of aggregate_arity, alpaca is unable to correctly determine the number of fields in the struct with optional fields.

So, to help out, specify the number of fields manually using serialize<MyStruct, N>(...).

Source

struct MyStruct {
  std::optional<int> a;
  std::optional<float> b;
  std::optional<std::string> c;
  std::optional<std::vector<bool>> d;
};

MyStruct s{5, 3.14f, std::nullopt, std::vector<bool>{true, false, true, false}};

// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize<MyStruct, 4>(s, bytes); // 14 bytes
	                            // ^^^^^^^^^^^^^ 
	                            //  specify the number of fields (4) in struct manually
	                            //  alpaca fails at correctly detecting 
				    //  this due to the nature of std::optional

// bytes:
// {
//   0x01                    // optional has_value = true
//   0x05                    // value = 5
//   0x01                    // optional has_value = true
//   0xc3 0xf5 0x48 0x40     // value = 3.14f
//   0x00                    // optional has_value = false
//   0x01                    // optional has_value = true
//   0x04                    // 4-element vector
//   0x01 0x00 0x01 0x00     // {true, false, true, false}
// }

NOTE Nested structures work as long std::optional is not used in the inner struct. This is because (1) alpaca will fail to correctly detect the number of fields in a struct when std::optional is used and (2) the API does not provide the means for the user to specify the number of fields in inner structs.

For std::optional<T>, a leading byte is used to represent if the optional has value

has_value?    value (if previous byte is 0x01)         
+----------+  +----+----+----+-----+
|    A1    |  | B1 | B2 | B3 | ... |
+----------+  +----+----+----+-----+

Type-safe Unions - Variant Types

alpaca also support std::variant. Although this is an uncommon data structure for one to use in a messaging framework, it is supported and available. Miscellaneous configuration parameters, like in JSON, can be serialized as variant values and sent to servers. For each variant, a byte of information is used to represent the variant_index. As long as the deserialization is performed on the same variant type (where the indices of each type matches exactly), the std::get<index> will work fine.

Source

struct MyStruct {
  std::map<std::string, 
           std::variant<uint16_t, 
                        std::string, 
                        bool,
                        std::vector<std::string>>
          > value;
};

Config s{{{"keepalive", true},
          {"port", uint16_t{8080}},
          {"ip_address", std::string{"192.168.8.1"}},
          {"subscriptions", std::vector<std::string>{"motor_state", "battery_state"}}}};
  
// serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(s, bytes); // 87 bytes

// bytes:
// {
//   0x04                                                                // 4-element map
//   0x0a                                                                // 10-byte string
//   0x69 0x70 0x5f 0x61 0x64 0x64 0x72 0x65 0x73 0x73                   // string "ip_address"
//   0x01                                                                // variant index = 1, type string
//   0x0b                                                                // 11-byte string
//   0x31 0x39 0x32 0x2e 0x31 0x36 0x38 0x2e 0x38 0x2e 0x31              // string "192.168.8.1"
//   0x09                                                                // 9-byte string
//   0x6b 0x65 0x65 0x70 0x61 0x6c 0x69 0x76 0x65                        // string "keepalive"
//   0x02                                                                // variant index = 2, type bool
//   0x01                                                                // bool true
//   0x04                                                                // 4-byte string
//   0x70 0x6f 0x72 0x74                                                 // string "port"
//   0x00                                                                // variant index = 0, type uint16_t
//   0x90 0x1f                                                           // uint 8080
//   0x0d                                                                // 13-byte string
//   0x73 0x75 0x62 0x73 0x63 0x72 0x69 0x70 0x74 0x69 0x6f 0x6e 0x73    // string "subscriptions"
//   0x03                                                                // variant index = 3, type vector<string>
//   0x02                                                                // 2-element vector
//   0x0b                                                                // 11-byte string
//   0x6d 0x6f 0x74 0x6f 0x72 0x5f 0x73 0x74 0x61 0x74 0x65              // string "motor_state"
//   0x0d                                                                // 13-byte string
//   0x62 0x61 0x74 0x74 0x65 0x72 0x79 0x5f 0x73 0x74 0x61 0x74 0x65    // string "battery_state"
// }

For std::variant<T, U, ...>, the leading bytes represent the index of the variant that is used by the value

variant index       value       
+-----------+  +----+----+-----+
|    A1     |  | B1 | B2 | ... |
+-----------+  +----+----+-----+

Smart Pointers and Recursive Data Structures

alpaca supports std::unique_ptr<T>. Alpaca does not support raw pointers or shared pointers at the moment. Using unique pointers, recursive data structures, e.g., tree structures, can be easily modeled and serialized. See below for an example:

Source

template <class T> 
struct Node {
  T data;
  std::unique_ptr<Node<T>> left;
  std::unique_ptr<Node<T>> right;
};

template <class T>
auto make_node(T const &value, std::unique_ptr<Node<T>> lhs = nullptr,
               std::unique_ptr<Node<T>> rhs = nullptr) {
  return std::unique_ptr<Node<T>>(
      new Node<T>{value, std::move(lhs), std::move(rhs)});
}

/*
  Binary Tree:
        5
       / \
      3   4
     / \
    1   2
*/

auto const root = make_node(
    5, 
    make_node(
        3, 
        make_node(1), 
        make_node(2)
    ), 
    make_node(4)
);  

// serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(*root, bytes); // 15 bytes

// bytes:
// {
//   0x05 // root = 5
//   0x01 // 5.has_left = true
//   0x03 // 5.left = 3
//   0x01 // 3.has_left = true
//   0x01 // 3.left = 1
//   0x00 // 1.left = null
//   0x00 // 1.right = null
//   0x01 // 3.has_right = true
//   0x02 // 3.right = 2
//   0x00 // 2.has_left = false
//   0x00 // 2.has_right = false
//   0x01 // 5.has_right = true
//   0x04 // 5.right = 4
//   0x00 // 4.has_left = false
//   0x00 // 4.has_right = false
// }

For std::unique_ptr<T>, a leading byte is used to represent if the pointer is nullptr

ptr != null?  value (if previous byte is 0x01)          
+----------+  +----+----+----+-----+
|    A1    |  | B1 | B2 | B3 | ... |
+----------+  +----+----+----+-----+

Timestamps and Durations

alpaca supports std::chrono::duration<Rep, Period> type, including std::chrono::milliseconds and the like. The Rep arithmetic value is serialized and the duration is reconstructed during deserialization

#include <alpaca/alpaca.h>
using namespace alpaca;

int main() {

  struct MyStruct {
    std::chrono::milliseconds period;
  };

  MyStruct s{std::chrono::milliseconds{500}};

  // Serialize
  std::vector<uint8_t> bytes;
  auto bytes_written = alpaca::serialize(s, bytes);

  // Deserialize
  std::error_code ec;
  auto recovered = alpaca::deserialize<MyStruct>(bytes, ec);
  // period == 500ms
}

Additionally, std::time_t can be used to store timestamps. Although not defined by the C standard, this is almost always an integral value holding the number of seconds (not counting leap seconds) since 00:00, Jan 1 1970 UTC, corresponding to POSIX time.

#include <alpaca/alpaca.h>
using namespace alpaca;

int main() {

  struct MyStruct {
    std::time_t timestamp;
  };

  auto timestamp = std::chrono::system_clock::to_time_t(
                        std::chrono::system_clock::now());

  MyStruct s{timestamp};

  constexpr auto OPTIONS = options::big_endian | 
                           options::fixed_length_encoding;

  // Serialize
  std::vector<uint8_t> bytes;
  auto bytes_written = alpaca::serialize<OPTIONS>(s, bytes);

  // bytes: {0x00 0x00 0x00 0x00 0x63 0x13 0xeb 0x21}

  // Deserialize
  std::error_code ec;
  auto recovered = alpaca::deserialize<OPTIONS, MyStruct>(bytes, ec);

  // timestamp: 1662249761
  //
  // Human time:
  // GMT: Sunday, September 4, 2022 12:02:41 AM
}

Saving/Loading to/from files

alpaca supports directly writing to files instead of using intermediate buffers. Serialize to files using std::ofstream and deserialize from files using std::ifstream objects. For deserialization, the size of the file must be provided as an argument:

Source

#include <alpaca/alpaca.h>
#include <filesystem>
using namespace alpaca;

struct GameState {
  int a;
  bool b;
  char c;
  std::string d;
  std::vector<uint64_t> e;
  std::map<std::string, std::array<uint8_t, 3>> f;
};

int main() {

  GameState s{5,
              true,
              'a',
              "Hello World",
              {6, 5, 4, 3, 2, 1},
              {{"abc", {1, 2, 3}}, {"def", {4, 5, 6}}}};

  const auto filename = "savefile.bin";

  {
    // Serialize to file
    std::ofstream os;
    os.open(filename, std::ios::out | std::ios::binary);
    auto bytes_written = serialize(s, os);
    os.close();

    assert(bytes_written == 37);
    assert(std::filesystem::file_size(filename) == 37);
  }

  {
    // Deserialize from file
    auto size = std::filesystem::file_size(filename);
    std::error_code ec;
    std::ifstream is;
    is.open(filename, std::ios::in | std::ios::binary);
    auto recovered = deserialize<GameState>(is, size, ec);
    is.close();

    assert(recovered.a == s.a);
    assert(recovered.b == s.b);
    assert(recovered.c == s.c);
    assert(recovered.d == s.d);
    assert(recovered.e == s.e);
    assert(recovered.f == s.f);
  }
}
pranav@ubuntu:~/dev/alpaca/build$ hexdump -C savefile.bin 
00000000  05 01 61 0b 48 65 6c 6c  6f 20 57 6f 72 6c 64 06  |..a.Hello World.|
00000010  06 05 04 03 02 01 02 03  61 62 63 01 02 03 03 64  |........abc....d|
00000020  65 66 04 05 06                                    |ef...|
00000025

Add custom type serialization

Not all types are supported by this library, but you can easily define serialization for custom types from other libraries. To do this, you need to create header file, in which define: type_info, to_bytes and from_bytes methods, and in the end of this file include <alpaca/alpaca.h>. After that, use your header file, instead of alpaca one.

For example, we need to add serialization for type MyCustomType<typename T, typename U, int L>

#pragma once

#include <alpaca/detail/field_type.h>
#include <alpaca/detail/options.h>
#include <alpaca/detail/aggregate_arity.h>

#include <system_error>
#include <unordered_map>
#include <vector>

namespace alpaca {

namespace detail {


template <typename T> struct is_my_custom_type : std::false_type {};

template <typename T, typename U, int L>
struct is_my_custom_type<MY_CUSTOM_TYPE<T, U, L>> : std::true_type {};

template <typename T>
typename std::enable_if<is_my_custom_type<T>::value, void>::type
type_info(
    std::vector<uint8_t> &typeids,
    std::unordered_map<std::string_view, std::size_t> &struct_visitor_map) {

    // Run type_info for inner types
	type_info<typename T::T>(typeids, struct_visitor_map);
	type_info<typename T::U>(typeids, struct_visitor_map);
}

template <options O, typename Container, typename T, typename U, int L>
void to_bytes(Container &bytes, std::size_t &byte_index, const MY_CUSTOM_TYPE<T, U, L> &input) {
    // implement to bytes
}

template <options O, typename T, typename U, int L, typename Container>
bool from_bytes(MY_CUSTOM_TYPE<T, U, L> &output, Container &bytes, std::size_t &byte_index, std::size_t &end_index,
                std::error_code &error_code) {
    // implement from bytes
    return true;
}
}  // namespace detail
}  // namespace alpaca

#include <alpaca/alpaca.h>

Backward and Forward Compatibility

  • A change made to a system or technology in such a way that the existing users are unaffected is a backward compatible change. The obvious advantage is that the existing users have a non-time sensitive and a graceful way of upgrading their integrations. On the other hand, a non backward-compatible change breaks the existing integrations and forces the existing users to deal with an immediate fix.
  • Forward compatibility, on the other hand is the ability of a system to process input meant for a later version of the system. A message/standard/library/tool (ex: alpaca) supports forward compatibility if an implementation (ex: a service built on alpaca) that uses an older version of the message processes a future version of the message.

Tips while changing alpaca message struct definitions:

  • Do not change the order or type of existing fields in the struct. This will break the design considerations meant for backward and forward compatibility.
  • Do not remove a field right away if it is not being used anymore. Mark it as deprecated and have a timeline to completely remote it, thereby giving the integrated applications time to flexibly remove the dependency on that field.
  • Add new fields for newer implementations and deprecate older fields in a timely way.
  • Adding fields is always a safe option as long as you manage them and don't end up with too many of them.

Consider an RPC interaction pattern where a client sends a message to a server.

Here's the first version of the message struct:

struct my_struct {
  int old_field_1;
  float old_field_2;
};

Case 1: Client-side is updated to use a newer version of the message struct

In the scenario where the client side is updated to use a newer version of the struct, which includes a string new_field_1. The server side will receive and deserialize this newer version of the message, even though it is compiled to unpack the older version. This is expected to work as long as the newer version simply has more fields. Changes to existing fields, e.g., if int was changed to int8_t, may or may not work depending on the data.

std::vector<uint8_t> bytes;
{
    // client side is updated to use a newer version
    struct my_struct {
        int old_field_1;
        float old_field_2;
        std::string new_field_1;
    };

    my_struct s{5, 3.14f, "Hello"};
    auto bytes_written = alpaca::serialize(s, bytes);
}

{
    // server side is still compiled to deserialize the older version of the struct
    struct my_struct {
        int old_field_1;
        float old_field_2;
    };
    std::error_code ec;
    auto s = deserialize<my_struct>(bytes, ec);
    assert((bool)ec == false);
    assert(s.old_field_1 == 5);
    assert(s.old_field_2 == 3.14f);
}

Case 2: Server-side is updated to use a newer version of the message struct

In this scenario, the server-side is updated to use a newer version of the struct, accepting 3 additional fields: a string, a vector, and an integer. The client-side is still compiled with the older version of the struct. When the message is deserialized on the server side, the server will construct the newer version of the struct, fill out the fields that are available in the input, and default initialize the rest of the fields.

std::vector<uint8_t> bytes;
{
    // client side is using an old structure
    struct my_struct {
        int old_field_1;
        float old_field_2;
    };

    my_struct s{5, 3.14f};
    auto bytes_written = alpaca::serialize(s, bytes);
}
    
{
    // server side is updated to use a new structure
    struct my_struct {
        int old_field_1;
        float old_field_2;
        std::string new_field_1;
        std::vector<bool> new_field_2;
        int new_field_3;
    };
    std::error_code ec;
    auto s = deserialize<my_struct>(bytes, ec);
    assert((bool)ec == false);
    assert(s.old_field_1 == 5);
    assert(s.old_field_2 == 3.14f);
    assert(s.new_field_1.empty()); // default initialized
    assert(s.new_field_2.size() == 0); // default initialized
    assert(s.new_field_3 == 0); // default initialized
}

Configuration Options

Endianness

By default, alpaca uses little endian. This option can be switched using options::big_endian

#include <alpaca/alpaca.h>
using namespace alpaca;

int main() {
  struct my_struct {
    uint16_t id;
  };

  my_struct s { 12345 };
  
  // little endian
  {
    std::vector<uint8_t> bytes;
    auto bytes_written = serialize(s, bytes); // {0x39, 0x30}
  }

  // big endian
  {
    std::vector<uint8_t> bytes;
    constexpr auto OPTIONS = options::big_endian;
    auto bytes_written = serialize<OPTIONS>(s, bytes); // {0x30, 0x39}
  }  
}

Fixed or Variable-length Encoding

By default, large integer types (32 and 64-bit values), e.g., int32_t, uint64_t are encoded as variable-length quantities (VLQ).

This can be changed with alpaca::options::fixed_length_encoding. In fixed-length encoding, an uint32_t will take up 4 bytes.

#include <alpaca/alpaca.h>
using namespace alpaca;

int main() {

  struct MyStruct {
    uint32_t value;
  };
  MyStruct s{5};

  // Variable-length encoding
  {
    std::vector<uint8_t> bytes;
    auto bytes_written = serialize(s, bytes); // {0x05}
  }

  // Fixed-length encoding
  {
    std::vector<uint8_t> bytes;
    constexpr auto OPTIONS = options::fixed_length_encoding;
    auto bytes_written = serialize<OPTIONS>(s, bytes); // {0x05, 0x00, 0x00, 0x00}
  }

  // Fixed-length encoding in big endian
  {
    std::vector<uint8_t> bytes;
    constexpr auto OPTIONS = options::fixed_length_encoding |
                             options::big_endian;
    auto bytes_written = serialize<OPTIONS>(s, bytes); // {0x00, 0x00, 0x00, 0x05}
  }
}

VLQ for Unsigned integers

  • uint8_t and uint16_t are stored as-is without any encoding.
  • uint32_t and uint64_t are represented as variable-length quantities (VLQ) with 7-bits for data and 1-bit to represent continuation
First OctetSecond Octet
7654321076543210
2⁷2⁶2⁵2⁴2⁰2⁷2⁶2⁵2⁴2⁰
AB₀ABₙ (n > 0)
  • If A is 0, then this is the last VLQ octet of the integer. If A is 1, then another VLQ octet follows.

VLQ for Signed integers

  • int8_t and int16_t are stored as-is without any encoding.
  • int32_t and int64_t are represented as VLQ, similar to the unsigned version. The only difference is that the first VLQ has the sixth bit reserved to indicate whether the encoded integer is positive or negative. Any consecutive VLQ octet follows the general structure.
First OctetSecond Octet
7654321076543210
2⁷2⁶2⁵2⁴2⁰2⁷2⁶2⁵2⁴2⁰
ABC₀BCₙ (n > 0)
  • If A is 0, then the VLQ represents a positive integer. If A is 1, then the VLQ represents a negative number.
  • If B is 0, then this is the last VLQ octet of the integer. If B is 1, then another VLQ octet follows.

Data Structure Versioning

alpaca provides a type-hashing mechanism to encode the version the aggregate class type as a uint32_t. This hash can be added to the output using alpaca::options::with_version. The type hash includes the number of fields in the struct, the sizeof(T) for the struct, an ordered list of the type of each field. This information is encoded into a bytearray and then a checksum is generated for those bytes.

During deserialization, the same type hash is calculated and compared against the input. In case of a mismatch, the error code is set.

std::vector<uint8_t> bytes;

// serialize
{
  struct MyStruct {
    int a;
  };

  MyStruct s{5};
  std::vector<uint8_t> bytes;
  auto bytes_written = serialize<options::with_version>(s, bytes);
}

// deserialize
{
  struct MyStruct {
    int a;
    float b;
    char c;
  };

  std::error_code ec;
  auto object = deserialize<options::with_version, MyStruct>(bytes, ec);
  // ec.value() == std::errc::invalid_argument here
}

Integrity Checking with Checksums

In addition to type hashing, checksums can be added to the end of the output using options::with_checksum. This will generate a CRC32 checksum for all the bytes in the serialized output and then append the four additional bytes to the end of the output.

struct MyStruct {
  char a;
  uint16_t b;
  float c;
};

MyStruct s{'m', 54321, -987.654};
	
std::vector<uint8_t> bytes;

// Serialize and append CRC32 hash
constexpr auto OPTIONS = options::with_checksum;
auto bytes_written = serialize<OPTIONS>(s, bytes); // 11 bytes

// Check CRC32 hash and deserialize
std::error_code ec;
auto object = deserialize<OPTIONS, MyStruct>(bytes, ec);
if (!ec) {
  // use object
}

// bytes:
// {
//   0x6d                   // char 'm'
//   0x31 0xd4              // uint 54321
//   0xdb 0xe9 0x76 0xc4    // float -987.654
//   0xa4 0xf2 0x54 0x76    // crc32 1985278628
// }
//
// crc32({6d,31,d4,db,e9,76,c4}) => 1985278628
// source: https://crccalc.com/

Macros to Exclude STL Data Structures

alpaca includes headers for a number of STL containers and classes. As this can affect the compile time of applications, define any of the following macros to remove support for particular data structures.

#define ALPACA_EXCLUDE_SUPPORT_STD_ARRAY
#define ALPACA_EXCLUDE_SUPPORT_STD_CHRONO
#define ALPACA_EXCLUDE_SUPPORT_STD_DEQUE
#define ALPACA_EXCLUDE_SUPPORT_STD_LIST
#define ALPACA_EXCLUDE_SUPPORT_STD_MAP
#define ALPACA_EXCLUDE_SUPPORT_STD_OPTIONAL
#define ALPACA_EXCLUDE_SUPPORT_STD_SET
#define ALPACA_EXCLUDE_SUPPORT_STD_STRING
#define ALPACA_EXCLUDE_SUPPORT_STD_TUPLE
#define ALPACA_EXCLUDE_SUPPORT_STD_PAIR
#define ALPACA_EXCLUDE_SUPPORT_STD_UNIQUE_PTR
#define ALPACA_EXCLUDE_SUPPORT_STD_UNORDERED_MAP
#define ALPACA_EXCLUDE_SUPPORT_STD_UNORDERED_SET
#define ALPACA_EXCLUDE_SUPPORT_STD_VARIANT
#define ALPACA_EXCLUDE_SUPPORT_STD_VECTOR

Here's an example that only uses std::vector, std::unordered_map, and std::string

#define ALPACA_EXCLUDE_SUPPORT_STD_ARRAY
#define ALPACA_EXCLUDE_SUPPORT_STD_MAP
#define ALPACA_EXCLUDE_SUPPORT_STD_UNIQUE_PTR
#define ALPACA_EXCLUDE_SUPPORT_STD_OPTIONAL
#define ALPACA_EXCLUDE_SUPPORT_STD_SET
#define ALPACA_EXCLUDE_SUPPORT_STD_TUPLE
#define ALPACA_EXCLUDE_SUPPORT_STD_UNORDERED_SET
#define ALPACA_EXCLUDE_SUPPORT_STD_PAIR
#define ALPACA_EXCLUDE_SUPPORT_STD_VARIANT
#include <alpaca/alpaca.h>
using namespace alpaca;

int main() {
  struct my_struct {
    uint16_t id;
    std::vector<char> alphabet;
    std::unordered_map<std::string, int> config;
  };

  my_struct s {12345,
	       {'a', 'b', 'c'},
	       {{"x", -20}, {"y", 45}}};
  
  std::vector<std::uint8_t> bytes;
  auto bytes_written = serialize<options::fixed_length_encoding>(s, bytes);
}

Aligned Memory Access

The Alpaca library, by default, utilizes unaligned memory access, as this is permitted on the x86_64 architecture. However, certain architectures, such as the Arm Cortex-M3, M4, and M33, require aligned memory access for 32-bit and 64-bit data types.

For architectures where aligned memory access is necessary, the Alpaca library includes the options::force_aligned_access option. When this option is enabled, the library will not perform unaligned accesses and will use memcpy instead.

Python Interoperability

alpaca comes with an experimental pybind11-based Python wrapper called pyalpaca. To build this wrapper, include the option -DALPACA_BUILD_PYTHON_LIB=on with cmake.

Instead of providing a struct type, the user will provide a string specification of the fields. This is inspired by the standard Python struct module.

Usage

# Serialize
def serialize(format_string, list_of_values) -> bytes

# Deserialize
def deserialize(format_string, bytes) -> list_of_values

Format String Specification

Code Type
? bool
c char
b int8_t
B uint8_t
h int16_t
H uint16_t
i int32_t
I uint32_t
q int64_t
Q uint64_t
f float
d double
N std::size_t
s std::string
[T] std::vector<T>
[NT] std::array<N,T>
{K:V} std::unordered_map<K, V>
{T} std::unordered_set<T>
(T, U, ...) std::tuple<T, U, ...>

Example 1: Serialize and Deserialize in Python

Once the wrapper is built, simply add it to PYTHONPTAH and import pyalpaca.

import pyalpaca

# Create format string
format = '?cifs[i][[d]][3c]{c:i}{I}(cif)(s(dI))'

# Construct object
object = [
    False, 
    'a', 
    5, 
    3.14, 
    "Hello World!",
    [0, 1, 2, 3], 
    [[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]],
    ['a', 'b', 'c'],
    {'a': 5, 'b': 19},
    {1, 1, 1, 2, 3, 4, 5, 5, 5, 5, 6},
    ('a', 45, 2.718),
    ("Hello", (39.456, 21))
]

# Serialize
bytes = pyalpaca.serialize(format, object)

# Print it
print("Bytes:")
hex_values = ["0x{:02x}".format(b) for b in bytes]
for i, h in enumerate(hex_values):
    if i > 0 and i % 8 == 0:
        print("\n  ", end="")
    elif i == 0 and i % 8 == 0:
        print("  ", end="")
    print(h, end=" ")
print()

# Deserialize
recovered = pyalpaca.deserialize(format, bytes)

# Print it
print("\nDeserialized:\n[ ")
for i in recovered:
    print("    " + str(i) + ",")
print("]")
pranav@ubuntu:~/dev/alpaca/build/python$ python3 test.py
Bytes:
  0x00 0x61 0x05 0xc3 0xf5 0x48 0x40 0x0c 
  0x48 0x65 0x6c 0x6c 0x6f 0x20 0x57 0x6f 
  0x72 0x6c 0x64 0x21 0x04 0x00 0x01 0x02 
  0x03 0x03 0x02 0x9a 0x99 0x99 0x99 0x99 
  0x99 0xf1 0x3f 0x9a 0x99 0x99 0x99 0x99 
  0x99 0x01 0x40 0x02 0x66 0x66 0x66 0x66 
  0x66 0x66 0x0a 0x40 0x9a 0x99 0x99 0x99 
  0x99 0x99 0x11 0x40 0x02 0x00 0x00 0x00 
  0x00 0x00 0x00 0x16 0x40 0x66 0x66 0x66 
  0x66 0x66 0x66 0x1a 0x40 0x61 0x62 0x63 
  0x02 0x61 0x05 0x62 0x13 0x06 0x01 0x02 
  0x03 0x04 0x05 0x06 0x61 0x2d 0xb6 0xf3 
  0x2d 0x40 0x05 0x48 0x65 0x6c 0x6c 0x6f 
  0xee 0x7c 0x3f 0x35 0x5e 0xba 0x43 0x40 
  0x15 

Deserialized:
[ 
    False,
    a,
    5,
    3.140000104904175,
    Hello World!,
    [0, 1, 2, 3],
    [[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]],
    ['a', 'b', 'c'],
    {'a': 5, 'b': 19},
    {1, 2, 3, 4, 5, 6},
    ('a', 45, 2.7179999351501465),
    ('Hello', (39.456, 21)),
]

Example 2: Serialize in C++ and Deserialize in Python

Serialize a GameState to file in C++

#include <alpaca/alpaca.h>
#include <filesystem>
using namespace alpaca;

struct GameState {
  int a;
  bool b;
  char c;
  std::string d;
  std::vector<uint64_t> e;
  std::map<std::string, std::array<uint8_t, 3>> f;
};

int main() {

  GameState s{5,
              true,
              'a',
              "Hello World",
              {6, 5, 4, 3, 2, 1},
              {{"abc", {1, 2, 3}}, {"def", {4, 5, 6}}}};

  const auto filename = "savefile.bin";

  {
    // Serialize to file
    std::ofstream os;
    os.open(filename, std::ios::out | std::ios::binary);
    auto bytes_written = serialize(s, os);
    os.close();

    assert(bytes_written == 37);
    assert(std::filesystem::file_size(filename) == 37);
  }
}
pranav@ubuntu:~/dev/alpaca/build/python$ hexdump -C savefile.bin 
00000000  05 01 61 0b 48 65 6c 6c  6f 20 57 6f 72 6c 64 06  |..a.Hello World.|
00000010  06 05 04 03 02 01 02 03  61 62 63 01 02 03 03 64  |........abc....d|
00000020  65 66 04 05 06                                    |ef...|
00000025

Now one can deserialize this file in Python by simply adding the format string to match the C++ struct:

import pyalpaca

# Read file
with open("savefile.bin", "rb") as file:
    bytes = file.read()

    # Format string
    format = "i?cs[Q]{s:[3B]}"

    # Deserialize
    recovered = pyalpaca.deserialize(format, bytes)

    # Print it
    print("\nDeserialized:\n[ ")
    for i in recovered:
        print("    " + str(i) + ",")
    print("]")
pranav@ubuntu:~/dev/alpaca/build/python$ python3 test.py 

Deserialized:
[ 
    5,
    True,
    a,
    Hello World,
    [6, 5, 4, 3, 2, 1],
    {'abc': [1, 2, 3], 'def': [4, 5, 6]},
]

Performance Benchmarks

Last updated: 2022-09-13

All tests benchmark the following properties (time or size):

  • Serialize: serialize data into a buffer
  • Deserialize: deserializes a buffer into a C++ struct object
  • Size: the size of the buffer when serialized

System Details

Type Value
Processor 11th Gen Intel(R) Core(TM) i9-11900KF @ 3.50GHz 3.50 GHz
Installed RAM 32.0 GB (31.9 GB usable)
SSD ADATA SX8200PNP
OS Ubuntu 20.04 LTS running on VMWare Player
C++ Compiler g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0

The tests cover three example scenarios:

  • Log: This data set is composed of HTTP request logs that are small and contain many strings.
  • Triangle Mesh: This data set is a single mesh. The mesh contains an array of triangles, each of which has three vertices and a normal vector.
  • Minecraft Save: This data set is composed of Minecraft player saves that contain highly structured data.
Test Name Count Serialize Deserialize Size
Log 10,000 logs 432.95 us 2.27 ms 850.52 KB
Triangle Mesh 125,000 triangles 777.96 us 2.37 ms 6.00 MB
Minecraft Save 50 players 71.54 us 321.10 us 149.05 KB

Building, Installing, and Testing

# Clone
git clone --recurse-submodules https://github.com/p-ranav/alpaca
cd alpaca

# Build
mkdir build
cd build
cmake -DALPACA_BUILD_TESTS=on \
      -DALPACA_BUILD_BENCHMARKS=on \
      -DALPACA_BUILD_SAMPLES=on \
      -DCMAKE_BUILD_TYPE=Release ..
make

# Test
./test/tests

# Install 
make install

CMake Integration

Use the latest alpaca in your CMake project without copying any content.

cmake_minimum_required(VERSION 3.11)

PROJECT(myproject)

# fetch latest argparse
include(FetchContent)
FetchContent_Declare(
    alpaca
    GIT_REPOSITORY https://github.com/p-ranav/alpaca.git
)
FetchContent_MakeAvailable(alpaca)

add_executable(myproject main.cpp)
target_link_libraries(myproject alpaca)

Supported Toolchains

alpaca has been tested on the following toolchains (see actions).

Compiler Standard Library Test Environment
AppleClang >= 13.0.0.13000029 libc++ Xcode 13.2.1
Clang >= 11.0.0 libstdc++ Ubuntu 20.04
GCC >= 9.4.0 libstdc++ Ubuntu 20.04
MSVC >= 19.33.31629.0 Microsoft STL Visual Studio 17 2022

Contributing

Feel free to contribute to this project. Issues and PRs welcome.

License

The project is available under the MIT license.

alpaca's People

Contributors

f-michaut avatar finger563 avatar ghostvaibhav avatar lolpie244 avatar lrodorigo-apo avatar p-ranav avatar regardsim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alpaca's Issues

Are structs containing functions serializable?

Thank you for the library! Question: is it guaranteed that, when feeding a given struct with functions, only the member variables will be serialized? If so, does the function declaration position matter?

Use case: a verify() function throwing a boolean after some sanity checks in the struct data.

@p-ranav

Bitfield support

As far as I can make out with my tests there is no support for bitfield struct serialization in Alpaca. Is there any way to achieve this, and give a standardized method to implement it for a large number of structs.

Is this library still maintained?

The commit history seems to indicate that the library is not even more maintainedo does it works almost perfectly and additional features are not developed anymore?

Variable length encoding fails the simpler test

The source code is quite self-explaining.
The code is properly working if the fixed-length econding is enable.
Linux - GCC v 14.1.1 - Current master branch.
Really scaring.

#include "alpaca/alpaca.h"

struct Simple {
    uint32_t field {0};
};

int main() {
    Simple data {0x12345678};

    std::vector<uint8_t> bytes;
    auto size = alpaca::serialize(data, bytes);

    std::error_code err;
    auto reconstructed = alpaca::deserialize<Simple>(bytes, err);

    assert(!err);
    
    assert(reconstructed.field == data.field); // And here it dies.
}

Linker error with std::filesystem::path

Hello, I am trying to serialize a class with std::filesystem::path data members.

I am getting the following linker error :

/usr/bin/ld: mylib.so: undefined reference to `std::enable_if<(!(std::is_aggregate_v<std::filesystem::__cxx11::path>))&&(std::is_class_v<std::filesystem::__cxx11::path>), void>::type alpaca::detail::to_bytes<(alpaca::options)8, std::vector<unsigned char, std::allocator<unsigned char> >, std::filesystem::__cxx11::path>(std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned long&, std::filesystem::__cxx11::path const&)'

Since alpaca is header-only and heavily using templates, I don't really understand why I can have an undefined reference error.
I would expect compilation errors if the template cannot be instanciated, but then if the template can be instanciated I should not have linker errors ?

How can I resolve this error ?

Serialization does not work between 64 bit and 32 bit architectures

When fixed-length encoding is enabled (which is mandatory to avoid breaking integer serialization as noted in issue #40), the string length and various other types (such as array lengths) are serialized using size_t.

Unfortunately, this results in 8-byte serialization on 64-bit architectures and 4-byte serialization on 32-bit architectures.

If a message is serialized on an x86_64 architecture and then deserialized on a 32-bit architecture (e.g., an ARMv7), the result is completely corrupted because the sizeof(std::size_t) differs between the two platforms.

Needs to embed a fake struct to serialize direct STL types

This works:
constexpr auto OPTIONS = alpaca::options::with_checksum | alpaca::options::fixed_length_encoding; using serialize_t = std::map<int,bool>; auto Serialize(const serialize_t& to_serialize) { std::vector<uint8_t> buffer; struct MyStruct { serialize_t field; }; const MyStruct& object{to_serialize}; if(auto bytes_written=alpaca::serialize<OPTIONS>(object,buffer)) return std::make_pair(buffer,true); else return std::make_pair(buffer,false); };

This does not:

constexpr auto OPTIONS = alpaca::options::with_checksum | alpaca::options::fixed_length_encoding; using serialize_t = std::map<int,bool>; auto Serialize(const serialize_t& to_serialize) { std::vector<uint8_t> buffer; if(auto bytes_written=alpaca::serialize<OPTIONS>(to_serialize,buffer)) return std::make_pair(buffer,true); else return std::make_pair(buffer,false); };

Would be great to be able to use it without using MyStruct.

Docs: Add example of own type serealization

Unfortunately, not all types are supported by this library. The one example is glm vectors. So it would be useful to have guide how to add own types.

@p-ranav, If you don't mind, I will be able to add serealization of glm vector as an example

[Question] Update release version

What do you think about creating new release version? The last one was at the september of 2022.

New release version is needed to update version on conan

Big-endian support?

I'm not sure if I'm reading the code correctly, but it seems like alpaca always reads data in little-endian order, and sets members assuming the target system is little endian. Is alpaca endian-agnostic or does it only support little-endian?

(De)serialize from C-style pointer without copy

It is not always possible to use std::vector, std::array, or c_array[] style types. Right now, deserialization from raw pointer (uint8_t*) requires a copy.

example:

MyStruct raw_pointer_deserialize(const uint8_t* ptr) {
  std::error_code ec;
  return deserialize<MyStruct>((const uint8_t*)ptr, sizeof(MyStruct), ec);
}

A version of from_bytes with std::is_pointer on the container and runtime size passed in may be all that is needed.

Qualification under older C++ standards

Hi thanks for your effort, I want to try this library but unfortunately I'm using an older C++ standard, so I was asking whether alpaca have been proven to be working with any older C++ standards (C++ 14)?

Deserialization of tuples does not work on g++11

It looks like tuple deserialization runs in a decltype(auto) bug in g++-11 as shown in this godbolt based on alpaca::detail::get.

A workaround seems to be to change the return type of alpaca::detail::get from decltype(auto) to auto & and change the bare return statements (that should never be call) to return a dummy int &. While ugly, since Since g++-11 is installed by default on Ubuntu 22.04 this is probably worth putting in the workaround.

Serializing unsupported types

Thanks for this awesome libs.

My question is how can I serialize custom types? My struct is using absl::int128 (abseil) and I want to be able to serialize it.
Is there a way to specialize the function that does the serialization for adding unsupported one? Just like nlohmann::json does

(DE)serialize class objects

Is there any way (samples ?) that this library can be used to serialize and deserialize C++ class objects (as compared to structs) ?

Integer value problem

I copyed the sample code and just change the value of c to UINT64_MAX, the assert failed.

#include <alpaca/alpaca.h>

int main() {

    struct MyStruct {
        char a;
        int b;
        uint64_t c;
        float d;
        bool e;
    };

    MyStruct s{ 'a', 5, UINT64_MAX, 3.14f, true };

    // Serialize
    std::vector<uint8_t> bytes;
    auto bytes_written = alpaca::serialize(s, bytes);

    // Deserialize
    std::error_code ec;
    auto recovered = alpaca::deserialize<MyStruct>(bytes, ec);
    assert((bool)ec == false);
    assert(recovered.a == 'a');
    assert(recovered.b == 5);
    assert(recovered.c == UINT64_MAX);
    assert(recovered.d == 3.14f);
    assert(recovered.e == true);
}

Is this a problem? My test environment is msvc2022.
I also tested the same code in Ubuntu 20.04.4 LTS and g++ version 9.4.0 , same result.

Problem serializing std::tuple

I am not able to get tuples to serialize using g++ 11.3.0 on ubuntu 22.04.

Here's my program

#include <alpaca/alpaca.h>
#include <tuple>
std::tuple<int, double> t(1, 2.3);

std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(t, bytes); 

which fails to compile as follows

g++ -std=c++17 -I../build/_deps/alpaca-src/include/ test_alpaca.cpp 
In file included from ../build/_deps/alpaca-src/include/alpaca/alpaca.h:9,
                 from test_alpaca.cpp:1:
../build/_deps/alpaca-src/include/alpaca/detail/struct_nth_field.h: In instantiation of ‘constexpr decltype(auto) alpaca::detail::get(type&) [with long unsigned int index = 0; type = const std::tuple<int, double>&; long unsigned int arity = 4]’:
../build/_deps/alpaca-src/include/alpaca/alpaca.h:139:60:   required from ‘void alpaca::detail::serialize_helper(const T&, Container&, std::size_t&) [with alpaca::options O = alpaca::options::none; T = std::tuple<int, double>; long unsigned int N = 4; Container = std::vector<unsigned char>; long unsigned int I = 0; std::size_t = long unsigned int]’
../build/_deps/alpaca-src/include/alpaca/alpaca.h:156:62:   required from ‘std::size_t alpaca::serialize(const T&, Container&) [with T = std::tuple<int, double>; long unsigned int N = 4; Container = std::vector<unsigned char>; std::size_t = long unsigned int]’
test_alpaca.cpp:8:39:   required from here
../build/_deps/alpaca-src/include/alpaca/detail/struct_nth_field.h:41:11: error: 4 names provided for structured binding
   41 |     auto &[p1, p2, p3, p4] = value;
      |           ^~~~~~~~~~~~~~~~
../build/_deps/alpaca-src/include/alpaca/detail/struct_nth_field.h:41:11: note: while ‘const std::tuple<int, double>’ decomposes into 2 elements

Any thoughts appreciated.
Thanks,
Mike

Failed to compile for struct with constructors which have more than 1 arguments

The following 3 cases could pass compile

struct Foo1 {
    int data;
};

struct Foo2 {
    int data;
    Foo2() = default;
};

struct Foo3 {
    int data;
    Foo3(const int data): data{data} {}
};

But once the constructor accepts more than one arugments, we will get an error, like this:

struct Foo4 {
    int data;
    Foo4() = default; // with or without it just lead to the same error
    Foo4(const int data, const bool bar): data{data} {}
}
FAILED: CMakeFiles/symusic.dir/src/io/alpaca.cpp.obj 
C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\MSVC\1438~1.331\bin\Hostx64\x64\cl.exe  /nologo /TP -DFMT_HEADER_ONLY=1 -IC:\Users\nhy\symusic-libra\include -IC:\Users\nhy\symusic-libra\3rdparty\minimidi\include -IC:\Users\nhy\symusic-libra\3rdparty\pdqsort -IC:\Users\nhy\symusic-libra\3rdparty\zpp_bits -IC:\Users\nhy\symusic-libra\3rdparty\fmt\include -IC:\Users\nhy\symusic-libra\3rdparty\alpaca\include /DWIN32 /D_WINDOWS /EHsc /Zi /Ob0 /Od /RTC1 -std:c++20 -MDd /GL -bigobj /showIncludes /FoCMakeFiles\symusic.dir\src\io\alpaca.cpp.obj /FdCMakeFiles\symusic.dir\symusic.pdb /FS -c C:\Users\nhy\symusic-libra\src\io\alpaca.cpp
C:\Users\nhy\symusic-libra\3rdparty\alpaca\include\alpaca/detail/struct_nth_field.h(21): error C3448: the number of identifiers must match the number of array elements or members in a structured binding declaration
C:\Users\nhy\symusic-libra\3rdparty\alpaca\include\alpaca/detail/struct_nth_field.h(21): note: the template instantiation context (the oldest one first) is
C:\Users\nhy\symusic-libra\src\io\alpaca.cpp(104): note: see reference to function template instantiation 'std::vector<uint8_t,std::allocator<uint8_t>> symusic::details::serailize_alpaca<symusic::Foo4>(const symusic::Foo4 &)' being compiled
C:\Users\nhy\symusic-libra\src\io\alpaca.cpp(29): note: see reference to function template instantiation 'size_t alpaca::serialize<T,2,std::vector<uint8_t,std::allocator<uint8_t>>>(const T &,Container &)' being compiled
        with
        [
            T=symusic::Foo4,
            Container=std::vector<uint8_t,std::allocator<uint8_t>>
        ]
C:\Users\nhy\symusic-libra\3rdparty\alpaca\include\alpaca/alpaca.h(152): note: see reference to function template instantiation 'void alpaca::detail::serialize_helper<alpaca::options::none,T,2,std::vector<uint8_t,std::allocator<uint8_t>>,0>(const T &,Container &,size_t &)' being compiled
        with
        [
            T=symusic::Foo4,
            Container=std::vector<uint8_t,std::allocator<uint8_t>>
        ]
C:\Users\nhy\symusic-libra\3rdparty\alpaca\include\alpaca/alpaca.h(135): note: see reference to function template instantiation 'decltype(auto) alpaca::detail::get<0,const T&,2>(type) noexcept' being compiled
        with
        [
            T=symusic::Foo4,
            type=const symusic::Foo4 &
        ]
ninja: build stopped: subcommand failed.

Note that I'm using c++20 and msvc 19.38.33133.0 on windows11. And I have read the similar issue #24

no matching function for call to 'to_bytes'

template<typename F>
void RegisterHandle(std::string Name, F f) {
using FirstArgType = first_arg_t<F>;
using SecondArgType = second_arg_t<F>;

auto fun = [f](TCP_SOCKET* Socket,std::vector<uint8_t>& bytes) -> int {

	auto rep = SecondArgType{};
	rep.ec = static_cast<std::errc>(0);

	auto req = alpaca::deserialize<FirstArgType>(bytes, rep.ec);

	if (static_cast<int>(rep.ec) == 0)
		rep.status = f(req, rep);

	bytes.clear();
	alpaca::serialize(rep, bytes);
	Socket->send(bytes);
	
};

Methods[Name] = fun;

}

The above is sample code

cross-compilation for embedded bare-metal environment

Hi @p-ranav ,

Would you be able to provide a list of instructions for cross-compiling to bare metal platform?

I'm using the following toolchain:

GNU Arm Embedded Toolchain 2021.07
Pre-built GNU toolchain for Arm Cortex-A/R and Cortex-M processors
GCC Version: 10.3

With this toolchain I get the following error:

./include/alpaca/detail/crc32.hpp:73:2: error: #error unsupported system 73 | #error unsupported system

Thanks!

samples/optional_values.cpp: compile error: no matching function for call to 'to_bytes'

I wanted to give this a try, downloaded and failed to compile 😞 :
(MacBook Pro M2, macOS Sonoma)

$ cmake -DALPACA_BUILD_TESTS=on       -DALPACA_BUILD_BENCHMARKS=on       -DALPACA_BUILD_SAMPLES=on     -DCMAKE_BUILD_TYPE=Release ..
-- The CXX compiler identification is AppleClang 15.0.0.15000100
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/usr/bin/g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The C compiler identification is AppleClang 15.0.0.15000100
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/usr/bin/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Building benchmark
-- Could NOT find benchmark (missing: benchmark_DIR)
-- Using CMake Version 3.24.4
-- Downloading GoogleBenchmark
-- Found Git: /usr/bin/git (found version "2.39.3 (Apple Git-145)") 
-- git Version: v1.5.2
-- Version: 1.5.2
-- Performing Test HAVE_CXX_FLAG_STD_CXX11
-- Performing Test HAVE_CXX_FLAG_STD_CXX11 - Success
-- Performing Test HAVE_CXX_FLAG_WALL
-- Performing Test HAVE_CXX_FLAG_WALL - Success
-- Performing Test HAVE_CXX_FLAG_WEXTRA
-- Performing Test HAVE_CXX_FLAG_WEXTRA - Success
-- Performing Test HAVE_CXX_FLAG_WSHADOW
-- Performing Test HAVE_CXX_FLAG_WSHADOW - Success
-- Performing Test HAVE_CXX_FLAG_WERROR
-- Performing Test HAVE_CXX_FLAG_WERROR - Success
-- Performing Test HAVE_CXX_FLAG_WSHORTEN_64_TO_32
-- Performing Test HAVE_CXX_FLAG_WSHORTEN_64_TO_32 - Success
-- Performing Test HAVE_CXX_FLAG_FSTRICT_ALIASING
-- Performing Test HAVE_CXX_FLAG_FSTRICT_ALIASING - Success
-- Performing Test HAVE_CXX_FLAG_WNO_DEPRECATED_DECLARATIONS
-- Performing Test HAVE_CXX_FLAG_WNO_DEPRECATED_DECLARATIONS - Success
-- Performing Test HAVE_CXX_FLAG_WNO_DEPRECATED
-- Performing Test HAVE_CXX_FLAG_WNO_DEPRECATED - Success
-- Performing Test HAVE_CXX_FLAG_WSTRICT_ALIASING
-- Performing Test HAVE_CXX_FLAG_WSTRICT_ALIASING - Success
-- Performing Test HAVE_CXX_FLAG_WD654
-- Performing Test HAVE_CXX_FLAG_WD654 - Failed
-- Performing Test HAVE_CXX_FLAG_WTHREAD_SAFETY
-- Performing Test HAVE_CXX_FLAG_WTHREAD_SAFETY - Success
-- Performing Test HAVE_THREAD_SAFETY_ATTRIBUTES
-- Performing Test HAVE_THREAD_SAFETY_ATTRIBUTES
-- Performing Test HAVE_THREAD_SAFETY_ATTRIBUTES -- failed to compile
-- Performing Test HAVE_CXX_FLAG_COVERAGE
-- Performing Test HAVE_CXX_FLAG_COVERAGE - Success
-- Performing Test HAVE_STD_REGEX
-- Performing Test HAVE_STD_REGEX
-- Performing Test HAVE_STD_REGEX -- success
-- Performing Test HAVE_GNU_POSIX_REGEX
-- Performing Test HAVE_GNU_POSIX_REGEX
-- Performing Test HAVE_GNU_POSIX_REGEX -- failed to compile
-- Performing Test HAVE_POSIX_REGEX
-- Performing Test HAVE_POSIX_REGEX
-- Performing Test HAVE_POSIX_REGEX -- success
-- Performing Test HAVE_STEADY_CLOCK
-- Performing Test HAVE_STEADY_CLOCK
-- Performing Test HAVE_STEADY_CLOCK -- success
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Performing Test BENCHMARK_HAS_O3_FLAG
-- Performing Test BENCHMARK_HAS_O3_FLAG - Success
-- Performing Test BENCHMARK_HAS_CXX03_FLAG
-- Performing Test BENCHMARK_HAS_CXX03_FLAG - Success
-- Performing Test BENCHMARK_HAS_WNO_ODR
-- Performing Test BENCHMARK_HAS_WNO_ODR - Success
-- Configuring done
-- Generating done
-- Build files have been written to:alpaca/build
$ make
[  0%] Building CXX object test/CMakeFiles/ALPACA.dir/main.cpp.o
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3725:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(char, "%d")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3726:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(char signed, "%d")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3727:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3728:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int short, "%d")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3729:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3730:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int, "%d")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3731:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3732:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int long, "%ld")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3733:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3734:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/test/main.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3735:1: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu")
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/test/./doctest.hpp:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/stdio.h:180:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
11 warnings generated.
[  0%] Building CXX object test/CMakeFiles/ALPACA.dir/test_backwards_compatibility.cpp.o
[  1%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_array.cpp.o
[  1%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_big_endian.cpp.o
[  2%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_bool.cpp.o
[  2%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_bool_from_array.cpp.o
[  3%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_chrono_duration.cpp.o
[  3%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_chrono_duration_from_array.cpp.o
[  4%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_deque.cpp.o
[  4%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_deque_from_array.cpp.o
[  5%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_enum_class.cpp.o
[  5%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_enum_class_from_array.cpp.o
[  6%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_crc32.cpp.o
[  6%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_crc32_from_array.cpp.o
[  7%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_set.cpp.o
[  7%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_set_from_array.cpp.o
[  8%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_vector.cpp.o
[  8%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_vector_from_array.cpp.o
[  9%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_version.cpp.o
[  9%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_error_version_from_array.cpp.o
[ 10%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_float.cpp.o
[ 10%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_float_from_array.cpp.o
[ 11%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_from_carray.cpp.o
[ 11%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_from_ifstream.cpp.o
[ 12%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_int.cpp.o
[ 12%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_int_from_array.cpp.o
[ 13%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_list.cpp.o
[ 13%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_list_from_array.cpp.o
[ 14%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_map.cpp.o
[ 14%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_map_from_array.cpp.o
[ 15%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_optional.cpp.o
[ 15%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_optional_from_array.cpp.o
[ 16%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_pair.cpp.o
[ 16%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_pair_from_array.cpp.o
[ 16%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_set.cpp.o
[ 17%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_set_from_array.cpp.o
[ 17%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_string.cpp.o
[ 18%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_string_from_array.cpp.o
[ 18%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_tuple.cpp.o
[ 19%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_tuple_from_array.cpp.o
[ 19%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_uint.cpp.o
[ 20%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_uint_from_array.cpp.o
[ 20%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_unique_ptr.cpp.o
[ 21%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_unique_ptr_from_array.cpp.o
[ 21%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_ustring.cpp.o
[ 22%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_variant.cpp.o
[ 22%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_variant_from_array.cpp.o
[ 23%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_vector.cpp.o
[ 23%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_vector_from_array.cpp.o
[ 24%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_with_crc32.cpp.o
[ 24%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_with_crc32_from_array.cpp.o
[ 25%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_with_version.cpp.o
[ 25%] Building CXX object test/CMakeFiles/ALPACA.dir/test_deserialize_with_version_from_array.cpp.o
[ 26%] Building CXX object test/CMakeFiles/ALPACA.dir/test_forwards_compatibility.cpp.o
[ 26%] Building CXX object test/CMakeFiles/ALPACA.dir/test_forwards_compatibility_from_array.cpp.o
[ 27%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_array.cpp.o
[ 27%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_array_into_array.cpp.o
[ 28%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_big_endian.cpp.o
[ 28%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_big_endian_into_array.cpp.o
[ 29%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_bool.cpp.o
[ 29%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_bool_into_array.cpp.o
[ 30%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_chrono_duration.cpp.o
[ 30%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_chrono_duration_to_array.cpp.o
[ 31%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_deque.cpp.o
[ 31%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_deque_into_array.cpp.o
[ 32%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_enum_class.cpp.o
[ 32%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_enum_class_into_array.cpp.o
[ 33%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_float.cpp.o
[ 33%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_float_into_array.cpp.o
[ 33%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_int.cpp.o
[ 34%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_int_into_array.cpp.o
[ 34%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_into_carray.cpp.o
[ 35%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_list.cpp.o
[ 35%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_list_into_array.cpp.o
[ 36%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_map.cpp.o
[ 36%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_map_into_array.cpp.o
[ 37%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_nested_struct.cpp.o
[ 37%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_nested_struct_into_array.cpp.o
[ 38%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_optional.cpp.o
[ 38%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_optional_into_array.cpp.o
[ 39%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_pair.cpp.o
[ 39%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_pair_optional.cpp.o
[ 40%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_set.cpp.o
[ 40%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_set_into_array.cpp.o
[ 41%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_string.cpp.o
[ 41%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_string_into_array.cpp.o
[ 42%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_to_fstream.cpp.o
[ 42%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_tuple.cpp.o
[ 43%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_tuple_into_array.cpp.o
[ 43%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_uint.cpp.o
[ 44%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_uint_into_array.cpp.o
[ 44%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_unique_ptr.cpp.o
[ 45%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_unique_ptr_into_array.cpp.o
[ 45%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_variant.cpp.o
[ 46%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_variant_into_array.cpp.o
[ 46%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_vector.cpp.o
[ 47%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_vector_into_array.cpp.o
[ 47%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_with_crc32.cpp.o
[ 48%] Building CXX object test/CMakeFiles/ALPACA.dir/test_serialize_with_crc32_into_array.cpp.o
[ 48%] Building CXX object test/CMakeFiles/ALPACA.dir/test_type_info.cpp.o
[ 49%] Linking CXX executable tests
[ 49%] Built target ALPACA
[ 50%] Building CXX object samples/CMakeFiles/fundamental_types.dir/fundamental_types.cpp.o
[ 50%] Linking CXX executable fundamental_types
[ 50%] Built target fundamental_types
[ 50%] Building CXX object samples/CMakeFiles/arrays_vectors_strings.dir/arrays_vectors_strings.cpp.o
[ 50%] Linking CXX executable arrays_vectors_strings
[ 50%] Built target arrays_vectors_strings
[ 51%] Building CXX object samples/CMakeFiles/maps_and_sets.dir/maps_and_sets.cpp.o
[ 51%] Linking CXX executable maps_and_sets
[ 51%] Built target maps_and_sets
[ 51%] Building CXX object samples/CMakeFiles/nested_structures.dir/nested_structures.cpp.o
[ 52%] Linking CXX executable nested_structures
[ 52%] Built target nested_structures
[ 52%] Building CXX object samples/CMakeFiles/optional_values.dir/optional_values.cpp.o
In file included from /Users/Mladen/Developer/C++/ngfsw/alpaca/samples/optional_values.cpp:1:
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:125:3: error: no matching function for call to 'to_bytes'
  to_bytes<O>(bytes, byte_index, input);
  ^~~~~~~~~~~
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/vector.h:34:5: note: in instantiation of function template specialization 'alpaca::detail::to_bytes_router<alpaca::options::none, std::__bit_const_reference<std::vector<bool>>, std::vector<unsigned char>>' requested here
    to_bytes_router<O>(v, bytes, byte_index);
    ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/vector.h:48:3: note: in instantiation of function template specialization 'alpaca::detail::to_bytes_from_vector_type<alpaca::options::none, std::vector<bool>, std::vector<unsigned char>>' requested here
  to_bytes_from_vector_type<O, std::vector<bool>, Container>(input, bytes,
  ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:125:3: note: in instantiation of function template specialization 'alpaca::detail::to_bytes<alpaca::options::none, std::vector<unsigned char>>' requested here
  to_bytes<O>(bytes, byte_index, input);
  ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/optional.h:35:5: note: in instantiation of function template specialization 'alpaca::detail::to_bytes_router<alpaca::options::none, std::vector<bool>, std::vector<unsigned char>>' requested here
    to_bytes_router<O, U>(input.value(), bytes, byte_index);
    ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:125:3: note: in instantiation of function template specialization 'alpaca::detail::to_bytes<alpaca::options::none, std::vector<unsigned char>, std::vector<bool>>' requested here
  to_bytes<O>(bytes, byte_index, input);
  ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:138:13: note: (skipping 1 context in backtrace; use -ftemplate-backtrace-limit=0 to see all)
    detail::to_bytes_router<O>(field, bytes, byte_index);
            ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:141:5: note: in instantiation of function template specialization 'alpaca::detail::serialize_helper<alpaca::options::none, MyStruct, 4UL, std::vector<unsigned char>, 3UL>' requested here
    serialize_helper<O, T, N, Container, I + 1>(s, bytes, byte_index);
    ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:141:5: note: in instantiation of function template specialization 'alpaca::detail::serialize_helper<alpaca::options::none, MyStruct, 4UL, std::vector<unsigned char>, 2UL>' requested here
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:141:5: note: in instantiation of function template specialization 'alpaca::detail::serialize_helper<alpaca::options::none, MyStruct, 4UL, std::vector<unsigned char>, 1UL>' requested here
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:152:11: note: in instantiation of function template specialization 'alpaca::detail::serialize_helper<alpaca::options::none, MyStruct, 4UL, std::vector<unsigned char>, 0UL>' requested here
  detail::serialize_helper<options::none, T, N, Container, 0>(s, bytes,
          ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/samples/optional_values.cpp:17:32: note: in instantiation of function template specialization 'alpaca::serialize<MyStruct, 4UL, std::vector<unsigned char>>' requested here
  auto bytes_written = alpaca::serialize<MyStruct, 4>(
                               ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/filesystem_path.h:26:6: note: candidate function template not viable: no known conversion from 'const std::__bit_const_reference<std::vector<bool>>' to 'const std::filesystem::path' for 3rd argument
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/vector.h:46:6: note: candidate function template not viable: no known conversion from 'const std::__bit_const_reference<std::vector<bool>>' to 'const std::vector<bool>' for 3rd argument
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/to_bytes.h:39:1: note: candidate template ignored: requirement 'std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, bool> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, char> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, wchar_t> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, char16_t> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, char32_t> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, unsigned char> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, unsigned short> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, signed char> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, short> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, float> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, double>' was not satisfied [with O = alpaca::options::none, T = std::vector<unsigned char>, U = std::__bit_const_reference<std::vector<bool>>]
to_bytes(T &bytes, std::size_t &byte_index, const U &original_value) {
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/to_bytes.h:54:1: note: candidate template ignored: requirement 'std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, unsigned int> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, unsigned long long> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, int> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, long long> || std::is_same_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>, unsigned long>' was not satisfied [with O = alpaca::options::none, T = std::vector<unsigned char>, U = std::__bit_const_reference<std::vector<bool>>]
to_bytes(T &bytes, std::size_t &byte_index, const U &original_value) {
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/to_bytes.h:79:1: note: candidate template ignored: requirement 'std::is_enum<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>>::value' was not satisfied [with O = alpaca::options::none, T = std::vector<unsigned char>, U = std::__bit_const_reference<std::vector<bool>>]
to_bytes(T &bytes, std::size_t &byte_index, const U &value) {
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/alpaca.h:117:1: note: candidate template ignored: requirement 'std::is_aggregate_v<std::__bit_const_reference<std::vector<bool, std::allocator<bool>>>>' was not satisfied [with O = alpaca::options::none, T = std::vector<unsigned char>, U = std::__bit_const_reference<std::vector<bool>>]
to_bytes(T &bytes, std::size_t &byte_index, const U &input) {
^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/array.h:26:6: note: candidate template ignored: could not match 'array' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/deque.h:39:6: note: candidate template ignored: could not match 'deque' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/duration.h:30:6: note: candidate template ignored: could not match 'duration' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/list.h:39:6: note: candidate template ignored: could not match 'list' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/map.h:65:6: note: candidate template ignored: could not match 'map' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/map.h:73:6: note: candidate template ignored: could not match 'unordered_map' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/optional.h:26:6: note: candidate template ignored: could not match 'optional' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/pair.h:30:6: note: candidate template ignored: could not match 'pair' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/set.h:63:6: note: candidate template ignored: could not match 'set' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/set.h:71:6: note: candidate template ignored: could not match 'unordered_set' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/string.h:26:6: note: candidate template ignored: could not match 'basic_string' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/tuple.h:58:6: note: candidate template ignored: could not match 'tuple' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/unique_ptr.h:28:6: note: candidate template ignored: could not match 'unique_ptr' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/variant.h:42:6: note: candidate template ignored: could not match 'variant' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
/Users/Mladen/Developer/C++/ngfsw/alpaca/include/alpaca/detail/types/vector.h:39:6: note: candidate template ignored: could not match 'vector' against '__bit_const_reference'
void to_bytes(Container &bytes, std::size_t &byte_index,
     ^
1 error generated.
make[2]: *** [samples/CMakeFiles/optional_values.dir/optional_values.cpp.o] Error 1
make[1]: *** [samples/CMakeFiles/optional_values.dir/all] Error 2
make: *** [all] Error 2

I removed samples/optional_values.cpp from CMakeLists and had to do it also for
samples/time_t.cpp. Then I recompiled successfully.

Is there something I missed or simply related to my building host ?

PS: had also to exclude for building benchmarks which failed because of the -Werror flag.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.