From e617ff441aa9cc9286846f3a49eca5ae26e85d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Thu, 12 Dec 2019 13:16:22 -0300 Subject: [PATCH] csharp: Add implicit conversion from tuples to structs. Summary: Also generates implicit conversion for 1-field structs. Usage examples: ``` // With tuples Point2D p = (1, 2); obj.SetPoint((1, 2)); // With 1-field structs StringWrapper sw = "Must be handled by wrapper."; ``` Due to limitations of MSC compiler, implicit conversions are generated only if struct has 4 fields or less. Ref T8489. Reviewers: brunobelo, cedric, lauromoura, segfaultxavi Reviewed By: lauromoura Subscribers: #reviewers, #committers Tags: #efl Maniphest Tasks: T8489 Differential Revision: https://phab.enlightenment.org/D10801 --- .../eolian/mono/struct_definition.hh | 71 +++++++++++++++++-- .../eolian_mono/eolian/mono/struct_fields.hh | 40 +++++++++++ src/lib/eolian_cxx/grammar/klass_def.hpp | 2 +- src/tests/efl_mono/Structs.cs | 38 ++++++++++ 4 files changed, 146 insertions(+), 5 deletions(-) diff --git a/src/bin/eolian_mono/eolian/mono/struct_definition.hh b/src/bin/eolian_mono/eolian/mono/struct_definition.hh index d83e91b591..03085335d1 100644 --- a/src/bin/eolian_mono/eolian/mono/struct_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/struct_definition.hh @@ -403,6 +403,66 @@ struct struct_internal_definition_generator struct struct_definition_generator { + /** + * Generates an implicit operator for packing only if the struct has more + * than one attribute. Then operator will receive a tuple with the same of + * each attribute's type in the same order they were declared. + * + * Remarks: due to the MCS compiler's limitations, no operator is generated + * for structs with more than 4 fields. + */ + template + bool generate_implicit_operator(attributes::struct_def const& struct_ + , OutputIterator sink + , Context const& context) const + { + if (struct_.fields.size() <= 1 || struct_.fields.size() > 4) + return true; + + auto struct_name = binding_struct_name(struct_); + auto const& indent = current_indentation(context); + + if (!as_generator( + indent << scope_tab << "/// Packs tuple into " << struct_name << " object.\n" + << indent << scope_tab << "///Since EFL 1.24.\n" + << indent << scope_tab << "///\n" + ).generate(sink, attributes::unused, context)) + return false; + + if (!as_generator( + indent << scope_tab << "public static implicit operator " << struct_name << "(\n" + << indent << scope_tab << scope_tab << "(\n" + << ((indent << scope_tab << scope_tab << " " << field_argument_decl) % ",\n") << "\n" + << indent << scope_tab << scope_tab << ") tuple)\n" + << indent << scope_tab << "{\n" + ).generate(sink, struct_.fields, context)) + return false; + + // object constructor + if (!as_generator( + indent << scope_tab << scope_tab << "return new " << struct_name << "{\n" + ).generate(sink, attributes::unused, context)) + return false; + + for (const auto& field: struct_.fields) + { + auto field_name = name_helpers::to_field_name(field.name); + + if (!as_generator( + indent << scope_tab << scope_tab << scope_tab << field_name << " = tuple." << field_name << ",\n" + ).generate(sink, attributes::unused, context)) + return false; + } + + if (!as_generator( + indent << scope_tab << scope_tab << "};\n" + << indent << scope_tab << "}\n" + ).generate(sink, attributes::unused, context)) + return false; + + return true; + } + template bool generate(OutputIterator sink, attributes::struct_def const& struct_, Context const& context) const { @@ -473,7 +533,10 @@ struct struct_definition_generator << *(indent << scope_tab << scope_tab << field_argument_assignment << ";\n") << indent << scope_tab << "}\n\n") .generate(sink, std::make_tuple(struct_.fields, struct_name, struct_.fields, struct_.fields), context)) - return false; + return false; + + if (!generate_implicit_operator(struct_, sink, context)) + return false; } std::string since_line; @@ -546,9 +609,9 @@ struct struct_definition_generator ).generate(sink, attributes::unused, context)) return false; } - - - if (!as_generator( + + + if (!as_generator( indent << scope_tab << scope_tab << ";\n" << indent << scope_tab << "}\n" ).generate(sink, attributes::unused, context)) diff --git a/src/bin/eolian_mono/eolian/mono/struct_fields.hh b/src/bin/eolian_mono/eolian/mono/struct_fields.hh index b8bb0a231b..76e0f64138 100644 --- a/src/bin/eolian_mono/eolian/mono/struct_fields.hh +++ b/src/bin/eolian_mono/eolian/mono/struct_fields.hh @@ -28,6 +28,30 @@ namespace eolian_mono { +struct field_argument_name_generator +{ + template + bool generate(OutputIterator sink, attributes::struct_field_def const& field, Context const& context) const + { + if (!as_generator(name_helpers::to_field_name(field.name)) + .generate(sink, attributes::unused, context)) + return false; + return true; + } +} const field_argument_name {}; + +struct field_argument_decl_generator +{ + template + bool generate(OutputIterator sink, attributes::struct_field_def const& field, Context const& context) const + { + if (!as_generator(type << " " << string) + .generate(sink, std::make_tuple(field.type, name_helpers::to_field_name(field.name)), context)) + return false; + return true; + } +} const field_argument_decl {}; + struct field_argument_default_generator { template @@ -69,6 +93,16 @@ struct field_argument_docs_generator namespace efl { namespace eolian { namespace grammar { +template<> +struct is_eager_generator< ::eolian_mono::field_argument_name_generator> : std::true_type {}; +template<> +struct is_generator< ::eolian_mono::field_argument_name_generator> : std::true_type {}; + +template<> +struct is_eager_generator< ::eolian_mono::field_argument_decl_generator> : std::true_type {}; +template<> +struct is_generator< ::eolian_mono::field_argument_decl_generator> : std::true_type {}; + template<> struct is_eager_generator< ::eolian_mono::field_argument_default_generator> : std::true_type {}; template<> @@ -86,6 +120,12 @@ struct is_generator< ::eolian_mono::field_argument_docs_generator> : std::true_t namespace type_traits { +template <> +struct attributes_needed< ::eolian_mono::field_argument_name_generator> : std::integral_constant {}; + +template <> +struct attributes_needed< ::eolian_mono::field_argument_decl_generator> : std::integral_constant {}; + template <> struct attributes_needed< ::eolian_mono::field_argument_default_generator> : std::integral_constant {}; diff --git a/src/lib/eolian_cxx/grammar/klass_def.hpp b/src/lib/eolian_cxx/grammar/klass_def.hpp index d6969315e7..ad3b6eecbd 100644 --- a/src/lib/eolian_cxx/grammar/klass_def.hpp +++ b/src/lib/eolian_cxx/grammar/klass_def.hpp @@ -1816,7 +1816,7 @@ template <> struct is_tuple : std::true_type {}; template <> struct is_tuple : std::true_type {}; - + } } } } diff --git a/src/tests/efl_mono/Structs.cs b/src/tests/efl_mono/Structs.cs index 60be42eea5..521d39ba35 100644 --- a/src/tests/efl_mono/Structs.cs +++ b/src/tests/efl_mono/Structs.cs @@ -421,4 +421,42 @@ internal class TestStructEquality } } +internal class TestStructTuples +{ + private static Eina.Position2D simulate_position_usage(Eina.Position2D p) { + return p; + } + + public static void test_same_type_fields_assign_conversion() { + Eina.Position2D p = (1, 2); + Test.AssertEquals(p.X, 1); + Test.AssertEquals(p.Y, 2); + } + + public static void test_same_type_fields_call_conversion() { + var p = simulate_position_usage((1, 2)); + Test.AssertEquals(p.X, 1); + Test.AssertEquals(p.Y, 2); + } + + public static void test_different_type_fields_assign_conversion() { + Efl.Ui.FormatValue v = (1, "Format"); + Test.AssertEquals(v.Value, 1); + Test.AssertEquals(v.Text, "Format"); + } + + public static void test_complex_fields_assign_conversion() { + var pos = new Eina.Position2D(1, 2); + var action = Efl.Ui.SelectionAction.Unknown; + var format = Efl.Ui.SelectionFormat.None; + var item = null as Efl.Canvas.Vg.Object; + + Efl.Dnd.DragPos attr = (pos, action, format, item); + Test.AssertEquals(attr.Pos, pos); + Test.AssertEquals(attr.Action, action); + Test.AssertEquals(attr.Format, format); + Test.AssertEquals(attr.Item, item); + } +} + }