source: rrlib_rtti_conversion/tConversionOperationSequence.cpp @ 7:4cc15587a7e6

17.03
Last change on this file since 7:4cc15587a7e6 was 7:4cc15587a7e6, checked in by Max Reichardt <mreichardt@…>, 2 years ago

Adds support for std::array types - tohether with additional type conversion operations

File size: 23.6 KB
Line 
1//
2// You received this file as part of RRLib
3// Robotics Research Library
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    rrlib/rtti_conversion/tConversionOperationSequence.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2016-07-31
27 *
28 */
29//----------------------------------------------------------------------
30#include "rrlib/rtti_conversion/tConversionOperationSequence.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35
36//----------------------------------------------------------------------
37// Internal includes with ""
38//----------------------------------------------------------------------
39#include "rrlib/rtti_conversion/tCompiledConversionOperation.h"
40#include "rrlib/rtti_conversion/tStaticCastOperation.h"
41
42//----------------------------------------------------------------------
43// Debugging
44//----------------------------------------------------------------------
45#include <cassert>
46
47//----------------------------------------------------------------------
48// Namespace usage
49//----------------------------------------------------------------------
50
51//----------------------------------------------------------------------
52// Namespace declaration
53//----------------------------------------------------------------------
54namespace rrlib
55{
56namespace rtti
57{
58namespace conversion
59{
60
61//----------------------------------------------------------------------
62// Forward declarations / typedefs / enums
63//----------------------------------------------------------------------
64
65//----------------------------------------------------------------------
66// Const values
67//----------------------------------------------------------------------
68
69const tConversionOperationSequence tConversionOperationSequence::cNONE;
70const tTypedConstPointer tConversionOperationSequence::cNO_PARAMETER_VALUE;
71
72/*! Enum for binary serialization */
73enum tOperationSerializationFlags : uint8_t { cFULL_OPERATION = 1, cPARAMETER = 2 };
74
75//----------------------------------------------------------------------
76// Implementation
77//----------------------------------------------------------------------
78
79tConversionOperationSequence::tConversionOperationSequence(const std::string& first, const std::string& second, const tType& intermediate_type) :
80  operations {nullptr, nullptr},
81           ambiguous_operation_lookup {false, false},
82           intermediate_type(intermediate_type)
83{
84  auto find_result = tRegisteredConversionOperation::Find(first);
85  operations[0].operation = find_result.first;
86  ambiguous_operation_lookup[0] = find_result.second;
87  if (operations[0].operation == nullptr)
88  {
89    throw std::runtime_error("Could not find registered conversion operation with name: " + first);
90  }
91  if (second.length())
92  {
93    find_result = tRegisteredConversionOperation::Find(second);
94    operations[1].operation = find_result.first;
95    ambiguous_operation_lookup[1] = find_result.second;
96    if (operations[1].operation == nullptr)
97    {
98      throw std::runtime_error("Could not find registered conversion operation with name: " + second);
99    }
100  }
101}
102
103tConversionOperationSequence& tConversionOperationSequence::operator=(const tConversionOperationSequence & other)
104{
105  operations[0].operation = other.operations[0].operation;
106  operations[1].operation = other.operations[1].operation;
107  CopyParameter(other.operations[0].parameter ? *other.operations[0].parameter : tTypedConstPointer(), operations[0].parameter);
108  CopyParameter(other.operations[1].parameter ? *other.operations[1].parameter : tTypedConstPointer(), operations[1].parameter);
109  memcpy(ambiguous_operation_lookup, other.ambiguous_operation_lookup, sizeof(ambiguous_operation_lookup));
110  intermediate_type = other.intermediate_type;
111  return *this;
112}
113
114tCompiledConversionOperation tConversionOperationSequence::Compile(bool allow_reference_to_source, const tType& source_type, const tType& destination_type) const
115{
116  // ############
117  // Resolve any ambiguous conversion operations
118  // ############
119  const tRegisteredConversionOperation* first_operation = operations[0].operation;
120  if (first_operation && ambiguous_operation_lookup[0])
121  {
122    first_operation = &tRegisteredConversionOperation::Find(first_operation->Name(), source_type, Size() == 2 ? intermediate_type : destination_type);
123  }
124  const tRegisteredConversionOperation* second_operation = operations[1].operation;
125  if (second_operation && ambiguous_operation_lookup[1])
126  {
127    second_operation = &tRegisteredConversionOperation::Find(first_operation->Name(), intermediate_type, destination_type);
128  }
129
130  // ############
131  // Infer conversion options
132  // This includes adding implicit conversions if less than two registered conversions are in chain
133  // ############
134
135  // Infer any undefined types
136  tType type_source = source_type;
137  tType type_destination = destination_type;
138  tType type_intermediate = this->intermediate_type;
139  if (!type_source)
140  {
141    type_source = first_operation ? first_operation->SupportedSourceTypes().single_type : tType();
142  }
143  if (!type_source)
144  {
145    throw std::runtime_error("Source type must be specified");
146  }
147  const tRegisteredConversionOperation* last_operation = second_operation ? second_operation : first_operation;
148  if (!type_destination)
149  {
150    type_destination = last_operation ? last_operation->SupportedDestinationTypes().single_type : tType();
151  }
152  if (!type_destination)
153  {
154    throw std::runtime_error("Destination type must be specified");
155  }
156  if ((!type_intermediate) && second_operation)
157  {
158    type_intermediate = first_operation ? first_operation->SupportedDestinationTypes().single_type : tType();
159    if (!type_intermediate)
160    {
161      type_intermediate = second_operation ? second_operation->SupportedSourceTypes().single_type : tType();
162    }
163    if (!type_intermediate)
164    {
165      throw std::runtime_error("Intermediate type must be specified");
166    }
167  }
168
169  // Variables for result
170  tConversionOption temp_conversion_option_1, temp_conversion_option_2;
171  const tConversionOption* conversion1 = nullptr;
172  const tConversionOption* conversion2 = nullptr;
173
174  // No conversion operation specified: Look for implicit cast
175  if ((!first_operation))
176  {
177    if (type_source == type_destination)
178    {
179      temp_conversion_option_1 = tConversionOption(type_source, type_destination, 0);
180      conversion1 = &temp_conversion_option_1;
181    }
182    else
183    {
184      auto implicit_conversion = tStaticCastOperation::GetImplicitConversionOptions(type_source, type_destination);
185      if (implicit_conversion.first.type == tConversionOptionType::NONE)
186      {
187        throw std::runtime_error("Type " + source_type.GetName() + " cannot be implicitly casted to " + destination_type.GetName());
188      }
189      temp_conversion_option_1 = implicit_conversion.first;
190      conversion1 = &temp_conversion_option_1;
191      if (implicit_conversion.second.type != tConversionOptionType::NONE)
192      {
193        temp_conversion_option_2 = implicit_conversion.second;
194        conversion2 = &temp_conversion_option_2;
195      }
196    }
197  }
198
199  // For each operation
200  else if (first_operation == &cFOR_EACH_OPERATION || first_operation == &cFOR_EACH_OPERATION_ARRAY)
201  {
202    if ((first_operation == &cFOR_EACH_OPERATION && (!(type_source.IsListType() && type_destination.IsListType()))) || (first_operation == &cFOR_EACH_OPERATION_ARRAY && (!(type_source.IsArray() && type_destination.IsArray() && type_source.GetArraySize() == destination_type.GetArraySize()))))
203    {
204      throw std::runtime_error("ForEach operation only applicable on list types and array types (of same size)");
205    }
206    if (!second_operation)
207    {
208      temp_conversion_option_2 = tStaticCastOperation::GetImplicitConversionOption(type_source.GetElementType(), type_destination.GetElementType());
209      if (temp_conversion_option_2.type == tConversionOptionType::NONE)
210      {
211        throw std::runtime_error("Type " + source_type.GetElementType().GetName() + " cannot be implicitly casted to " + destination_type.GetElementType().GetName() + ". Second operation for ForEach must be specified.");
212      }
213    }
214    else
215    {
216      temp_conversion_option_2 = second_operation->GetConversionOption(type_source.GetElementType(), type_destination.GetElementType(), operations[1].parameter.get());
217      if (temp_conversion_option_2.type == tConversionOptionType::NONE)
218      {
219        throw std::runtime_error("Type " + source_type.GetElementType().GetName() + " cannot be converted to " + destination_type.GetElementType().GetName() + " with the selected operations.");
220      }
221    }
222    conversion2 = &temp_conversion_option_2;
223    temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_destination, operations[0].parameter.get());
224    conversion1 = &temp_conversion_option_1;
225  }
226
227  // Two conversion operations specified: Check types
228  else if (second_operation)
229  {
230    temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_intermediate, operations[0].parameter.get());
231    temp_conversion_option_2 = second_operation->GetConversionOption(type_intermediate, type_destination, operations[1].parameter.get());
232    if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE)
233    {
234      conversion1 = &temp_conversion_option_1;
235      conversion2 = &temp_conversion_option_2;
236    }
237  }
238
239  // One conversion option specified: Is it enough - or do we need additional implicit cast?
240  else
241  {
242    temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_destination, operations[0].parameter.get());
243    if (temp_conversion_option_1.type != tConversionOptionType::NONE)
244    {
245      conversion1 = &temp_conversion_option_1;
246    }
247    else
248    {
249      // Try implicit cast as second
250      if (first_operation->SupportedSourceTypes().single_type == source_type && (first_operation->SupportedDestinationTypes().single_type || type_intermediate))
251      {
252        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedDestinationTypes().single_type;
253        temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_intermediate, operations[0].parameter.get());
254        temp_conversion_option_2 = tStaticCastOperation::GetImplicitConversionOption(type_intermediate, type_destination);
255      }
256      else if ((first_operation->SupportedSourceTypes().single_type || type_intermediate) && first_operation->SupportedDestinationTypes().single_type == destination_type)
257      {
258        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedSourceTypes().single_type;
259        temp_conversion_option_1 = tStaticCastOperation::GetImplicitConversionOption(type_source, type_intermediate);
260        temp_conversion_option_2 = first_operation->GetConversionOption(type_intermediate, type_destination, operations[1].parameter.get());
261      }
262      if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE)
263      {
264        conversion1 = &temp_conversion_option_1;
265        conversion2 = &temp_conversion_option_2;
266      }
267      else
268      {
269        throw std::runtime_error("Intermediate type must be specified");
270      }
271    }
272  }
273  if (!conversion1)
274  {
275    throw std::runtime_error("Type " + source_type.GetName() + " cannot be casted to " + destination_type.GetName() + " with the selected operations");
276  }
277
278
279  // ############
280  // Compile conversion operation from conversion options
281  // ############
282  typedef tCompiledConversionOperation::tFlag tFlag;
283  assert(conversion1);
284  const tConversionOption* last_conversion = conversion2 ? conversion2 : conversion1;
285
286  // Do some sanity checks
287  if ((conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && conversion1->const_offset_reference_to_source_object > std::numeric_limits<unsigned int>::max() / 2) ||
288      (last_conversion->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && last_conversion->const_offset_reference_to_source_object > std::numeric_limits<unsigned int>::max() / 2))
289  {
290    throw std::runtime_error("Invalid fixed offset in conversion option");
291  }
292
293  // Prepare result
294  tCompiledConversionOperation result;
295  result.operations[0].operation = first_operation;
296  result.operations[1].operation = second_operation;
297  result.destination_type = last_conversion->destination_type;
298
299  // Handle special case: only const offsets
300  if (conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && last_conversion->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
301  {
302    result.type_after_first_fixed_offset = result.destination_type;
303    result.intermediate_type = result.destination_type;
304    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object + (conversion2 ? conversion2->const_offset_reference_to_source_object : 0));
305    result.flags = tFlag::cRESULT_INDEPENDENT | tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY | tFlag::cDEEPCOPY_ONLY;
306    return result;
307  }
308
309  // Handle cases where first operation is const offset
310  bool first_op_is_const_offset = conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT;
311  result.type_after_first_fixed_offset = first_op_is_const_offset ? conversion1->destination_type : conversion1->source_type;
312  if (first_op_is_const_offset)
313  {
314    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object);
315
316    // first operation is done, so move second to first
317    conversion1 = nullptr;
318    std::swap(conversion1, conversion2);
319    result.flags |= tFlag::cFIRST_OPERATION_OPTIMIZED_AWAY;
320  }
321  result.intermediate_type = conversion1->destination_type;
322
323  // Single operation REFERENCES_SOURCE
324  if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT && (!conversion2))
325  {
326    result.conversion_function_first = allow_reference_to_source ? conversion1->final_conversion_function : conversion1->first_conversion_function;
327    result.flags = allow_reference_to_source ? tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY : (tFlag::cRESULT_INDEPENDENT | tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION);
328  }
329  // First operation is standard or REFERENCES_SOURCE
330  else if (conversion1->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION || conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
331  {
332    result.conversion_function_first = conversion2 ? conversion1->first_conversion_function : conversion1->final_conversion_function;
333    result.flags = tFlag::cRESULT_INDEPENDENT;
334    if (conversion2 && conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
335    {
336      result.conversion_function_final = conversion2->final_conversion_function;
337    }
338    else if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
339    {
340      if (conversion2->const_offset_reference_to_source_object == 0 && conversion2->source_type == conversion2->destination_type && (conversion1->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION || allow_reference_to_source))
341      {
342        result.conversion_function_first =  conversion1->final_conversion_function; // second operation can be optimized away
343        result.intermediate_type = result.destination_type;
344        if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
345        {
346          assert(allow_reference_to_source);
347          result.flags = tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
348        }
349      }
350      else
351      {
352        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
353        result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
354      }
355    }
356    else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
357    {
358      result.conversion_function_final = conversion2->first_conversion_function;
359      result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
360    }
361  }
362  // First operation is variable offset
363  else if (conversion1->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
364  {
365    bool reference_result = allow_reference_to_source && ((!conversion2) || conversion2->type != tConversionOptionType::STANDARD_CONVERSION_FUNCTION);
366    if (reference_result)
367    {
368      if (conversion2 && conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
369      {
370        result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
371        result.conversion_function_first = conversion1->first_conversion_function;
372        result.conversion_function_final = conversion2->final_conversion_function;
373      }
374      else
375      {
376        result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY;
377        result.get_destination_reference_function_first = conversion2->destination_reference_function;
378        if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
379        {
380          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
381        }
382        else if (conversion2 && conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
383        {
384          result.get_destination_reference_function_final = conversion2->destination_reference_function;
385        }
386      }
387    }
388    else
389    {
390      result.conversion_function_first = conversion1->first_conversion_function;
391      result.flags |= tFlag::cRESULT_INDEPENDENT;
392      if (!conversion2)
393      {
394        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
395      }
396      else if (conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
397      {
398        result.conversion_function_final = conversion2->final_conversion_function;
399      }
400      else if (conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
401      {
402        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
403        result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
404      }
405      else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
406      {
407        result.conversion_function_final = conversion2->first_conversion_function;
408        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
409      }
410    }
411  }
412
413  // ############
414  // Convert any parameters provided as strings to their required types
415  // ############
416  for (size_t i = 0; i < 2; i++)
417  {
418    const tRegisteredConversionOperation* operation = i == 0 ? first_operation : second_operation;
419    if (operation && operation->Parameter() && GetParameterValue(i))
420    {
421      const tTypedConstPointer& value = GetParameterValue(i);
422      if (value.GetType() == operation->Parameter().GetType())
423      {
424        CopyParameter(value, result.operations[i].parameter);
425      }
426      else if (value.GetType() == tDataType<std::string>())
427      {
428        serialization::tStringInputStream stream(*value.Get<std::string>());
429        result.operations[i].parameter.reset(operation->Parameter().GetType().CreateGenericObject());
430        result.operations[i].parameter->Deserialize(stream);
431      }
432      else
433      {
434        throw std::runtime_error(std::string("Parameter ") + operation->Parameter().GetName() + " has invalid type");
435      }
436    }
437  }
438
439  return result;
440}
441
442void tConversionOperationSequence::CopyParameter(const tTypedConstPointer& source, std::unique_ptr<tGenericObject>& destination)
443{
444  if (source)
445  {
446    if ((!destination) || destination->GetType() != source.GetType())
447    {
448      destination.reset(source.GetType().CreateGenericObject());
449      destination->DeepCopyFrom(source);
450    }
451  }
452  else
453  {
454    destination.reset();
455  }
456}
457
458void tConversionOperationSequence::SetParameterValue(size_t operation_index, const tTypedConstPointer& new_value)
459{
460  assert(operation_index < 2);
461  CopyParameter(new_value, operations[operation_index].parameter);
462}
463
464serialization::tOutputStream& operator << (serialization::tOutputStream& stream, const tConversionOperationSequence& sequence)
465{
466  stream.WriteByte(static_cast<uint8_t>(sequence.Size()));
467  for (size_t i = 0; i < sequence.Size(); i++)
468  {
469    auto operation = sequence[i];
470    auto parameter_value = sequence.GetParameterValue(i);
471    uint8_t flags = (operation.second ? cFULL_OPERATION : 0) | (parameter_value ? cPARAMETER : 0);
472    stream.WriteByte(flags);
473    if (operation.second)
474    {
475      stream << *operation.second;
476    }
477    else
478    {
479      assert(operation.first);
480      stream << operation.first;
481    }
482    if (parameter_value)
483    {
484      parameter_value.Serialize(stream);
485    }
486  }
487  if (sequence.Size() > 1)
488  {
489    stream << sequence.IntermediateType();
490  }
491  return stream;
492}
493
494serialization::tInputStream& operator >> (serialization::tInputStream& stream, tConversionOperationSequence& sequence)
495{
496  size_t size = stream.ReadByte();
497  if (size > 2)
498  {
499    throw std::runtime_error("Invalid sequence size");
500  }
501  for (size_t i = 0; i < 2; i++)
502  {
503    if (i >= size)
504    {
505      sequence.operations[i].operation = nullptr;
506      sequence.operations[i].parameter.reset();
507      sequence.ambiguous_operation_lookup[i] = false;
508    }
509    else
510    {
511      uint8_t flags = stream.ReadByte();
512      if (flags & cFULL_OPERATION)
513      {
514        sequence.operations[i].operation = tRegisteredConversionOperation::Deserialize(stream, true);
515        sequence.ambiguous_operation_lookup[i] = false;
516      }
517      else
518      {
519        std::string name = stream.ReadString();
520        auto search_result = tRegisteredConversionOperation::Find(name);
521        if (!search_result.first)
522        {
523          throw std::runtime_error("No conversion operation named " + name + " found");
524        }
525        sequence.operations[i].operation = search_result.first;
526        sequence.ambiguous_operation_lookup[i] = search_result.second;
527      }
528      if (flags & cPARAMETER)
529      {
530        if ((!sequence.operations[i].operation) || (!sequence.operations[i].operation->Parameter().GetType()))
531        {
532          throw std::runtime_error("No parameter defined in conversion operation to deserialize");
533        }
534        if ((!sequence.operations[i].parameter))
535        {
536          sequence.operations[i].parameter.reset(sequence.operations[i].operation->Parameter().GetType().CreateGenericObject());
537        }
538        sequence.operations[i].parameter->Deserialize(stream);
539      }
540      else
541      {
542        sequence.operations[i].parameter.reset();
543      }
544    }
545  }
546  if (size > 1)
547  {
548    stream >> sequence.intermediate_type;
549  }
550  else
551  {
552    sequence.intermediate_type = rrlib::rtti::tType();
553  }
554  return stream;
555}
556
557//----------------------------------------------------------------------
558// End of namespace declaration
559//----------------------------------------------------------------------
560}
561}
562}
Note: See TracBrowser for help on using the repository browser.