diff --git a/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/data_type_template.tmpl b/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/data_type_template.tmpl index ed690a098a..c491ec8f0d 100644 --- a/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/data_type_template.tmpl +++ b/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/data_type_template.tmpl @@ -59,6 +59,16 @@ struct UAVCAN_EXPORT ${t.cpp_type_name} ${expand_attr_types(group_name='FieldTypes', attrs=fields)} % if union: + + struct Tag + { + enum Type + { + % for idx,a in enumerate(fields): + ${a.name}${',' if idx < (len(fields) - 1) else ''} + % endfor + }; + }; typedef IntegerSpec< ::uavcan::IntegerBitLen< ${len(fields)} >::Result, ::uavcan::SignednessUnsigned, ::uavcan::CastModeTruncate > TagType; @@ -150,12 +160,49 @@ public: ::uavcan::TailArrayOptimizationMode tao_mode = ::uavcan::TailArrayOptEnabled); % if union: + /** + * Constructors and assignment operators will only work if T is unique within the union; otherwise + * compilation will fail with an error referring to ambiguous template instantiation. + */ + template + ${type_name}(const T& x) + % for idx,a in enumerate(fields): + ${':' if idx == 0 else ','} ${a.name}() + % endfor + % if union: + , _tag_() + % endif + { + to() = x; + } + + template + ${type_name}& operator=(const T& x) + { + to() = x; + return *this; + } + + /** + * Use explicit tag access if the union contains non-unique types. + * Otherwise it is safer to use as<>(), to<>(), is<>(). + */ + typename Tag::Type getTag() const { return Tag::Type(_tag_); } + void setTag(typename Tag::Type x) { _tag_ = ::uavcan::StorageType< TagType >::Type(x); } + /** * Whether the union is set to the given type. + * Access by type; this will only work if the type is unique within the union; otherwise compilation will fail. */ template bool is() const; + /** + * Whether the union is set to the given type. + * Access by tag; this will work even if there are non-unique types within the union. + */ + bool is(typename Tag::Type x) const { return Tag::Type(_tag_) == x; } + /** * If the union is currently set to the type T, returns pointer to the appropriate field. * If the union is set to another type, returns null pointer. @@ -328,32 +375,49 @@ ${generate_codec_calls_per_field(call_name='encode', self_parameter_type='Parame ${generate_codec_calls_per_field(call_name='decode', self_parameter_type='ReferenceType')} % if union: +template <> +template +typename ::uavcan::EnableIfType< typename T::StorageType, bool>::Type ${scope_prefix}<0>::is() const +{ + return is(); +} + +template <> +template +typename ::uavcan::EnableIfType< typename T::StorageType, const typename T::StorageType*>::Type +${scope_prefix}<0>::as() const +{ + return as(); +} + +template <> +template +typename ::uavcan::EnableIfType< typename T::StorageType, typename T::StorageType&>::Type +${scope_prefix}<0>::to() +{ + return to(); +} + % for idx,a in enumerate(fields): template <> template <> -bool ${scope_prefix}<0>::is< typename ${scope_prefix}<0>::FieldTypes::${a.name} >() const +bool ${scope_prefix}<0>::is< typename ::uavcan::StorageType< typename ${scope_prefix}<0>::FieldTypes::${a.name} >::Type >() const { return _tag_ == ${idx}; } - % endfor - - % for idx,a in enumerate(fields): template <> template <> const typename ::uavcan::StorageType< typename ${scope_prefix}<0>::FieldTypes::${a.name} >::Type* -${scope_prefix}<0>::as< typename ${scope_prefix}<0>::FieldTypes::${a.name} >() const +${scope_prefix}<0>::as< typename ::uavcan::StorageType< typename ${scope_prefix}<0>::FieldTypes::${a.name} >::Type >() const { - return is< typename FieldTypes::${a.name} >() ? &${a.name} : NULL; + return is< typename ${scope_prefix}<0>::FieldTypes::${a.name} >() ? &${a.name} : NULL; } - % endfor - - % for idx,a in enumerate(fields): template <> template <> typename ::uavcan::StorageType< typename ${scope_prefix}<0>::FieldTypes::${a.name} >::Type& -${scope_prefix}<0>::to< ${scope_prefix}<0>::FieldTypes::${a.name} >() +${scope_prefix}<0>::to< typename ::uavcan::StorageType< typename ${scope_prefix}<0>::FieldTypes::${a.name} >::Type >() { if (_tag_ != ${idx}) { @@ -368,8 +432,10 @@ ${scope_prefix}<0>::to< ${scope_prefix}<0>::FieldTypes::${a.name} >() % if t.kind == t.KIND_SERVICE: -${define_out_of_line_struct_methods(scope_prefix=t.cpp_type_name + '::Request_', fields=t.request_fields, union=t.request_union)} -${define_out_of_line_struct_methods(scope_prefix=t.cpp_type_name + '::Response_', fields=t.response_fields, union=t.response_union)} +${define_out_of_line_struct_methods(scope_prefix=t.cpp_type_name + '::Request_', fields=t.request_fields, \ + union=t.request_union)} +${define_out_of_line_struct_methods(scope_prefix=t.cpp_type_name + '::Response_', fields=t.response_fields, \ + union=t.response_union)} % else: ${define_out_of_line_struct_methods(scope_prefix=t.cpp_type_name, fields=t.fields, union=t.union)} % endif