Skip to content

Commit a274e91

Browse files
authored
Merge pull request #28 from GrigoriySokolov/conditionals_in_set
[MED] support for Conditional/Setter flds of med::set
2 parents c234a0f + 18c280c commit a274e91

5 files changed

Lines changed: 290 additions & 12 deletions

File tree

med/mandatory.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,19 @@ struct mandatory<
246246
{
247247
};
248248

249+
//M<TAG, FIELD, SETTER>
250+
template <ATag TAG, AField FIELD, class SETTER> requires ASetter<FIELD, SETTER>
251+
struct mandatory<
252+
TAG,
253+
FIELD,
254+
SETTER,
255+
min<1>,
256+
max<1>
257+
> : field_t<FIELD, add_tag<TAG>>
258+
{
259+
using setter_type = SETTER;
260+
};
261+
249262
//M<TAG, FIELD, arity<NUM>
250263
template <ATag TAG, AField FIELD, std::size_t NUM>
251264
struct mandatory<

med/optional.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,19 @@ struct optional<
274274
{
275275
};
276276

277+
//single-instance field as a part of compound
278+
template <ATag TAG, AField FIELD, ACondition CONDITION>
279+
struct optional<
280+
TAG,
281+
FIELD,
282+
CONDITION,
283+
min<1>,
284+
max<1>
285+
> : field_t<FIELD, add_tag<TAG>>, optional_t
286+
{
287+
using condition = CONDITION;
288+
};
289+
277290
//multi-instance field w/ tag
278291
template <ATag TAG, AField FIELD, std::size_t MAX>
279292
struct optional<

med/set.hpp

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,32 @@ namespace med {
2323

2424
namespace sl {
2525

26+
template <class FUNC, class IE>
27+
inline constexpr void encode_single(FUNC& func, IE const& ie)
28+
{
29+
if (ie.is_set())
30+
{
31+
using mi = meta::produce_info_t<FUNC, IE>;
32+
constexpr bool explicit_meta = explicit_meta_in<mi, get_field_type_t<IE>>();
33+
34+
CODEC_TRACE("[%s]%s: %s", name<IE>(), class_name<IE>(), class_name<mi>());
35+
if constexpr (explicit_meta)
36+
{
37+
using ctx = type_context<IE_SET, meta::list_rest_t<mi>>;
38+
sl::ie_encode<ctx>(func, ie);
39+
}
40+
else
41+
{
42+
using ctx = type_context<IE_SET, mi>;
43+
sl::ie_encode<ctx>(func, ie);
44+
}
45+
}
46+
else if constexpr (!AOptional<IE>)
47+
{
48+
MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0)
49+
}
50+
}
51+
2652
struct set_name
2753
{
2854
template <class IE, typename TAG, class CODEC>
@@ -51,12 +77,13 @@ struct set_enc
5177
template <class CTX, class PREV_IE, class IE, class TO, class ENCODER>
5278
static constexpr void apply(TO const& to, ENCODER& encoder)
5379
{
54-
using mi = meta::produce_info_t<ENCODER, IE>;
5580
IE const& ie = to;
56-
constexpr bool explicit_meta = explicit_meta_in<mi, get_field_type_t<IE>>();
5781

5882
if constexpr (AMultiField<IE>)
5983
{
84+
using mi = meta::produce_info_t<ENCODER, IE>;
85+
constexpr bool explicit_meta = explicit_meta_in<mi, get_field_type_t<IE>>();
86+
6087
CODEC_TRACE("[%s]*%zu: %s", name<IE>(), ie.count(), class_name<mi>());
6188
check_arity(encoder, ie);
6289

@@ -79,23 +106,29 @@ struct set_enc
79106
}
80107
else //single-instance field
81108
{
82-
if (ie.is_set())
109+
if constexpr (AHasSetterType<IE>) //with setter
83110
{
84-
CODEC_TRACE("[%s]%s: %s", name<IE>(), class_name<IE>(), class_name<mi>());
85-
if constexpr (explicit_meta)
111+
CODEC_TRACE("[%s] with setter from %s", name<IE>(), name<TO>());
112+
IE ie;
113+
ie.copy(static_cast<IE const&>(to), encoder);
114+
115+
typename IE::setter_type setter;
116+
if constexpr (std::is_same_v<bool, decltype(setter(ie, to))>)
86117
{
87-
using ctx = type_context<IE_SET, meta::list_rest_t<mi>>;
88-
sl::ie_encode<ctx>(encoder, ie);
118+
if (not setter(ie, to))
119+
{
120+
MED_THROW_EXCEPTION(invalid_value, name<IE>(), ie.get())
121+
}
89122
}
90123
else
91124
{
92-
using ctx = type_context<IE_SET, mi>;
93-
sl::ie_encode<ctx>(encoder, ie);
125+
setter(ie, to);
94126
}
127+
encode_single(encoder, ie);
95128
}
96-
else if constexpr (!AOptional<IE>)
129+
else
97130
{
98-
MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0)
131+
encode_single(encoder, ie);
99132
}
100133
}
101134
}
@@ -166,6 +199,17 @@ struct set_check
166199
MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0)
167200
}
168201
}
202+
203+
if constexpr (AHasCondition<IE>) // conditional - quite an exotic case, since a med::set usually does not require conditional fields
204+
{
205+
bool const should_be_set = typename IE::condition{}(to);
206+
if (ie.is_set() != should_be_set)
207+
{
208+
CODEC_TRACE("%cC[%s] %s be set in %s", ie.is_set() ? '+' : '-', name<IE>(), should_be_set ? "MUST" : "must NOT", name<TO>());
209+
if (should_be_set) { MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0); }
210+
else { MED_THROW_EXCEPTION(extra_ie, name<IE>(), 0, 1); }
211+
}
212+
}
169213
}
170214

