1 /*
2  * Copyright 2019 by its authors. See AUTHORS.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef EOLIAN_MONO_STRUCT_DEFINITION_HH
17 #define EOLIAN_MONO_STRUCT_DEFINITION_HH
18 
19 #include "grammar/generator.hpp"
20 #include "grammar/klass_def.hpp"
21 #include "grammar/indentation.hpp"
22 #include "grammar/list.hpp"
23 #include "grammar/alternative.hpp"
24 #include "grammar/attribute_reorder.hpp"
25 #include "name_helpers.hh"
26 #include "helpers.hh"
27 #include "type.hh"
28 #include "using_decl.hh"
29 #include "documentation.hh"
30 #include "struct_fields.hh"
31 #include "blacklist.hh"
32 #include "culture_info.hh"
33 
34 namespace eolian_mono {
35 
binding_struct_name(attributes::struct_def const & struct_)36 inline std::string binding_struct_name(attributes::struct_def const& struct_)
37 {
38     return name_helpers::typedecl_managed_name(struct_);
39 }
40 
struct_internal_decl_name()41 inline std::string struct_internal_decl_name()
42 {
43    return  "NativeStruct";
44 }
45 
binding_struct_internal_name(attributes::struct_def const & struct_)46 inline std::string binding_struct_internal_name(attributes::struct_def const& struct_)
47 {
48    return binding_struct_name(struct_) + "." + struct_internal_decl_name();
49 }
50 
51 // Conversors generation //
52 
53 struct to_internal_field_convert_generator
54 {
55    template <typename OutputIterator, typename Context>
generateeolian_mono::to_internal_field_convert_generator56    bool generate(OutputIterator sink, attributes::struct_field_def const& field, Context const& context) const
57    {
58       auto const& indent = current_indentation(context);
59       auto field_name = name_helpers::to_field_name(field.name);
60       // FIXME Replace need_struct_conversion(regular) with need_struct_conversion(type)
61       auto regular = efl::eina::get<attributes::regular_type_def>(&field.type.original_type);
62       auto klass = efl::eina::get<attributes::klass_name>(&field.type.original_type);
63       auto complex = efl::eina::get<attributes::complex_type_def>(&field.type.original_type);
64 
65       if (klass)
66         {
67            if (!as_generator(
68                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << "?.NativeHandle ?? System.IntPtr.Zero;\n")
69                .generate(sink, std::make_tuple(field_name, field_name), context))
70              return false;
71         }
72       else if ((complex && (complex->outer.base_type == "array")))
73         {
74            if (!as_generator(
75                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Efl.Eo.Globals.IListToNativeArray(_external_struct." << string << ", " << (field.type.has_own ? "true" : "false") << ");\n")
76                .generate(sink, std::make_tuple(field_name, field_name), context))
77              return false;
78         }
79       else if ((complex && (complex->outer.base_type == "list")))
80         {
81            if (!as_generator(
82                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Efl.Eo.Globals.IListToNativeList(_external_struct." << string << ", " << (field.type.has_own ? "true" : "false") << ");\n")
83                .generate(sink, std::make_tuple(field_name, field_name), context))
84              return false;
85         }
86       else if ((complex && (complex->outer.base_type == "iterator")))
87         {
88            if (!as_generator(
89                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Efl.Eo.Globals.IEnumerableToIterator(_external_struct." << string << ", " << (field.type.has_own ? "true" : "false")  << ");\n")
90                .generate(sink, std::make_tuple(field_name, field_name), context))
91              return false;
92         }
93       else if ((complex && (complex->outer.base_type == "accessor")))
94         {
95            if (!as_generator(
96                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Efl.Eo.Globals.IEnumerableToAccessor(_external_struct." << string << ", " << (field.type.has_own ? "true" : "false")  << ");\n")
97                .generate(sink, std::make_tuple(field_name, field_name), context))
98              return false;
99         }
100       else if ((complex && (complex->outer.base_type == "hash"))
101             || field.type.c_type == "Eina_Binbuf *" || field.type.c_type == "const Eina_Binbuf *")
102         {
103            // Always assumes pointer
104            if (!as_generator(
105                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << ".Handle;\n")
106                .generate(sink, std::make_tuple(field_name, field_name), context))
107              return false;
108         }
109       else if (field.type.is_ptr && helpers::need_pointer_conversion(regular) && !helpers::need_struct_conversion(regular))
110         {
111            if (!as_generator(
112                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Eina.PrimitiveConversion.ManagedToPointerAlloc(_external_struct." << string << ");\n")
113                .generate(sink, std::make_tuple(field_name, field_name), context))
114              return false;
115         }
116       else if (helpers::need_struct_conversion(regular))
117         {
118            if (!as_generator(
119                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << ";\n")
120                .generate(sink, std::make_tuple(field_name, field_name), context))
121              return false;
122         }
123       else if (regular && (regular->base_type == "string" || regular->base_type == "mstring"))
124         {
125            if (!as_generator(
126                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Eina.MemoryNative.StrDup(_external_struct." << string << ");\n")
127                .generate(sink, std::make_tuple(field_name, field_name), context))
128              return false;
129         }
130       else if (regular && regular->base_type == "stringshare")
131         {
132            if (!as_generator(
133                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = Eina.MemoryNative.AddStringshare(_external_struct." << string << ");\n")
134                .generate(sink, std::make_tuple(field_name, field_name), context))
135              return false;
136         }
137       else if (field.type.c_type == "Eina_Slice" || field.type.c_type == "const Eina_Slice"
138                || field.type.c_type == "Eina_Rw_Slice" || field.type.c_type == "const Eina_Rw_Slice")
139         {
140            if (!as_generator(
141                  "\n" <<
142                  indent << scope_tab << scope_tab << "_internal_struct." << field_name << ".Len = _external_struct." << field_name << ".Len;\n" <<
143                  indent << scope_tab << scope_tab << "_internal_struct." << field_name << ".Mem = _external_struct." << field_name << ".Mem;\n")
144                .generate(sink, attributes::unused, context))
145              return false;
146         }
147       else if (field.type.c_type == "Eina_Value" || field.type.c_type == "const Eina_Value")
148         {
149            if (!as_generator(
150                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << ".GetNative();\n"
151                ).generate(sink, std::make_tuple(field_name, field_name), context))
152              return false;
153         }
154       else if (field.type.c_type == "Eina_Value *" || field.type.c_type == "const Eina_Value *")
155         {
156            if (!as_generator(
157                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << "?.NativeHandle ?? System.IntPtr.Zero;\n"
158                ).generate(sink, std::make_tuple(field_name, field_name), context))
159              return false;
160         }
161       else if (!field.type.is_ptr && regular && regular->base_type == "bool")
162         {
163            if (!as_generator(
164                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << " ? (byte)1 : (byte)0;\n")
165                .generate(sink, std::make_tuple(field_name, field_name), context))
166              return false;
167         }
168       else if (!field.type.is_ptr && regular && regular->base_type == "char")
169         {
170            if (!as_generator(
171                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = (byte)_external_struct." << string << ";\n")
172                .generate(sink, std::make_tuple(field_name, field_name), context))
173              return false;
174         }
175       else // primitives and enums
176         {
177            if (!as_generator(
178                  indent << scope_tab << scope_tab << "_internal_struct." << string << " = _external_struct." << string << ";\n")
179                .generate(sink, std::make_tuple(field_name, field_name), context))
180              return false;
181         }
182 
183       return true;
184    }
185 } const to_internal_field_convert {};
186 
187 struct to_external_field_convert_generator
188 {
189    template <typename OutputIterator, typename Context>
generateeolian_mono::to_external_field_convert_generator190    bool generate(OutputIterator sink, attributes::struct_field_def const& field, Context const& context) const
191    {
192       auto field_name = name_helpers::to_field_name(field.name);
193       auto regular = efl::eina::get<attributes::regular_type_def>(&field.type.original_type);
194       auto klass = efl::eina::get<attributes::klass_name>(&field.type.original_type);
195       auto complex = efl::eina::get<attributes::complex_type_def>(&field.type.original_type);
196 
197       if (klass)
198         {
199            auto interface_name = name_helpers::klass_full_interface_name(*klass);
200            if (!as_generator(
201                  "(" << interface_name << ") Efl.Eo.Globals.CreateWrapperFor(" << string << ");"
202                  ).generate(sink, std::make_tuple(field_name, field_name), context))
203              return false;
204         }
205       else if (field.type.c_type == "Eina_Binbuf *" || field.type.c_type == "const Eina_Binbuf *")
206         {
207            if (!as_generator(
208                  "new " << type << "(" << string << ", false);")
209                .generate(sink, std::make_tuple(field.type, field_name), context))
210              return false;
211         }
212       else if (complex && (complex->outer.base_type == "array"))
213         {
214            // Always assumes pointer
215            if (!as_generator(
216                  "Efl.Eo.Globals.NativeArrayTo" << type << "(" << string << ");")
217                .generate(sink, std::make_tuple(field.type, field_name), context))
218              return false;
219         }
220       else if (complex && (complex->outer.base_type == "list"))
221         {
222            // Always assumes pointer
223            if (!as_generator(
224                  "Efl.Eo.Globals.NativeListTo" << type << "(" << string << ");")
225                .generate(sink, std::make_tuple(field.type, field_name), context))
226              return false;
227         }
228       else if (complex && complex->outer.base_type == "hash")
229         {
230            if (!as_generator(
231                  "new " << type << "(" << string << ", false, false, false);")
232                .generate(sink, std::make_tuple(field.type, field_name), context))
233              return false;
234         }
235       else if (complex && complex->outer.base_type == "iterator")
236         {
237            if (!as_generator(
238                  "Efl.Eo.Globals.IteratorTo" << type << "(" << string << ");")
239                .generate(sink, std::make_tuple(field.type, field_name), context))
240              return false;
241         }
242       else if (complex && complex->outer.base_type == "accessor")
243         {
244            if (!as_generator(
245                  "Efl.Eo.Globals.AccessorTo" << type << "(" << string << ");")
246                .generate(sink, std::make_tuple(field.type, field_name), context))
247              return false;
248         }
249       else if (field.type.is_ptr && helpers::need_pointer_conversion(regular) && !helpers::need_struct_conversion(regular))
250         {
251            if (!as_generator(
252                  "Eina.PrimitiveConversion.PointerToManaged<" << type << ">(" << string << ");")
253                .generate(sink, std::make_tuple(field.type, field_name), context))
254              return false;
255         }
256       else if (helpers::need_struct_conversion(regular))
257         {
258            if (!as_generator(
259                  string << ";")
260                .generate(sink, field_name, context))
261              return false;
262         }
263       else if (regular && (regular->base_type == "string" || regular->base_type == "mstring" || regular->base_type == "stringshare"))
264         {
265            if (!as_generator(
266                  "Eina.StringConversion.NativeUtf8ToManagedString(" << string << ");")
267                .generate(sink, std::make_tuple(field_name, field_name), context))
268              return false;
269         }
270       else if (field.type.c_type == "Eina_Slice" || field.type.c_type == "const Eina_Slice"
271                || field.type.c_type == "Eina_Rw_Slice" || field.type.c_type == "const Eina_Rw_Slice")
272         {
273            if (!as_generator(field_name << ";")
274                .generate(sink, attributes::unused, context))
275              return false;
276         }
277       else if (field.type.c_type == "Eina_Value" || field.type.c_type == "const Eina_Value")
278         {
279            if (!as_generator(
280                  "new Eina.Value(" << string << ");"
281                ).generate(sink, std::make_tuple(field_name), context))
282              return false;
283         }
284       else if (field.type.c_type == "Eina_Value *" || field.type.c_type == "const Eina_Value *")
285         {
286            if (!as_generator(
287                  "new Eina.Value(" << string << ", Eina.Ownership.Unmanaged);"
288                ).generate(sink, std::make_tuple(field_name), context))
289              return false;
290         }
291       else if (!field.type.is_ptr && regular && regular->base_type == "bool")
292         {
293            if (!as_generator(
294                  string << " != 0;"
295                ).generate(sink, std::make_tuple(field_name), context))
296              return false;
297         }
298       else if (!field.type.is_ptr && regular && regular->base_type == "char")
299         {
300            if (!as_generator(
301                  "(char)" << string << ";"
302                ).generate(sink, std::make_tuple(field_name), context))
303              return false;
304         }
305       else // primitives and enums
306         {
307            if (!as_generator(
308                  field_name << ";"
309                ).generate(sink, attributes::unused, context))
310              return false;
311         }
312       return true;
313    }
314 } const to_external_field_convert {};
315 
316 // Internal Struct //
317 
318 struct struct_private_property_generator
319 {
320   template <typename OutputIterator, typename Context>
generateeolian_mono::struct_private_property_generator321   bool generate(OutputIterator sink, attributes::struct_def const& struct_, Context const& context) const
322   {
323      auto const& indent = current_indentation(context);
324 
325      // iterate struct fields
326      for (auto const& field : struct_.fields)
327        {
328           auto field_name = name_helpers::to_field_name(field.name);
329           auto klass = efl::eina::get<attributes::klass_name>(&field.type.original_type);
330           auto regular = efl::eina::get<attributes::regular_type_def>(&field.type.original_type);
331 
332           if (klass
333               || (regular && (regular->base_type == "string"
334                               || regular->base_type == "mstring"
335                               || regular->base_type == "stringshare"
336                               || regular->base_type == "any_value_ref")))
337             {
338                if (!as_generator(indent << scope_tab << "/// <summary>Internal wrapper for field " << field_name << "</summary>\n"
339                                  << indent << scope_tab << "private System.IntPtr " << field_name << ";\n")
340                    .generate(sink, nullptr, context))
341                  return false;
342             }
343           else if (regular && !(regular->base_qualifier & efl::eolian::grammar::attributes::qualifier_info::is_ref)
344                    && regular->base_type == "bool")
345             {
346                if (!as_generator(indent << scope_tab << "/// <summary>Internal wrapper for field " << field_name << "</summary>\n"
347                                  << indent << scope_tab << "private System.Byte " << field_name << ";\n")
348                    .generate(sink, nullptr, context))
349                  return false;
350             }
351           else if (regular && !(regular->base_qualifier & efl::eolian::grammar::attributes::qualifier_info::is_ref)
352                    && regular->base_type == "char")
353             {
354                if (!as_generator(indent << scope_tab << "/// <summary>Internal wrapper for field " << field_name << "</summary>\n"
355                                  << indent << scope_tab << "private System.Byte " << field_name << ";\n")
356                    .generate(sink, nullptr, context))
357                  return false;
358             }
359           else if (!as_generator(indent << scope_tab << eolian_mono::marshall_annotation(false) << "\n"
360                                  << indent << scope_tab << "private " << eolian_mono::marshall_type(false) << " " << string << ";\n")
361                    .generate(sink, std::make_tuple(field.type, field.type, field_name), context))
362             return false;
363        }
364 
365      // Check whether this is an extern struct without declared fields in .eo file and generate a
366      // placeholder field if positive.
367      // Mono's JIT is picky when generating function pointer for delegates with empty structs, leading to
368      // those 'mini-amd64.c condition fields not met' crashes.
369      if (struct_.fields.size() == 0)
370        {
371            if (!as_generator(indent << scope_tab << "/// <summary>Placeholder field</summary>\n"
372                              << indent << scope_tab << "private IntPtr field;\n").generate(sink, nullptr, context))
373              return false;
374        }
375 
376      if(!as_generator("\n")
377         .generate(sink, attributes::unused, context))
378        return false;
379 
380      return true;
381   }
382 } const struct_private_property {};
383 
384 // Managed Struct //
385 
386 struct struct_definition_generator
387 {
388   /**
389    * Generates an implicit operator for packing only if the struct has more
390    * than one attribute. Then operator will receive a tuple with the same of
391    * each attribute's type in the same order they were declared.
392    *
393    * Remarks: due to the MCS compiler's limitations, no operator is generated
394    * for structs with more than 4 fields.
395    */
396   template <typename OutputIterator, typename Context>
generate_implicit_operatoreolian_mono::struct_definition_generator397   bool generate_implicit_operator(attributes::struct_def const& struct_
398                               , OutputIterator sink
399                               , Context const& context) const
400   {
401      if (struct_.fields.size() <= 1 || struct_.fields.size() > 4)
402        return true;
403 
404      auto struct_name = binding_struct_name(struct_);
405      auto const& indent = current_indentation(context).inc();
406 
407      if (!as_generator(
408            indent << scope_tab << "/// <summary>Packs tuple into " << struct_name << " object.\n"
409            << indent << scope_tab << "///<para>Since EFL 1.24.</para>\n"
410            << indent << scope_tab << "///</summary>\n"
411         ).generate(sink, attributes::unused, context))
412        return false;
413 
414      if (!as_generator(
415           indent << scope_tab << "public static implicit operator " << struct_name << "(("
416           << (field_argument_decl % ", ")
417           << ") tuple)\n"
418         ).generate(sink, struct_.fields, context))
419        return false;
420 
421      // object constructor
422      if (!as_generator(
423           indent << scope_tab << scope_tab << "=> new " << struct_name << "("
424         ).generate(sink, attributes::unused, context))
425        return false;
426 
427      if (!as_generator(
428           (("tuple." << struct_field_name) % ", ")
429           << ");\n\n"
430         ).generate(sink, struct_.fields, context))
431        return false;
432 
433      return true;
434   }
435 
436   template <typename OutputIterator, typename Context>
generate_deconstruct_methodeolian_mono::struct_definition_generator437   bool generate_deconstruct_method(OutputIterator sink, attributes::struct_def const& struct_, Context const& context) const
438   {
439      auto const& indent = current_indentation(context).inc();
440      auto struct_name = binding_struct_name(struct_);
441 
442      if (!as_generator(
443          indent << scope_tab << "/// <summary>Unpacks " << struct_name << " into tuple.\n"
444          << indent << scope_tab << "/// <para>Since EFL 1.24.</para>\n"
445          << indent << scope_tab << "/// </summary>\n"
446          << indent << scope_tab << "public void Deconstruct(\n"
447          ).generate(sink, attributes::unused, context))
448        return false;
449 
450      // parameters
451      {
452         auto i = 0u;
453         for (auto const& field : struct_.fields)
454           {
455              auto field_name = name_helpers::to_field_name(field.name);
456 
457              auto suffix = i == struct_.fields.size() - 1 ? "\n" : ",\n";
458 
459              if (!as_generator(
460                  indent << scope_tab << scope_tab << "out " << type << " " << field_name << suffix
461                  ).generate(sink, std::make_tuple(field.type), context))
462                return false;
463 
464              ++i;
465           }
466      }
467 
468      if (!as_generator(
469          indent << scope_tab << ")\n"
470          << indent << scope_tab << "{\n"
471          ).generate(sink, attributes::unused, context))
472        return false;
473 
474      // assigments
475      for (auto const& field : struct_.fields)
476        {
477           auto field_name = name_helpers::managed_name(field.name);
478           auto param_name = name_helpers::to_field_name(field.name);
479 
480           if (!as_generator(
481               indent << scope_tab << scope_tab << param_name << " = this." << field_name << ";\n"
482               ).generate(sink, attributes::unused, context))
483             return false;
484        }
485 
486      // the end
487      return as_generator(
488              indent << scope_tab << "}\n"
489              ).generate(sink, attributes::unused, context);
490   }
491 
492   template <typename OutputIterator, typename Context>
generateeolian_mono::struct_definition_generator493   bool generate(OutputIterator sink, attributes::struct_def const& struct_, Context const& context) const
494   {
495      EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "struct_definition_generator: " << struct_.cxx_name << std::endl;
496      auto const& indent = current_indentation(context).inc();
497      if(!as_generator(documentation(1)).generate(sink, struct_, context))
498        return false;
499      auto struct_managed_name = binding_struct_name(struct_);
500      if(!as_generator
501         (
502             indent << "[StructLayout(LayoutKind.Sequential)]\n"
503          << indent << "[Efl.Eo.BindingEntity]\n"
504          << indent << "[SuppressMessage(\"Microsoft.Naming\", \"CA1724:TypeNamesShouldNotMatchNamespaces\")]\n"
505          << indent << "public struct " << struct_managed_name << " : IEquatable<" << struct_managed_name << ">\n"
506          << indent << "{\n"
507          )
508         .generate(sink, attributes::unused, context))
509        return false;
510 
511      if (!struct_private_property.generate(sink, struct_, change_indentation(indent, context)))
512        return false;
513 
514      // iterate struct fields
515      for (auto const& field : struct_.fields)
516        {
517           auto docs = documentation(indent.n + 1);
518           if (!as_generator(docs).generate(sink, field, context))
519             return false;
520 
521           if (!field.type.doc_summary.empty())
522             {
523                if (!docs.generate_tag_value(sink, field.type.doc_summary, context))
524                  return false;
525             }
526 
527           if (!as_generator(indent << scope_tab << "public " << type << " " << name_helpers::managed_name(field.name) << " { get => " << to_external_field_convert << " }\n").generate(sink, std::make_tuple(field.type, field), context))
528             return false;
529        }
530 
531       auto struct_name = binding_struct_name(struct_);
532 
533      if (struct_.fields.size() != 0)
534        {
535           // Constructor with default parameters for easy struct initialization
536           if(!as_generator(
537                       indent << scope_tab << "/// <summary>Constructor for " << string << ".\n"
538                ).generate(sink, struct_name, context))
539             return false;
540 
541           if (!struct_.documentation.since.empty())
542             if (!as_generator(indent << scope_tab << "/// <para>Since EFL " + struct_.documentation.since + ".</para>\n"
543                     ).generate(sink, attributes::unused, context))
544               return false;
545 
546           if (!as_generator(
547                       indent << scope_tab << "/// </summary>\n"
548                       << *(indent << scope_tab << field_argument_docs << "\n")
549                       << indent << scope_tab << "public " << string << "(\n"
550                       << ((indent << scope_tab << scope_tab << field_argument_default) % ",\n")
551                       << ")\n"
552                       << indent << scope_tab << "{\n"
553                       << *(indent << scope_tab << scope_tab << field_argument_assignment)
554                       << indent << scope_tab << "}\n\n")
555              .generate(sink, std::make_tuple(struct_.fields, struct_name, struct_.fields, struct_.fields), context))
556             return false;
557 
558           if (!generate_implicit_operator(struct_, sink, context))
559             return false;
560 
561           if (!generate_deconstruct_method(sink, struct_, context))
562             return false;
563        }
564 
565      std::string since_line;
566      if (!struct_.documentation.since.empty())
567          if (!as_generator(indent << scope_tab << "/// <para>Since EFL " + struct_.documentation.since + ".</para>\n"
568                  ).generate(std::back_inserter(since_line), attributes::unused, context))
569            return false;
570 
571      // GetHashCode (needed by the equality comparisons)
572      if (!as_generator(
573              indent << scope_tab << "/// <summary>Get a hash code for this item.\n"
574              << since_line
575              << indent << scope_tab << "/// </summary>\n"
576              << indent << scope_tab << "public override int GetHashCode()\n"
577              << indent << scope_tab << "{\n"
578           ).generate(sink, attributes::unused, context))
579        return false;
580 
581      if (struct_.fields.size() == 1 )
582        {
583           if (!as_generator(
584                 indent << scope_tab << scope_tab  << "return " << name_helpers::managed_name(struct_.fields[0].name) << ".GetHashCode();\n"
585               ).generate(sink, attributes::unused, context))
586             return false;
587        }
588      else if (struct_.fields.size() != 0 )
589        {
590           // int hash = 17;
591           // hash = 23 * fieldA.GetHashCode();
592           // hash = 23 * fieldB.GetHashCode();
593           // hash = 23 * fieldC.GetHashCode();
594           // return hash
595           if (!as_generator(
596                 indent << scope_tab << scope_tab << "int hash = 17;\n"
597                 << *(grammar::attribute_reorder<-1, -1>(indent << scope_tab << scope_tab << "hash = hash * 23 + " << name_helpers::struct_property_name << ".GetHashCode(" << culture_info << ");\n"))
598                 << indent << scope_tab << scope_tab << "return hash;\n"
599               ).generate(sink, struct_.fields, context))
600             return false;
601        }
602      else
603        {
604           // Just compare the place holder pointers
605           if (!as_generator(
606                 indent << scope_tab << scope_tab  << "return field.GetHashCode();\n"
607               ).generate(sink, attributes::unused, context))
608             return false;
609        }
610 
611      if (!as_generator(
612              indent << scope_tab << "}\n"
613           ).generate(sink, attributes::unused, context))
614        return false;
615 
616      // IEquatable<T> Equals
617      if (!as_generator(
618              indent << scope_tab << "/// <summary>Equality comparison.\n"
619              << since_line
620              << indent << scope_tab << "/// </summary>\n"
621              << indent << scope_tab << "public bool Equals(" << struct_managed_name << " other)\n"
622              << indent << scope_tab << "{\n"
623              << indent << scope_tab << scope_tab << "return "
624           ).generate(sink, attributes::unused, context))
625        return false;
626 
627      if (struct_.fields.size() != 0 )
628        {
629           if (!as_generator(
630                 grammar::attribute_reorder<-1, -1>((name_helpers::struct_property_name << " == other." << name_helpers::struct_property_name)) % " && "
631               ).generate(sink, struct_.fields, context))
632             return false;
633        }
634      else
635        {
636           // Just compare the place holder pointers
637           if (!as_generator(
638                 "field.Equals(other.field)"
639               ).generate(sink, attributes::unused, context))
640             return false;
641        }
642 
643 
644      if (!as_generator(
645              ";\n"
646              << indent << scope_tab << "}\n"
647           ).generate(sink, attributes::unused, context))
648       return false;
649 
650      // ValueType.Equals
651      if (!as_generator(
652            indent << scope_tab << "/// <summary>Equality comparison.\n"
653            << since_line
654            << indent << scope_tab << "/// </summary>\n"
655            << indent << scope_tab << "public override bool Equals(object other)\n"
656            << indent << scope_tab << scope_tab << "=> ((other is " << struct_managed_name  << ") ? Equals((" << struct_managed_name << ")other) : false);\n"
657         ).generate(sink, attributes::unused, context))
658        return false;
659 
660      // Equality operators
661      if (!as_generator(
662            indent << scope_tab << "/// <summary>Equality comparison.\n"
663            << since_line
664            << indent << scope_tab << "/// </summary>\n"
665            << indent << scope_tab << "public static bool operator ==(" << struct_managed_name << " lhs, " << struct_managed_name << " rhs)\n"
666            << indent << scope_tab << scope_tab << "=> lhs.Equals(rhs);"
667         ).generate(sink, attributes::unused, context))
668        return false;
669 
670      if (!as_generator(
671            indent << scope_tab << "/// <summary>Equality comparison.\n"
672            << since_line
673            << indent << scope_tab << "/// </summary>\n"
674            << indent << scope_tab << "public static bool operator !=(" << struct_managed_name << " lhs, " << struct_managed_name << " rhs)\n"
675            << indent << scope_tab << scope_tab << "=> !lhs.Equals(rhs);"
676         ).generate(sink, attributes::unused, context))
677        return false;
678 
679      // Conversions from/to internal struct and IntPtrs
680      if(!as_generator(
681             indent << scope_tab << "/// <summary>Implicit conversion to the managed representation from a native pointer.\n"
682             ).generate(sink, attributes::unused, context))
683        return false;
684 
685      if (!struct_.documentation.since.empty())
686        if (!as_generator(indent << scope_tab << "/// <para>Since EFL " + struct_.documentation.since + ".</para>\n"
687             ).generate(sink, attributes::unused, context))
688          return false;
689 
690      if (!as_generator(
691             indent << scope_tab << "/// </summary>\n"
692             << indent << scope_tab << "/// <param name=\"ptr\">Native pointer to be converted.</param>\n"
693             << indent << scope_tab << "public static implicit operator " << struct_name << "(IntPtr ptr)\n"
694             << indent << scope_tab << "{\n"
695             << indent << scope_tab << scope_tab << "return (" << struct_name << ")Marshal.PtrToStructure(ptr, typeof(" << struct_name << "));\n"
696             << indent << scope_tab << "}\n\n"
697             ).generate(sink, attributes::unused, context))
698        return false;
699 
700     if(!as_generator(
701             indent << scope_tab << "/// <summary>Conversion to the managed representation from a native pointer.\n"
702             ).generate(sink, attributes::unused, context))
703        return false;
704 
705      if (!struct_.documentation.since.empty())
706        if (!as_generator(indent << scope_tab << "/// <para>Since EFL " + struct_.documentation.since + ".</para>\n"
707             ).generate(sink, attributes::unused, context))
708          return false;
709 
710      if (!as_generator(
711             indent << scope_tab << "/// </summary>\n"
712             << indent << scope_tab << "/// <param name=\"ptr\">Native pointer to be converted.</param>\n"
713             << indent << scope_tab << "public static " << struct_name << " FromIntPtr(IntPtr ptr)\n"
714             << indent << scope_tab << "{\n"
715             << indent << scope_tab << scope_tab << "return ptr;\n"
716             << indent << scope_tab << "}\n\n"
717             ).generate(sink, attributes::unused, context))
718        return false;
719 
720      if(!as_generator(indent << "}\n\n").generate(sink, attributes::unused, context)) return false;
721 
722      return true;
723   }
724 } const struct_definition {};
725 
726 struct struct_entities_generator
727 {
728   template <typename OutputIterator, typename Context>
generateeolian_mono::struct_entities_generator729   bool generate(OutputIterator sink, attributes::struct_def const& struct_, Context const& context) const
730   {
731      if (blacklist::is_struct_blacklisted(struct_, context))
732        return true;
733 
734      if (!name_helpers::open_namespaces(sink, struct_.namespaces, context))
735        return false;
736 
737      if (!struct_definition.generate(sink, struct_, context))
738        return false;
739 
740      return name_helpers::close_namespaces(sink, struct_.namespaces, context);
741 
742   }
743 } const struct_entities {};
744 
745 }
746 
747 namespace efl { namespace eolian { namespace grammar {
748 
749 template <>
750 struct is_eager_generator< ::eolian_mono::struct_definition_generator> : std::true_type {};
751 template <>
752 struct is_generator< ::eolian_mono::struct_definition_generator> : std::true_type {};
753 
754 template <>
755 struct is_eager_generator< ::eolian_mono::struct_private_property_generator> : std::true_type {};
756 template <>
757 struct is_generator< ::eolian_mono::struct_private_property_generator> : std::true_type {};
758 
759 template <>
760 struct is_eager_generator< ::eolian_mono::to_internal_field_convert_generator> : std::true_type {};
761 template <>
762 struct is_generator< ::eolian_mono::to_internal_field_convert_generator> : std::true_type {};
763 
764 template <>
765 struct is_eager_generator< ::eolian_mono::to_external_field_convert_generator> : std::true_type {};
766 template <>
767 struct is_generator< ::eolian_mono::to_external_field_convert_generator> : std::true_type {};
768 
769 template <>
770 struct is_eager_generator< ::eolian_mono::struct_entities_generator> : std::true_type {};
771 template <>
772 struct is_generator< ::eolian_mono::struct_entities_generator> : std::true_type {};
773 
774 namespace type_traits {
775 template <>
776 struct attributes_needed< ::eolian_mono::struct_definition_generator> : std::integral_constant<int, 1> {};
777 
778 template <>
779 struct attributes_needed< ::eolian_mono::struct_private_property_generator> : std::integral_constant<int, 1> {};
780 
781 template <>
782 struct attributes_needed< ::eolian_mono::to_internal_field_convert_generator> : std::integral_constant<int, 1> {};
783 
784 template <>
785 struct attributes_needed< ::eolian_mono::to_external_field_convert_generator> : std::integral_constant<int, 1> {};
786 
787 template <>
788 struct attributes_needed< ::eolian_mono::struct_entities_generator> : std::integral_constant<int, 1> {};
789 }
790 
791 } } }
792 
793 #endif
794