source: rrlib_rtti_conversion/tConversionOperationSequence.cpp @ 8:b9554b04aa0f

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

Adds conversion operations for tuples - and unifies 'for-each' and 'get-element' conversion operations for vectors and arrays

File size: 23.5 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)
201  {
202    if (!((type_source.IsListType() && type_destination.IsListType()) ||
203          (type_source.IsArray() && type_destination.IsArray() && type_source.GetArraySize() == destination_type.GetArraySize()) ||
204          (type_source.IsArray() && type_destination.IsListType())))
205    {
206      throw std::runtime_error("ForEach operation only applicable on list types and array types");
207    }
208    if (!second_operation)
209    {
210      temp_conversion_option_2 = tStaticCastOperation::GetImplicitConversionOption(type_source.GetElementType(), type_destination.GetElementType());
211      if (temp_conversion_option_2.type == tConversionOptionType::NONE)
212      {
213        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.");
214      }
215    }
216    else
217    {
218      temp_conversion_option_2 = second_operation->GetConversionOption(type_source.GetElementType(), type_destination.GetElementType(), operations[1].parameter.get());
219      if (temp_conversion_option_2.type == tConversionOptionType::NONE)
220      {
221        throw std::runtime_error("Type " + source_type.GetElementType().GetName() + " cannot be converted to " + destination_type.GetElementType().GetName() + " with the selected operations.");
222      }
223    }
224    conversion2 = &temp_conversion_option_2;
225    temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_destination, operations[0].parameter.get());
226    conversion1 = &temp_conversion_option_1;
227  }
228
229  // Two conversion operations specified: Check types
230  else if (second_operation)
231  {
232    temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_intermediate, operations[0].parameter.get());
233    temp_conversion_option_2 = second_operation->GetConversionOption(type_intermediate, type_destination, operations[1].parameter.get());
234    if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE)
235    {
236      conversion1 = &temp_conversion_option_1;
237      conversion2 = &temp_conversion_option_2;
238    }
239  }
240
241  // One conversion option specified: Is it enough - or do we need additional implicit cast?
242  else
243  {
244    temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_destination, operations[0].parameter.get());
245    if (temp_conversion_option_1.type != tConversionOptionType::NONE)
246    {
247      conversion1 = &temp_conversion_option_1;
248    }
249    else
250    {
251      // Try implicit cast as second
252      if (first_operation->SupportedSourceTypes().single_type == source_type && (first_operation->SupportedDestinationTypes().single_type || type_intermediate))
253      {
254        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedDestinationTypes().single_type;
255        temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_intermediate, operations[0].parameter.get());
256        temp_conversion_option_2 = tStaticCastOperation::GetImplicitConversionOption(type_intermediate, type_destination);
257      }
258      else if ((first_operation->SupportedSourceTypes().single_type || type_intermediate) && first_operation->SupportedDestinationTypes().single_type == destination_type)
259      {
260        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedSourceTypes().single_type;
261        temp_conversion_option_1 = tStaticCastOperation::GetImplicitConversionOption(type_source, type_intermediate);
262        temp_conversion_option_2 = first_operation->GetConversionOption(type_intermediate, type_destination, operations[1].parameter.get());
263      }
264      if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE)
265      {
266        conversion1 = &temp_conversion_option_1;
267        conversion2 = &temp_conversion_option_2;
268      }
269      else
270      {
271        throw std::runtime_error("Intermediate type must be specified");
272      }
273    }
274  }
275  if (!conversion1)
276  {
277    throw std::runtime_error("Type " + source_type.GetName() + " cannot be casted to " + destination_type.GetName() + " with the selected operations");
278  }
279
280
281  // ############
282  // Compile conversion operation from conversion options
283  // ############
284  typedef tCompiledConversionOperation::tFlag tFlag;
285  assert(conversion1);
286  const tConversionOption* last_conversion = conversion2 ? conversion2 : conversion1;
287
288  // Do some sanity checks
289  if ((conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && conversion1->const_offset_reference_to_source_object > std::numeric_limits<unsigned int>::max() / 2) ||
290      (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))
291  {
292    throw std::runtime_error("Invalid fixed offset in conversion option");
293  }
294
295  // Prepare result
296  tCompiledConversionOperation result;
297  result.operations[0].operation = first_operation;
298  result.operations[1].operation = second_operation;
299  result.destination_type = last_conversion->destination_type;
300
301  // Handle special case: only const offsets
302  if (conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && last_conversion->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
303  {
304    result.type_after_first_fixed_offset = result.destination_type;
305    result.intermediate_type = result.destination_type;
306    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object + (conversion2 ? conversion2->const_offset_reference_to_source_object : 0));
307    result.flags = tFlag::cRESULT_INDEPENDENT | tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY | tFlag::cDEEPCOPY_ONLY;
308    return result;
309  }
310
311  // Handle cases where first operation is const offset
312  bool first_op_is_const_offset = conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT;
313  result.type_after_first_fixed_offset = first_op_is_const_offset ? conversion1->destination_type : conversion1->source_type;
314  if (first_op_is_const_offset)
315  {
316    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object);
317
318    // first operation is done, so move second to first
319    conversion1 = nullptr;
320    std::swap(conversion1, conversion2);
321    result.flags |= tFlag::cFIRST_OPERATION_OPTIMIZED_AWAY;
322  }
323  result.intermediate_type = conversion1->destination_type;
324
325  // Single operation REFERENCES_SOURCE
326  if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT && (!conversion2))
327  {
328    result.conversion_function_first = allow_reference_to_source ? conversion1->final_conversion_function : conversion1->first_conversion_function;
329    result.flags = allow_reference_to_source ? tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY : (tFlag::cRESULT_INDEPENDENT | tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION);
330  }
331  // First operation is standard or REFERENCES_SOURCE
332  else if (conversion1->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION || conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
333  {
334    result.conversion_function_first = conversion2 ? conversion1->first_conversion_function : conversion1->final_conversion_function;
335    result.flags = tFlag::cRESULT_INDEPENDENT;
336    if (conversion2 && conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
337    {
338      result.conversion_function_final = conversion2->final_conversion_function;
339    }
340    else if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
341    {
342      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))
343      {
344        result.conversion_function_first =  conversion1->final_conversion_function; // second operation can be optimized away
345        result.intermediate_type = result.destination_type;
346        if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
347        {
348          assert(allow_reference_to_source);
349          result.flags = tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
350        }
351      }
352      else
353      {
354        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
355        result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
356      }
357    }
358    else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
359    {
360      result.conversion_function_final = conversion2->first_conversion_function;
361      result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
362    }
363  }
364  // First operation is variable offset
365  else if (conversion1->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
366  {
367    bool reference_result = allow_reference_to_source && ((!conversion2) || conversion2->type != tConversionOptionType::STANDARD_CONVERSION_FUNCTION);
368    if (reference_result)
369    {
370      if (conversion2 && conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
371      {
372        result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
373        result.conversion_function_first = conversion1->first_conversion_function;
374        result.conversion_function_final = conversion2->final_conversion_function;
375      }
376      else
377      {
378        result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY;
379        result.get_destination_reference_function_first = conversion2->destination_reference_function;
380        if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
381        {
382          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
383        }
384        else if (conversion2 && conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
385        {
386          result.get_destination_reference_function_final = conversion2->destination_reference_function;
387        }
388      }
389    }
390    else
391    {
392      result.conversion_function_first = conversion1->first_conversion_function;
393      result.flags |= tFlag::cRESULT_INDEPENDENT;
394      if (!conversion2)
395      {
396        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
397      }
398      else if (conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
399      {
400        result.conversion_function_final = conversion2->final_conversion_function;
401      }
402      else if (conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
403      {
404        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
405        result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
406      }
407      else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
408      {
409        result.conversion_function_final = conversion2->first_conversion_function;
410        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
411      }
412    }
413  }
414
415  // ############
416  // Convert any parameters provided as strings to their required types
417  // ############
418  for (size_t i = 0; i < 2; i++)
419  {
420    const tRegisteredConversionOperation* operation = i == 0 ? first_operation : second_operation;
421    if (operation && operation->Parameter() && GetParameterValue(i))
422    {
423      const tTypedConstPointer& value = GetParameterValue(i);
424      if (value.GetType() == operation->Parameter().GetType())
425      {
426        CopyParameter(value, result.operations[i].parameter);
427      }
428      else if (value.GetType() == tDataType<std::string>())
429      {
430        serialization::tStringInputStream stream(*value.Get<std::string>());
431        result.operations[i].parameter.reset(operation->Parameter().GetType().CreateGenericObject());
432        result.operations[i].parameter->Deserialize(stream);
433      }
434      else
435      {
436        throw std::runtime_error(std::string("Parameter ") + operation->Parameter().GetName() + " has invalid type");
437      }
438    }
439  }
440
441  return result;
442}
443
444void tConversionOperationSequence::CopyParameter(const tTypedConstPointer& source, std::unique_ptr<tGenericObject>& destination)
445{
446  if (source)
447  {
448    if ((!destination) || destination->GetType() != source.GetType())
449    {
450      destination.reset(source.GetType().CreateGenericObject());
451      destination->DeepCopyFrom(source);
452    }
453  }
454  else
455  {
456    destination.reset();
457  }
458}
459
460void tConversionOperationSequence::SetParameterValue(size_t operation_index, const tTypedConstPointer& new_value)
461{
462  assert(operation_index < 2);
463  CopyParameter(new_value, operations[operation_index].parameter);
464}
465
466serialization::tOutputStream& operator << (serialization::tOutputStream& stream, const tConversionOperationSequence& sequence)
467{
468  stream.WriteByte(static_cast<uint8_t>(sequence.Size()));
469  for (size_t i = 0; i < sequence.Size(); i++)
470  {
471    auto operation = sequence[i];
472    auto parameter_value = sequence.GetParameterValue(i);
473    uint8_t flags = (operation.second ? cFULL_OPERATION : 0) | (parameter_value ? cPARAMETER : 0);
474    stream.WriteByte(flags);
475    if (operation.second)
476    {
477      stream << *operation.second;
478    }
479    else
480    {
481      assert(operation.first);
482      stream << operation.first;
483    }
484    if (parameter_value)
485    {
486      parameter_value.Serialize(stream);
487    }
488  }
489  if (sequence.Size() > 1)
490  {
491    stream << sequence.IntermediateType();
492  }
493  return stream;
494}
495
496serialization::tInputStream& operator >> (serialization::tInputStream& stream, tConversionOperationSequence& sequence)
497{
498  size_t size = stream.ReadByte();
499  if (size > 2)
500  {
501    throw std::runtime_error("Invalid sequence size");
502  }
503  for (size_t i = 0; i < 2; i++)
504  {
505    if (i >= size)
506    {
507      sequence.operations[i].operation = nullptr;
508      sequence.operations[i].parameter.reset();
509      sequence.ambiguous_operation_lookup[i] = false;
510    }
511    else
512    {
513      uint8_t flags = stream.ReadByte();
514      if (flags & cFULL_OPERATION)
515      {
516        sequence.operations[i].operation = tRegisteredConversionOperation::Deserialize(stream, true);
517        sequence.ambiguous_operation_lookup[i] = false;
518      }
519      else
520      {
521        std::string name = stream.ReadString();
522        auto search_result = tRegisteredConversionOperation::Find(name);
523        if (!search_result.first)
524        {
525          throw std::runtime_error("No conversion operation named " + name + " found");
526        }
527        sequence.operations[i].operation = search_result.first;
528        sequence.ambiguous_operation_lookup[i] = search_result.second;
529      }
530      if (flags & cPARAMETER)
531      {
532        if ((!sequence.operations[i].operation) || (!sequence.operations[i].operation->Parameter().GetType()))
533        {
534          throw std::runtime_error("No parameter defined in conversion operation to deserialize");
535        }
536        if ((!sequence.operations[i].parameter))
537        {
538          sequence.operations[i].parameter.reset(sequence.operations[i].operation->Parameter().GetType().CreateGenericObject());
539        }
540        sequence.operations[i].parameter->Deserialize(stream);
541      }
542      else
543      {
544        sequence.operations[i].parameter.reset();
545      }
546    }
547  }
548  if (size > 1)
549  {
550    stream >> sequence.intermediate_type;
551  }
552  else
553  {
554    sequence.intermediate_type = rrlib::rtti::tType();
555  }
556  return stream;
557}
558
559//----------------------------------------------------------------------
560// End of namespace declaration
561//----------------------------------------------------------------------
562}
563}
564}
Note: See TracBrowser for help on using the repository browser.