171215
template <class TO, class DECODER>

ut/set.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,102 @@ TEST(encode, mset_fail_arity)
192192
EXPECT_THROW(encode(med::octet_encoder{ctx}, proto), med::missing_ie);
193193
}
194194

195+
TEST(encode, set_func_ok)
196+
{
197+
PROTO proto;
198+
199+
MSG_SET_FUNC& msg = proto.ref<MSG_SET_FUNC>();
200+
201+
//one mandatory, one conditional + setter, one optional
202+
msg.ref<FLD_UC>().set(0x11);
203+
msg.ref<FLD_U16>().set(3);
204+
msg.ref<FLD_IP>().set(1);
205+
206+
uint8_t buffer[1024];
207+
med::encoder_context ctx{buffer};
208+
209+
EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto));
210+
211+
// M< T16<0x0b>, FLD_UC >, //<TV>
212+
// M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
213+
// O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits<FLD_FLAGS::U16> >,
214+
// O< T16<0x89>, FLD_IP >
215+
uint8_t const encoded1[] = { 0x24
216+
, 0, 0x0b, 0x11
217+
, 0, 0x0d, 0b0000'0101
218+
, 0, 0x21, 0x00, 0x03
219+
, 0, 0x89, 0x00, 0x00, 0x00, 0x01
220+
};
221+
EXPECT_EQ(sizeof(encoded1), ctx.buffer().get_offset());
222+
EXPECT_TRUE(Matches(encoded1, buffer));
223+
224+
//one mandatory + setter
225+
ctx.reset();
226+
msg.clear();
227+
msg.ref<FLD_UC>().set(0x11);
228+
EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto));
229+
230+
// M< T16<0x0b>, FLD_UC >, //<TV>
231+
// M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
232+
uint8_t const encoded2[] = { 0x24
233+
, 0, 0x0b, 0x11
234+
, 0, 0x0d, 0b0000'0000
235+
};
236+
EXPECT_EQ(sizeof(encoded2), ctx.buffer().get_offset());
237+
EXPECT_TRUE(Matches(encoded2, buffer));
238+
239+
//full msg
240+
ctx.reset();
241+
msg.clear();
242+
msg.ref<FLD_UC>().set(0x11);
243+
msg.ref<FLD_U16>().set(3);
244+
msg.ref<FLD_IP>().set(1);
245+
msg.ref<FLD_U24>().set(2);
246+
msg.ref<FLD_U8>().set(4);
247+
msg.ref<FLD_QTY>().set(5);
248+
249+
// try to set wrong flags
250+
msg.ref<FLD_FLAGS>().set(255);
251+
252+
EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto));
253+
254+
// M< T16<0x0b>, FLD_UC >, //<TV>
255+
// M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
256+
// O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits<FLD_FLAGS::QTY> >,
257+
// O< T16<0x0c>, FLD_U8 >, //<TV>
258+
// O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits<FLD_FLAGS::U16> >,
259+
// O< T16<0x49>, FLD_U24, FLD_FLAGS::has_bits<FLD_FLAGS::U24> >,
260+
// O< T16<0x89>, FLD_IP >
261+
uint8_t const encoded3[] = { 0x24
262+
, 0, 0x0b, 0x11
263+
, 0, 0x0d, 0b0100'0111
264+
, 0, 0x0e, 0x05
265+
, 0, 0x0c, 0x04
266+
, 0, 0x21, 0x00, 0x03
267+
, 0, 0x49, 0x00, 0x00, 0x02
268+
, 0, 0x89, 0x00, 0x00, 0x00, 0x01
269+
};
270+
271+
EXPECT_EQ(sizeof(encoded3), ctx.buffer().get_offset());
272+
EXPECT_TRUE(Matches(encoded3, buffer));
273+
}
274+
275+
TEST(encode, set_func_fail)
276+
{
277+
PROTO proto;
278+
279+
MSG_SET_FUNC& msg = proto.ref<MSG_SET_FUNC>();
280+
281+
//NO mandatory, one conditional + setter, one optional
282+
msg.ref<FLD_U16>().set(3);
283+
msg.ref<FLD_IP>().set(1);
284+
285+
uint8_t buffer[1024];
286+
med::encoder_context ctx{buffer};
287+
288+
EXPECT_THROW(encode(med::octet_encoder{ctx}, proto), med::missing_ie);
289+
}
290+
195291
TEST(decode, set_ok)
196292
{
197293
PROTO proto;
@@ -413,3 +509,98 @@ TEST(decode, mset_fail_arity)
413509
ctx.reset(mandatory_overflow, sizeof(mandatory_overflow));
414510
EXPECT_THROW(decode(med::octet_decoder{ctx}, proto), med::extra_ie);
415511
}
512+
513+
TEST(decode, set_func_ok)
514+
{
515+
PROTO proto;
516+
517+
//mandatory fields only
518+
uint8_t const encoded1[] = { 0x24
519+
, 0, 0x0b, 0x11
520+
, 0, 0x0d, 0x00,
521+
};
522+
med::decoder_context ctx{encoded1};
523+
EXPECT_NO_THROW(decode(med::octet_decoder{ctx}, proto));
524+
{
525+
MSG_SET_FUNC const* msg = proto.get<MSG_SET_FUNC>();
526+
ASSERT_NE(nullptr, msg);
527+
528+
ASSERT_EQ(0x11, msg->get<FLD_UC>().get());
529+
ASSERT_EQ(0x00, msg->get<FLD_FLAGS>().get());
530+
FLD_U24 const* fld1 = msg->get<FLD_U24>();
531+
FLD_U16 const* fld2 = msg->get<FLD_U16>();
532+
FLD_U8 const* fld3 = msg->get<FLD_U8>();
533+
FLD_IP const* fld4 = msg->get<FLD_IP>();
534+
FLD_QTY const* fld5 = msg->get<FLD_QTY>();
535+
ASSERT_EQ(nullptr, fld1);
536+
ASSERT_EQ(nullptr, fld2);
537+
ASSERT_EQ(nullptr, fld3);
538+
ASSERT_EQ(nullptr, fld4);
539+
ASSERT_EQ(nullptr, fld5);
540+
}
541+
//all fields but in reverse order
542+
uint8_t const encoded2[] = { 0x24
543+
, 0, 0x89, 0x00, 0x00, 0x00, 0x01
544+
, 0, 0x49, 0x00, 0x00, 0x02
545+
, 0, 0x21, 0x00, 0x03
546+
, 0, 0x0c, 0x04
547+
, 0, 0x0e, 0x05
548+
, 0, 0x0b, 0x11
549+
, 0, 0x0d, 0b0100'0111,
550+
};
551+
ctx.reset(encoded2);
552+
EXPECT_NO_THROW(decode(med::octet_decoder{ctx}, proto));
553+
{
554+
MSG_SET_FUNC const* msg = proto.get<MSG_SET_FUNC>();
555+
ASSERT_NE(nullptr, msg);
556+
557+
ASSERT_EQ(0x11, msg->get<FLD_UC>().get());
558+
FLD_U24 const* fld1 = msg->get<FLD_U24>();
559+
FLD_U16 const* fld2 = msg->get<FLD_U16>();
560+
FLD_U8 const* fld3 = msg->get<FLD_U8>();
561+
FLD_IP const* fld4 = msg->get<FLD_IP>();
562+
FLD_QTY const* fld5 = msg->get<FLD_QTY>();
563+
ASSERT_NE(nullptr, fld1);
564+
ASSERT_NE(nullptr, fld2);
565+
ASSERT_NE(nullptr, fld3);
566+
ASSERT_NE(nullptr, fld4);
567+
ASSERT_NE(nullptr, fld5);
568+
ASSERT_EQ(2, fld1->get());
569+
ASSERT_EQ(3, fld2->get());
570+
ASSERT_EQ(4, fld3->get());
571+
ASSERT_EQ(1, fld4->get());
572+
ASSERT_EQ(5, fld5->get());
573+
// ASSERT_EQ(0b0010'1111, msg->get<FLD_FLAGS>().get());
574+
ASSERT_TRUE(FLD_FLAGS::has_bits<FLD_FLAGS::U16>{}(msg->body()));
575+
ASSERT_TRUE(FLD_FLAGS::has_bits<FLD_FLAGS::U24>{}(msg->body()));
576+
ASSERT_TRUE(FLD_FLAGS::has_bits<FLD_FLAGS::QTY>{}(msg->body()));
577+
constexpr size_t u8_qty = 1;
578+
ASSERT_TRUE(FLD_FLAGS::has_bits<u8_qty << FLD_FLAGS::U8_QTY>{}(msg->body()));
579+
auto const uc_qty_minus_one = 1 - 1;
580+
ASSERT_FALSE(FLD_FLAGS::has_bits<uc_qty_minus_one << FLD_FLAGS::UC_QTY>{}(msg->body()));
581+
}
582+
}
583+
584+
TEST(decode, set_func_fail)
585+
{
586+
PROTO proto;
587+
588+
//mandatory fields only, but some flags are set
589+
uint8_t const encoded1[] = { 0x24
590+
, 0, 0x0b, 0x11
591+
, 0, 0x0d, 0b0100'0111
592+
};
593+
med::decoder_context ctx{encoded1};
594+
EXPECT_THROW(decode(med::octet_decoder{ctx}, proto), med::missing_ie);
595+
596+
//some conditional fields, but flags are NOT set
597+
uint8_t const encoded2[] = { 0x24
598+
, 0, 0x49, 0x00, 0x00, 0x02
599+
, 0, 0x21, 0x00, 0x03
600+
, 0, 0x0c, 0x04
601+
, 0, 0x0b, 0x11
602+
, 0, 0x0d, 0b0100'0000
603+
};
604+
ctx.reset(encoded2);
605+
EXPECT_THROW(decode(med::octet_decoder{ctx}, proto), med::extra_ie);
606+
}

