/* * Copyright (C) 2014 Pavel Kirienko */ #include #include using uavcan::Array; using uavcan::ArrayModeDynamic; using uavcan::ArrayModeStatic; using uavcan::IntegerSpec; using uavcan::FloatSpec; using uavcan::SignednessSigned; using uavcan::SignednessUnsigned; using uavcan::CastModeSaturate; using uavcan::CastModeTruncate; struct CustomType { typedef uavcan::IntegerSpec<8, uavcan::SignednessSigned, uavcan::CastModeTruncate> A; typedef uavcan::FloatSpec<16, uavcan::CastModeSaturate> B; // Dynamic array of max len 5 --> 3 bits for len, 5 bits for data --> 1 byte max len typedef uavcan::Array, uavcan::ArrayModeDynamic, 5> C; enum { MinBitLen = A::MinBitLen + B::MinBitLen + C::MinBitLen }; enum { MaxBitLen = A::MaxBitLen + B::MaxBitLen + C::MaxBitLen }; typename uavcan::StorageType::Type a; typename uavcan::StorageType::Type b; typename uavcan::StorageType::Type c; CustomType() : a(), b(), c() { } bool operator==(const CustomType& rhs) const { return a == rhs.a && b == rhs.b && c == rhs.c; } static int encode(const CustomType& obj, uavcan::ScalarCodec& codec, uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled) { int res = 0; res = A::encode(obj.a, codec, uavcan::TailArrayOptDisabled); if (res <= 0) return res; res = B::encode(obj.b, codec, uavcan::TailArrayOptDisabled); if (res <= 0) return res; res = C::encode(obj.c, codec, tao_mode); if (res <= 0) return res; return 1; } static int decode(CustomType& obj, uavcan::ScalarCodec& codec, uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled) { int res = 0; res = A::decode(obj.a, codec, uavcan::TailArrayOptDisabled); if (res <= 0) return res; res = B::decode(obj.b, codec, uavcan::TailArrayOptDisabled); if (res <= 0) return res; res = C::decode(obj.c, codec, tao_mode); if (res <= 0) return res; return 1; } }; TEST(Array, Basic) { typedef Array, ArrayModeStatic, 4> A1; typedef Array, ArrayModeStatic, 2> A2; typedef Array A3; A1 a1; A2 a2; A3 a3; ASSERT_EQ(1, A3::ValueType::C::RawValueType::BitLen); ASSERT_EQ(8 * 4, A1::MaxBitLen); ASSERT_EQ(16 * 2, A2::MaxBitLen); ASSERT_EQ((8 + 16 + 5 + 3) * 2, A3::MaxBitLen); /* * Zero initialization check */ ASSERT_FALSE(a1.empty()); for (A1::const_iterator it = a1.begin(); it != a1.end(); ++it) ASSERT_EQ(0, *it); ASSERT_FALSE(a2.empty()); for (A2::const_iterator it = a2.begin(); it != a2.end(); ++it) ASSERT_EQ(0, *it); for (A3::const_iterator it = a3.begin(); it != a3.end(); ++it) { ASSERT_EQ(0, it->a); ASSERT_EQ(0, it->b); ASSERT_EQ(0, it->c.size()); ASSERT_TRUE(it->c.empty()); } /* * Modification with known values; array lengths are hard coded. */ for (int i = 0; i < 4; i++) a1.at(i) = i; for (int i = 0; i < 2; i++) a2.at(i) = i; for (int i = 0; i < 2; i++) { a3[i].a = i; a3[i].b = i; for (int i2 = 0; i2 < 5; i2++) a3[i].c.push_back(i2 & 1); ASSERT_EQ(5, a3[i].c.size()); ASSERT_FALSE(a3[i].c.empty()); } /* * Representation check * Note that TAO in A3 is not possible because A3::C has less than one byte per item */ uavcan::StaticTransferBuffer<16> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(1, A1::encode(a1, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, A2::encode(a2, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, A3::encode(a3, sc_wr, uavcan::TailArrayOptEnabled)); ASSERT_EQ(0, A3::encode(a3, sc_wr, uavcan::TailArrayOptEnabled)); // Out of buffer space static const std::string Reference = "00000000 00000001 00000010 00000011 " // A1 (0, 1, 2, 3) "00000000 00000000 00000000 00111100 " // A2 (0, 1) "00000000 00000000 00000000 10101010 " // A3[0] (0, 0, bool[5]) "00000001 00000000 00111100 10101010"; // A3[1] (1, 1, bool[5]) ASSERT_EQ(Reference, bs_wr.toString()); /* * Read back */ uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); A1 a1_; A2 a2_; A3 a3_; ASSERT_EQ(1, A1::decode(a1_, sc_rd, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, A2::decode(a2_, sc_rd, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, A3::decode(a3_, sc_rd, uavcan::TailArrayOptEnabled)); ASSERT_EQ(a1_, a1); ASSERT_EQ(a2_, a2); ASSERT_EQ(a3_, a3); for (int i = 0; i < 4; i++) ASSERT_EQ(a1[i], a1_[i]); for (int i = 0; i < 2; i++) ASSERT_EQ(a2[i], a2_[i]); for (int i = 0; i < 2; i++) { ASSERT_EQ(a3[i].a, a3_[i].a); ASSERT_EQ(a3[i].b, a3_[i].b); ASSERT_EQ(a3[i].c, a3_[i].c); } ASSERT_EQ(0, A3::decode(a3_, sc_rd, uavcan::TailArrayOptEnabled)); // Out of buffer space /* * STL compatibility */ std::vector v1; v1.push_back(0); v1.push_back(1); v1.push_back(2); v1.push_back(3); ASSERT_TRUE(a1 == v1); ASSERT_FALSE(a1 != v1); ASSERT_TRUE(v1 == a1); ASSERT_FALSE(v1 != a1); ASSERT_FALSE(a1 < v1); v1[0] = 9000; ASSERT_FALSE(a1 == v1); ASSERT_TRUE(a1 != v1); ASSERT_TRUE(a1 < v1); ASSERT_EQ(0, a1.front()); ASSERT_EQ(3, a1.back()); // Boolean vector std::vector v2; v2.push_back(false); v2.push_back(true); v2.push_back(false); v2.push_back(true); v2.push_back(false); ASSERT_TRUE(a3[0].c == v2); ASSERT_FALSE(a3[0].c == v1); ASSERT_FALSE(a3[0].c != v2); ASSERT_TRUE(a3[0].c != v1); v2[0] = true; ASSERT_TRUE(a3[0].c != v2); ASSERT_FALSE(a3[0].c == v2); } TEST(Array, Dynamic) { typedef Array, ArrayModeDynamic, 5> A; typedef Array, ArrayModeDynamic, 255> B; A a; B b; B b2; ASSERT_EQ(3 + 5, A::MaxBitLen); ASSERT_EQ(8 + 255 * 8, B::MaxBitLen); ASSERT_TRUE(a.empty()); ASSERT_TRUE(b.empty()); ASSERT_TRUE(b2.empty()); { uavcan::StaticTransferBuffer<16> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(1, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::encode(b, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::encode(b2, sc_wr, uavcan::TailArrayOptEnabled)); ASSERT_EQ("000" "00000 000" "00000", bs_wr.toString()); // Last array was optimized away completely uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); ASSERT_EQ(1, A::decode(a, sc_rd, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::decode(b, sc_rd, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::decode(b2, sc_rd, uavcan::TailArrayOptEnabled)); ASSERT_TRUE(a.empty()); ASSERT_TRUE(b.empty()); ASSERT_TRUE(b2.empty()); } a.push_back(true); a.push_back(false); a.push_back(true); a.push_back(false); a.push_back(true); b.push_back(42); b.push_back(-42); b2.push_back(123); b2.push_back(72); { uavcan::StaticTransferBuffer<16> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(1, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::encode(b, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::encode(b2, sc_wr, uavcan::TailArrayOptEnabled)); // No length field // A B len B[0] B[1] B2[0] B2[1] ASSERT_EQ("10110101 00000010 00101010 11010110 01111011 01001000", bs_wr.toString()); uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); a.clear(); b.clear(); b2.clear(); ASSERT_TRUE(a.empty()); ASSERT_TRUE(b.empty()); ASSERT_TRUE(b2.empty()); ASSERT_EQ(1, A::decode(a, sc_rd, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::decode(b, sc_rd, uavcan::TailArrayOptDisabled)); ASSERT_EQ(1, B::decode(b2, sc_rd, uavcan::TailArrayOptEnabled)); ASSERT_EQ(5, a.size()); ASSERT_EQ(2, b.size()); ASSERT_EQ(2, b2.size()); ASSERT_TRUE(a[0]); ASSERT_FALSE(a[1]); ASSERT_TRUE(a[2]); ASSERT_FALSE(a[3]); ASSERT_TRUE(a[4]); ASSERT_EQ(42, b[0]); ASSERT_EQ(-42, b[1]); ASSERT_EQ(123, b2[0]); ASSERT_EQ(72, b2[1]); } ASSERT_FALSE(a == b); ASSERT_FALSE(b == a); ASSERT_TRUE(a != b); ASSERT_TRUE(b != a); a.resize(0); b.resize(0); ASSERT_TRUE(a.empty()); ASSERT_TRUE(b.empty()); a.resize(5, true); b.resize(255, 72); ASSERT_EQ(5, a.size()); ASSERT_EQ(255, b.size()); for (int i = 0; i < 5; i++) ASSERT_TRUE(a[i]); for (int i = 0; i < 255; i++) ASSERT_EQ(72, b[i]); } template struct CustomType2 { typedef uavcan::FloatSpec<16, uavcan::CastModeSaturate> A; enum { MinBitLen = A::MinBitLen + B::MinBitLen }; enum { MaxBitLen = A::MaxBitLen + B::MaxBitLen }; typename uavcan::StorageType::Type a; typename uavcan::StorageType::Type b; CustomType2() : a(), b() { } bool operator==(const CustomType2& rhs) const { return a == rhs.a && b == rhs.b; } static int encode(const CustomType2& obj, uavcan::ScalarCodec& codec, uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled) { int res = 0; res = A::encode(obj.a, codec, uavcan::TailArrayOptDisabled); if (res <= 0) return res; res = B::encode(obj.b, codec, tao_mode); if (res <= 0) return res; return 1; } static int decode(CustomType2& obj, uavcan::ScalarCodec& codec, uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled) { int res = 0; res = A::decode(obj.a, codec, uavcan::TailArrayOptDisabled); if (res <= 0) return res; res = B::decode(obj.b, codec, tao_mode); if (res <= 0) return res; return 1; } }; template static std::string runEncodeDecode(const typename uavcan::StorageType::Type& value, const uavcan::TailArrayOptimizationMode tao_mode) { uavcan::StaticTransferBuffer<(T::MaxBitLen + 7) / 8> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); EXPECT_EQ(1, T::encode(value, sc_wr, tao_mode)); typename uavcan::StorageType::Type value2 = typename uavcan::StorageType::Type(); // Decode multiple times to make sure that the decoded type is being correctly de-initialized for (int i = 0; i < 3; i++) { uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); EXPECT_EQ(1, T::decode(value2, sc_rd, tao_mode)); EXPECT_EQ(value, value2); } return bs_wr.toString(); } TEST(Array, TailArrayOptimization) { typedef Array, ArrayModeDynamic, 5> OneBitArray; typedef Array, ArrayModeDynamic, 255> EightBitArray; typedef CustomType2 > A; typedef CustomType2 > B; typedef CustomType2 C; A a; B b; C c; /* * Empty */ // a LSB a MSB b len ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode(a, uavcan::TailArrayOptEnabled)); ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode(a, uavcan::TailArrayOptDisabled)); // a LSB a MSB b len ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode(b, uavcan::TailArrayOptEnabled)); ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode(b, uavcan::TailArrayOptDisabled)); // a LSB a MSB ASSERT_EQ("00000000 00000000", runEncodeDecode(c, uavcan::TailArrayOptEnabled)); ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode(c, uavcan::TailArrayOptDisabled)); /* * A */ a.b.resize(2); a.b[0].push_back(true); a.b[0].push_back(false); // a.b[1] remains empty // a LSB a MSB b len b: len(2), 1, 0, len(0) ASSERT_EQ("00000000 00000000 00000010 01010000", runEncodeDecode(a, uavcan::TailArrayOptEnabled)); ASSERT_EQ("00000000 00000000 00000010 01010000", runEncodeDecode(a, uavcan::TailArrayOptDisabled)); /* * B */ b.b.resize(3); b.b[0].push_back(42); b.b[0].push_back(72); // b.b[1] remains empty b.b[2].push_back(123); b.b[2].push_back(99); // a LSB a MSB b len b[0]len 42 72 b[1]len 123 99 (b[2] len optimized out) ASSERT_EQ("00000000 00000000 00000011 00000010 00101010 01001000 00000000 01111011 01100011", runEncodeDecode(b, uavcan::TailArrayOptEnabled)); // Same as above, but b[2] len is present v here v ASSERT_EQ("00000000 00000000 00000011 00000010 00101010 01001000 00000000 00000010 01111011 01100011", runEncodeDecode(b, uavcan::TailArrayOptDisabled)); /* * C */ c.a = 1; c.b.push_back(1); c.b.push_back(2); c.b.push_back(3); // a LSB a MSB 1 2 3 ASSERT_EQ("00000000 00111100 00000001 00000010 00000011", runEncodeDecode(c, uavcan::TailArrayOptEnabled)); // a LSB a MSB b len 1 2 3 ASSERT_EQ("00000000 00111100 00000011 00000001 00000010 00000011", runEncodeDecode(c, uavcan::TailArrayOptDisabled)); } TEST(Array, TailArrayOptimizationErrors) { typedef Array, ArrayModeDynamic, 5> A; A a; ASSERT_TRUE(a.empty()); ASSERT_EQ("", runEncodeDecode(a, uavcan::TailArrayOptEnabled)); ASSERT_EQ("00000000", runEncodeDecode(a, uavcan::TailArrayOptDisabled)); // Correct decode/encode a.push_back(1); a.push_back(126); a.push_back(5); ASSERT_FALSE(a.empty()); ASSERT_EQ("00000001 01111110 00000101", runEncodeDecode(a, uavcan::TailArrayOptEnabled)); ASSERT_EQ("01100000 00101111 11000000 10100000", runEncodeDecode(a, uavcan::TailArrayOptDisabled)); // Invalid decode - length field is out of range uavcan::StaticTransferBuffer<7> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(1, sc_wr.encode<3>(uint8_t(6))); // Length - more than 5 items, error ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(42))); ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(72))); ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(126))); ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(1))); ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(2))); ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(3))); // Out of range - only 5 items allowed // 197 73 15 192 32 ... ASSERT_EQ("11000101 01001001 00001111 11000000 00100000 01000000 01100000", bs_wr.toString()); { uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); A a2; a2.push_back(56); // Garbage ASSERT_EQ(1, a2.size()); // Will fail - declared length is more than 5 items ASSERT_EQ(-1, A::decode(a2, sc_rd, uavcan::TailArrayOptDisabled)); // Must be cleared ASSERT_TRUE(a2.empty()); } { uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); A a2; a2.push_back(56); // Garbage ASSERT_EQ(1, a2.size()); // Will fail - no length field, but the stream is too long ASSERT_EQ(-1, A::decode(a2, sc_rd, uavcan::TailArrayOptEnabled)); // Will contain some garbage ASSERT_EQ(5, a2.size()); // Interpreted stream - see the values above ASSERT_EQ(197, a2[0]); ASSERT_EQ(73, a2[1]); ASSERT_EQ(15, a2[2]); ASSERT_EQ(192, a2[3]); ASSERT_EQ(32, a2[4]); } } TEST(Array, DynamicEncodeDecodeErrors) { typedef CustomType2, ArrayModeDynamic, 255>, ArrayModeDynamic, 255> > A; A a; a.b.resize(2); a.b[0].push_back(55); a.b[0].push_back(66); { uavcan::StaticTransferBuffer<4> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(0, A::encode(a, sc_wr, uavcan::TailArrayOptEnabled)); // Not enough buffer space uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); ASSERT_EQ(0, A::decode(a, sc_rd, uavcan::TailArrayOptEnabled)); } { uavcan::StaticTransferBuffer<4> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(0, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled)); // Not enough buffer space uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); ASSERT_EQ(0, A::decode(a, sc_rd, uavcan::TailArrayOptDisabled)); } } TEST(Array, StaticEncodeDecodeErrors) { typedef CustomType2, ArrayModeStatic, 2>, ArrayModeStatic, 2> > A; A a; a.a = 1.0; a.b[0][0] = 0x11; a.b[0][1] = 0x22; a.b[1][0] = 0x33; a.b[1][1] = 0x44; { // Just enough buffer space - 6 bytes uavcan::StaticTransferBuffer<6> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(1, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ("00000000 00111100 00010001 00100010 00110011 01000100", bs_wr.toString()); uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); ASSERT_EQ(1, A::decode(a, sc_rd, uavcan::TailArrayOptEnabled)); } { // Not enough space uavcan::StaticTransferBuffer<5> buf; uavcan::BitStream bs_wr(buf); uavcan::ScalarCodec sc_wr(bs_wr); ASSERT_EQ(0, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled)); ASSERT_EQ("00000000 00111100 00010001 00100010 00110011", bs_wr.toString()); uavcan::BitStream bs_rd(buf); uavcan::ScalarCodec sc_rd(bs_rd); ASSERT_EQ(0, A::decode(a, sc_rd, uavcan::TailArrayOptEnabled)); } } TEST(Array, Copyability) { typedef Array, ArrayModeDynamic, 5> OneBitArray; typedef Array, ArrayModeDynamic, 255> EightBitArray; typedef Array A; typedef Array B; typedef EightBitArray C; A a; B b; C c; A a2 = a; B b2 = b; C c2 = c; ASSERT_TRUE(a == a2); ASSERT_TRUE(b == b2); ASSERT_TRUE(c == c2); a.push_back(OneBitArray()); b.push_back(EightBitArray()); c.push_back(42); ASSERT_TRUE(a != a2); ASSERT_TRUE(b != b2); ASSERT_TRUE(c != c2); a2 = a; b2 = b; c2 = c; ASSERT_TRUE(a2 == a); ASSERT_TRUE(b2 == b); ASSERT_TRUE(c2 == c); } TEST(Array, Strings) { typedef Array, ArrayModeDynamic, 32> A8; typedef Array, ArrayModeDynamic, 32> A7; A8 a8; A8 a8_2; A7 a7; ASSERT_TRUE(a8 == a7); // cppcheck-suppress duplicateExpression ASSERT_TRUE(a8 == a8); // cppcheck-suppress duplicateExpression ASSERT_TRUE(a7 == a7); ASSERT_TRUE(a8 == ""); ASSERT_TRUE(a7 == ""); a8 = "Hello world!"; a7 = "123"; ASSERT_TRUE(a8 == "Hello world!"); ASSERT_TRUE(a7 == "123"); a8 = "Our sun is dying."; a7 = "456"; ASSERT_TRUE("Our sun is dying." == a8); ASSERT_TRUE("456" == a7); a8 += " 123456"; a8 += "-789"; ASSERT_TRUE("Our sun is dying. 123456-789" == a8); ASSERT_TRUE(a8_2 == ""); ASSERT_TRUE(a8_2.empty()); ASSERT_TRUE(a8_2 != a8); a8_2 = a8; ASSERT_TRUE(a8_2 == "Our sun is dying. 123456-789"); ASSERT_TRUE(a8_2 == a8); /* * c_str() */ ASSERT_STREQ("", A8().c_str()); ASSERT_STREQ("", A7().c_str()); ASSERT_STREQ("Our sun is dying. 123456-789", a8_2.c_str()); ASSERT_STREQ("Our sun is dying. 123456-789", a8.c_str()); ASSERT_STREQ("456", a7.c_str()); } TEST(Array, FlatStreaming) { typedef Array, ArrayModeDynamic, 32> A8D; typedef Array, ArrayModeDynamic, 16> AF16D; typedef Array, ArrayModeStatic, 3> AF16S; A8D a1; a1 = "12\n3\x44\xa5\xde\xad\x79"; uavcan::YamlStreamer::stream(std::cout, a1, 0); std::cout << std::endl; A8D a2; a2 = "Hello"; uavcan::YamlStreamer::stream(std::cout, a2, 0); std::cout << std::endl; AF16D af16d1; af16d1.push_back(1.23); af16d1.push_back(4.56); uavcan::YamlStreamer::stream(std::cout, af16d1, 0); std::cout << std::endl; AF16D af16d2; uavcan::YamlStreamer::stream(std::cout, af16d2, 0); std::cout << std::endl; AF16S af16s; uavcan::YamlStreamer::stream(std::cout, af16s, 0); std::cout << std::endl; } TEST(Array, MultidimensionalStreaming) { typedef Array, ArrayModeDynamic, 16> Float16Array; typedef Array TwoDimensional; typedef Array ThreeDimensional; ThreeDimensional threedee; threedee.resize(3); for (int x = 0; x < threedee.size(); x++) { threedee[x].resize(3); for (int y = 0; y < threedee[x].size(); y++) { threedee[x][y].resize(3); for (int z = 0; z < threedee[x][y].size(); z++) threedee[x][y][z] = 1.0 / (x + y + z + 1.0); } } uavcan::YamlStreamer::stream(std::cout, threedee, 0); std::cout << std::endl; }