source: rrlib_rtti_conversion/tConversionOperationSequence.cpp @ 3:c3f0aa1ccd48

Last change on this file since 3:c3f0aa1ccd48 was 3:c3f0aa1ccd48, checked in by Max Reichardt <mreichardt@…>, 3 years ago

Fixes bug in construction of tConversionOptions and serialization of tConversionOperationSequence

File size: 22.7 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    {
204      throw std::runtime_error("ForEach operation only applicable on list types");
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());
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 = cFOR_EACH_OPERATION.GetConversionOption(type_source, type_destination);
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);
231    temp_conversion_option_2 = second_operation->GetConversionOption(type_intermediate, type_destination);
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);
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);
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);
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->const_offset_reference_to_source_object > std::numeric_limits<unsigned int>::max() / 2 || last_conversion->const_offset_reference_to_source_object > std::numeric_limits<unsigned int>::max() / 2)
288  {
289    throw std::runtime_error("Invalid fixed offset in conversion option");
290  }
291
292  // Prepare result
293  tCompiledConversionOperation result;
294  result.operations[0].operation = first_operation;
295  result.operations[1].operation = second_operation;
296  result.destination_type = last_conversion->destination_type;
297
298  // Handle special case: only const offsets
299  if (conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && last_conversion->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
300  {
301    result.type_after_first_fixed_offset = result.destination_type;
302    result.intermediate_type = result.destination_type;
303    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object + (conversion2 ? conversion2->const_offset_reference_to_source_object : 0));
304    result.flags = tFlag::cRESULT_INDEPENDENT | tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY | tFlag::cDEEPCOPY_ONLY;
305    return result;
306  }
307
308  // Handle cases where first operation is const offset
309  bool first_op_is_const_offset = conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT;
310  result.type_after_first_fixed_offset = first_op_is_const_offset ? conversion1->destination_type : conversion1->source_type;
311  if (first_op_is_const_offset)
312  {
313    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object);
314
315    // first operation is done, so move second to first
316    conversion1 = nullptr;
317    std::swap(conversion1, conversion2);
318    result.flags |= tFlag::cFIRST_OPERATION_OPTIMIZED_AWAY;
319  }
320  result.intermediate_type = conversion1->destination_type;
321
322  // Single operation REFERENCES_SOURCE
323  if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT && (!conversion2))
324  {
325    result.conversion_function_first = allow_reference_to_source ? conversion1->final_conversion_function : conversion1->first_conversion_function;
326    result.flags = allow_reference_to_source ? tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY : (tFlag::cRESULT_INDEPENDENT | tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION);
327  }
328  // First operation is standard or REFERENCES_SOURCE
329  else if (conversion1->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION || conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
330  {
331    result.conversion_function_first = conversion2 ? conversion1->first_conversion_function : conversion1->final_conversion_function;
332    result.flags = tFlag::cRESULT_INDEPENDENT;
333    if (conversion2 && conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
334    {
335      result.conversion_function_final = conversion2->final_conversion_function;
336    }
337    else if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
338    {
339      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))
340      {
341        result.conversion_function_first =  conversion1->final_conversion_function; // second operation can be optimized away
342        result.intermediate_type = result.destination_type;
343        if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
344        {
345          assert(allow_reference_to_source);
346          result.flags = tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
347        }
348      }
349      else
350      {
351        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
352        result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
353      }
354    }
355    else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
356    {
357      result.conversion_function_final = conversion2->first_conversion_function;
358      result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
359    }
360  }
361  // First operation is variable offset
362  else if (conversion1->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
363  {
364    bool reference_result = allow_reference_to_source && ((!conversion2) || conversion2->type != tConversionOptionType::STANDARD_CONVERSION_FUNCTION);
365    if (reference_result)
366    {
367      if (conversion2 && conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
368      {
369        result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
370        result.conversion_function_first = conversion1->first_conversion_function;
371        result.conversion_function_final = conversion2->final_conversion_function;
372      }
373      else
374      {
375        result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY;
376        result.get_destination_reference_function_first = conversion2->destination_reference_function;
377        if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
378        {
379          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
380        }
381        else if (conversion2 && conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
382        {
383          result.get_destination_reference_function_final = conversion2->destination_reference_function;
384        }
385      }
386    }
387    else
388    {
389      result.conversion_function_first = conversion1->first_conversion_function;
390      result.flags |= tFlag::cRESULT_INDEPENDENT;
391      if (!conversion2)
392      {
393        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
394      }
395      else if (conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
396      {
397        result.conversion_function_final = conversion2->final_conversion_function;
398      }
399      else if (conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
400      {
401        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
402        result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
403      }
404      else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
405      {
406        result.conversion_function_final = conversion2->first_conversion_function;
407        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
408      }
409    }
410  }
411
412  // ############
413  // Convert any parameters provided as strings to their required types
414  // ############
415  for (size_t i = 0; i < 2; i++)
416  {
417    const tRegisteredConversionOperation* operation = i == 0 ? first_operation : second_operation;
418    if (operation && operation->Parameter() && GetParameterValue(i))
419    {
420      const tTypedConstPointer& value = GetParameterValue(i);
421      if (value.GetType() == operation->Parameter().GetType())
422      {
423        CopyParameter(value, result.operations[i].parameter);
424      }
425      else if (value.GetType() == tDataType<std::string>())
426      {
427        serialization::tStringInputStream stream(*value.Get<std::string>());
428        result.operations[i].parameter.reset(operation->Parameter().GetType().CreateGenericObject());
429        result.operations[i].parameter->Deserialize(stream);
430      }
431      else
432      {
433        throw std::runtime_error(std::string("Parameter ") + operation->Parameter().GetName() + " has invalid type");
434      }
435    }
436  }
437
438  return result;
439}
440
441void tConversionOperationSequence::CopyParameter(const tTypedConstPointer& source, std::unique_ptr<tGenericObject>& destination)
442{
443  if (source)
444  {
445    if ((!destination) || destination->GetType() != source.GetType())
446    {
447      destination.reset(source.GetType().CreateGenericObject());
448      destination->DeepCopyFrom(source);
449    }
450  }
451  else
452  {
453    destination.reset();
454  }
455}
456
457void tConversionOperationSequence::SetParameterValue(size_t operation_index, const tTypedConstPointer& new_value)
458{
459  assert(operation_index < 2);
460  CopyParameter(new_value, operations[operation_index].parameter);
461}
462
463serialization::tOutputStream& operator << (serialization::tOutputStream& stream, const tConversionOperationSequence& sequence)
464{
465  stream.WriteByte(static_cast<uint8_t>(sequence.Size()));
466  for (size_t i = 0; i < sequence.Size(); i++)
467  {
468    auto operation = sequence[i];
469    auto parameter_value = sequence.GetParameterValue(i);
470    uint8_t flags = (operation.second ? cFULL_OPERATION : 0) | (parameter_value ? cPARAMETER : 0);
471    stream.WriteByte(flags);
472    if (operation.second)
473    {
474      stream << *operation.second;
475    }
476    else
477    {
478      assert(operation.first);
479      stream << operation.first;
480    }
481    if (parameter_value)
482    {
483      parameter_value.Serialize(stream);
484    }
485  }
486  if (sequence.Size() > 1)
487  {
488    stream << sequence.IntermediateType();
489  }
490  return stream;
491}
492
493serialization::tInputStream& operator >> (serialization::tInputStream& stream, tConversionOperationSequence& sequence)
494{
495  size_t size = stream.ReadByte();
496  if (size > 2)
497  {
498    throw std::runtime_error("Invalid sequence size");
499  }
500  for (size_t i = 0; i < 2; i++)
501  {
502    if (i >= size)
503    {
504      sequence.operations[i].operation = nullptr;
505      sequence.operations[i].parameter.reset();
506      sequence.ambiguous_operation_lookup[i] = false;
507    }
508    else
509    {
510      uint8_t flags = stream.ReadByte();
511      if (flags & cFULL_OPERATION)
512      {
513        sequence.operations[i].operation = tRegisteredConversionOperation::Deserialize(stream, true);
514        sequence.ambiguous_operation_lookup[i] = false;
515      }
516      else
517      {
518        std::string name = stream.ReadString();
519        auto search_result = tRegisteredConversionOperation::Find(name);
520        if (!search_result.first)
521        {
522          throw std::runtime_error("No conversion operation named " + name + " found");
523        }
524        sequence.operations[i].operation = search_result.first;
525        sequence.ambiguous_operation_lookup[i] = search_result.second;
526      }
527      if (flags & cPARAMETER)
528      {
529        tType type;
530        stream >> type;
531        if ((!sequence.operations[i].parameter) || (sequence.operations[i].parameter->GetType() != type))
532        {
533          sequence.operations[i].parameter.reset(type.CreateGenericObject());
534        }
535        sequence.operations[i].parameter->Deserialize(stream);
536      }
537      else
538      {
539        sequence.operations[i].parameter.reset();
540      }
541    }
542  }
543  if (size > 1)
544  {
545    stream >> sequence.intermediate_type;
546  }
547  else
548  {
549    sequence.intermediate_type = rrlib::rtti::tType();
550  }
551  return stream;
552}
553
554//----------------------------------------------------------------------
555// End of namespace declaration
556//----------------------------------------------------------------------
557}
558}
559}
Note: See TracBrowser for help on using the repository browser.