ut/ut_proto.hpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ struct FLD_QTY : med::value<uint8_t>
229229
}
230230
}
231231
};
232+
static constexpr char const* name() { return "Fld-Qty"; }
232233
};
233234

234235
struct FLD_FLAGS : med::value<uint8_t>
@@ -293,6 +294,7 @@ struct FLD_FLAGS : med::value<uint8_t>
293294
//ies.template as<FLD_FLAGS>().set(bits);
294295
}
295296
};
297+
static constexpr char const* name() { return "Fld-Flags"; }
296298
};
297299

298300
//optional fields with functors
@@ -309,13 +311,28 @@ struct MSG_FUNC : med::sequence<
309311
static constexpr char const* name() { return "Msg-With-Functors"; }
310312
};
311313

314+
//optional fields with functors
315+
struct MSG_SET_FUNC : med::set<
316+
M< T16<0x0b>, FLD_UC >, //<TV>
317+
M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
318+
O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits<FLD_FLAGS::QTY> >,
319+
O< T16<0x0c>, FLD_U8 >, //<TV>
320+
O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits<FLD_FLAGS::U16> >,
321+
O< T16<0x49>, FLD_U24, FLD_FLAGS::has_bits<FLD_FLAGS::U24> >,
322+
O< T16<0x89>, FLD_IP >
323+
>
324+
{
325+
static constexpr char const* name() { return "Msg-Set-With-Functors"; }
326+
decltype(auto) body() const { return this->m_ies; }
327+
};
312328

313329
struct PROTO : med::choice<
314330
M<C<0x01>, MSG_SEQ>,
315331
M<C<0x11>, MSG_MSEQ>,
316332
M<C<0x04>, MSG_SET>,
317333
M<C<0x14>, MSG_MSET>,
318-
M<C<0xFF>, MSG_FUNC>
334+
M<C<0xFF>, MSG_FUNC>,
335+
M<C<0x24>, MSG_SET_FUNC>
319336
>
320337
{
321338
};

0 commit comments

Comments
 (0)