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
This commit is contained in:
João Paulo Taylor Ienczak Zanette 2019-12-12 13:16:22 -03:00 committed by Lauro Moura
parent 8a2ccfefe1
commit e617ff441a
4 changed files with 146 additions and 5 deletions

View File

@ -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 <typename OutputIterator, typename Context>
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 << "/// <summary>Packs tuple into " << struct_name << " object.\n"
<< indent << scope_tab << "///<para>Since EFL 1.24.</para>\n"
<< indent << scope_tab << "///</summary>\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 <typename OutputIterator, typename Context>
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))

View File

@ -28,6 +28,30 @@
namespace eolian_mono {
struct field_argument_name_generator
{
template<typename OutputIterator, typename Context>
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<typename OutputIterator, typename Context>
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<typename OutputIterator, typename Context>
@ -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<int, 1> {};
template <>
struct attributes_needed< ::eolian_mono::field_argument_decl_generator> : std::integral_constant<int, 1> {};
template <>
struct attributes_needed< ::eolian_mono::field_argument_default_generator> : std::integral_constant<int, 1> {};

View File

@ -1816,7 +1816,7 @@ template <>
struct is_tuple<attributes::parameter_def> : std::true_type {};
template <>
struct is_tuple<attributes::event_def> : std::true_type {};
}
} } }

View File

@ -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);
}
}
}