オブジェクトのバイト列化、バイト列からオブジェクトへの復元を行います。主に通信クラス内部で使用されています。
使用例
Loadingflowchart LR subgraph マイコン まいこんおぶ[オブジェクト] --シリアライズ--> a[バイト列] end subgraph パソコン a[バイト列] --> バイト列 --デシリアライズ--> ぱそこんおぶ[オブジェクト] end
#include <Udon/Serializer/Serializer.hpp>
整数型 ( int
, uint8_t
, uint16_t
... )
浮動小数点型 ( double
, float
... )
列挙型 ( enum
, enum class
)
シリアライズ可能な型の配列 (メンバ変数のみ)
列挙可能なユーザー定義型
シリアライズしたいオブジェクトのデータ構造を構造体、クラスを用いて定義します。UDON_ENUMERABLE
マクロにメンバ変数を登録することで、メンバ変数を走査できるようになり、シリアライズできるようになります。
Udon::Serialize(object)
の引数にオブジェクトを渡すことでバイト列 std::vector<uint8_t>
が返されます。
struct Vec2
{
double x;
double y;
UDON_ENUMERABLE(x, y);
};
void setup() {}
void loop()
{
Vec2 v{ 2.5, 3.4 };
const std::vector<uint8_t> packed = Udon::Serialize(v);
}
バイト列をオブジェクトに復元するには Udon::Deserialize<T>(...)
を使用します。 Udon::Deserialize
はデシリアライズされたオブジェクトを Udon::Optional
でラップして返します。
Optional
型とは値とエラー値を持つ型で、Optional::operator bool()
を呼び出すことで次のように if 文でエラー判定可能です。
バイト列末端バイトに挿入されているチェックサムと、バイト列から求めたチェックサムが異なる場合、デシリアライズが失敗します。 チェックサムの整合が取れない場合、データが破損しています。
void loop()
{
// packed にはシリアライズされたバイト列が入っている
if (const Udon::Optional<Vec2> unpacked = Udon::Deserialize<Vec2>(packed))
{
Serial.print(unpacked->x); Serial.print('\t');
Serial.print(unpacked->y); Serial.print('\n');
}
else
{
Serial.println("Deserialize failed!"); // データ破損
}
}
作成中
バイト列末尾にチェックサム (CRC8) を付与します。
bool 型は 1bit としてシリアライズします。
浮動小数点型はアーキテクチャによってサイズが異なるので、32bit 浮動小数点型にキャストして扱います。
列挙型は基底型を基にシリアライズします。
エンディアン変換を自動的に行います。
オブジェクトをシリアライズします
Serialize 関数は次のオーバーロードが定義されています。buffer
を引数にとる関数はヒープ領域を使用しません。
std::vector<uint8_t> Serialize(const T& object);
bool Serialize(const T& object, ArrayView<uint8_t> buffer);
バイト列をオブジェクトにデシリアライズします
template <typename T>
Udon::Optional<T> Deserialize(ArrayView<const uint8_t> buffer);
デシリアライズできるかを確認します。(チェックサム確認)
bool CanDeserialize(ArrayView<const uint8_t> buffer);
T 型オブジェクトシリアライズ後のバイト列のバイトサイズを取得します。
コンパイル時にサイズを取得可能なため、バッファの大きさを静的に指定するときなどにも使えます。
uint8_t buffer[Udon::SerializedSize<Vec2>()];
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
int main()
{
const auto packed = Udon::Serialize(1000);
if (const auto unpacked = Udon::Deserialize<int>(packed))
{
std::cout << *unpacked << std::endl;
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
100
Udon::Serialize(T) -> std::vector<uint8_t>
は std::vector
を用いており、バイト列をヒープ領域に割り当てて返します。パフォーマンスを気にする場合、ヒープ領域の使用はなるべく控えるべきです。Udon::Serialize(T, Udon::ArrayView<uint8_t>)
を用いることで、スタック領域(静的配列)をバッファーとしてシリアライズできます。
この時、バッファーのサイズと、シリアライズ後のサイズ(Udon::SerializedSize<T>()
)が異なる場合 Udon::Serialize
は失敗し false を返します。
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
int main()
{
uint8_t buffer[Udon::SerializedSize<int>()];
if (not Udon::Serialize(1000, buffer))
{
std::cout << "serialize failed" << std::endl;
}
if (const auto unpacked = Udon::Deserialize<int>(buffer))
{
std::cout << *unpacked << std::endl;
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
1000
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
enum class Language : uint8_t
{
C,
Cpp,
CSharp,
Python,
};
int main()
{
const Language lang = Language::Cpp;
const auto packed = Udon::Serialize(lang);
if (const auto unpacked = Udon::Deserialize<Language>(packed))
{
switch (*unpacked)
{
case Language::C : std::cout << "C" << std::endl; break;
case Language::Cpp : std::cout << "C++" << std::endl; break;
case Language::CSharp: std::cout << "C#" << std::endl; break;
case Language::Python: std::cout << "Python" << std::endl; break;
}
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
C++
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
struct Vec2
{
double x;
double y;
UDON_ENUMERABLE(x, y);
};
int main()
{
const Vec2 vector{ 1.0, 2.0 };
const auto packed = Udon::Serialize(vector);
if (const auto unpacked = Udon::Deserialize<Vec2>(packed))
{
std::cout
<< unpacked->x << ", "
<< unpacked->y << std::endl;
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
1, 2
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
struct Array
{
int array[5];
UDON_ENUMERABLE(array);
};
int main()
{
Array array{ { 1, 2, 3, 4, 5 } };
const auto packed = Udon::Serialize(array);
if (const auto unpacked = Udon::Deserialize<Array>(packed))
{
for (const auto& e : unpacked->array)
{
std::cout << e << std::endl;
}
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
1
2
3
4
5
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
struct Vec2
{
struct Double
{
double value;
UDON_ENUMERABLE(value);
};
Double x;
Double y;
UDON_ENUMERABLE(x, y);
};
int main()
{
const Vec2 vector{ { 1.0 }, { 2.0 } };
const auto packed = Udon::Serialize(vector);
if (const auto unpacked = Udon::Deserialize<Vec2>(packed))
{
std::cout
<< unpacked->x.value << ", "
<< unpacked->y.value << std::endl;
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
1, 2
#include <iostream>
#include <Udon/Serializer/Serializer.hpp>
int main()
{
auto packed = Udon::Serialize(1000);
packed.at(1) = 0x00; // データ破損
if (const auto unpacked = Udon::Deserialize<int>(packed))
{
std::cout << *unpacked << std::endl;
}
else
{
std::cout << "deserialize failed" << std::endl;
}
}
deserialize failed