source: rrlib_rtti_conversion/tConversionOperationSequence.cpp @ 46:e34e2a90131d

17.03
Last change on this file since 46:e34e2a90131d was 46:e34e2a90131d, checked in by Max Reichardt <max.reichardt@…>, 13 months ago

Adds option for conversion operation parameters to externally handle and serialize them as string by default. This enables full tool support for conversion parameters of uncommon data types.

File size: 30.8 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, cSTRING_PARAMETER = 4 };
74
75//----------------------------------------------------------------------
76// Implementation
77//----------------------------------------------------------------------
78
79namespace
80{
81
82struct tConversionOptionWithOrigin : tConversionOption
83{
84  const tRegisteredConversionOperation* origin;
85
86  tConversionOptionWithOrigin(const tRegisteredConversionOperation* origin = nullptr, const tConversionOption& option = tConversionOption()) :
87    tConversionOption(option),
88    origin(origin)
89  {
90  }
91
92  void Set(const tRegisteredConversionOperation* origin, const tConversionOption& option)
93  {
94    this->origin = origin;
95    static_cast<tConversionOption&>(*this) = option;
96  }
97};
98
99tConversionOptionWithOrigin GetConversionOption(const tRegisteredConversionOperation* operation, const tType& source_type, const tType& destination_type, const tGenericObject* parameter)
100{
101  return tConversionOptionWithOrigin(operation, operation->GetConversionOption(source_type, destination_type, parameter));
102}
103
104tConversionOptionWithOrigin GetImplicitConversionOption(const tType& source_type, const tType& destination_type)
105{
106  return tConversionOptionWithOrigin(&tStaticCastOperation::GetInstance(), tStaticCastOperation::GetImplicitConversionOption(source_type, destination_type));
107}
108
109}
110
111tConversionOperationSequence::tConversionOperationSequence(const std::string& first, const std::string& second, const tType& intermediate_type) :
112  operations {nullptr, nullptr},
113           ambiguous_operation_lookup {false, false},
114           intermediate_type(intermediate_type)
115{
116  auto find_result = tRegisteredConversionOperation::Find(first);
117  operations[0].operation = find_result.first;
118  ambiguous_operation_lookup[0] = find_result.second;
119  if (operations[0].operation == nullptr)
120  {
121    throw std::runtime_error("Could not find registered conversion operation with name: " + first);
122  }
123  if (second.length())
124  {
125    find_result = tRegisteredConversionOperation::Find(second);
126    operations[1].operation = find_result.first;
127    ambiguous_operation_lookup[1] = find_result.second;
128    if (operations[1].operation == nullptr)
129    {
130      throw std::runtime_error("Could not find registered conversion operation with name: " + second);
131    }
132  }
133}
134
135tConversionOperationSequence& tConversionOperationSequence::operator=(const tConversionOperationSequence & other)
136{
137  operations[0].operation = other.operations[0].operation;
138  operations[1].operation = other.operations[1].operation;
139  CopyParameter(other.operations[0].parameter ? *other.operations[0].parameter : tTypedConstPointer(), operations[0].parameter);
140  CopyParameter(other.operations[1].parameter ? *other.operations[1].parameter : tTypedConstPointer(), operations[1].parameter);
141  memcpy(ambiguous_operation_lookup, other.ambiguous_operation_lookup, sizeof(ambiguous_operation_lookup));
142  intermediate_type = other.intermediate_type;
143  return *this;
144}
145
146tCompiledConversionOperation tConversionOperationSequence::Compile(bool allow_reference_to_source, const tType& source_type, const tType& destination_type) const
147{
148  // ############
149  // Resolve any ambiguous conversion operations
150  // ############
151  const tRegisteredConversionOperation* first_operation = operations[0].operation;
152  if (first_operation && ambiguous_operation_lookup[0])
153  {
154    first_operation = &tRegisteredConversionOperation::Find(first_operation->Name(), source_type, Size() == 2 ? intermediate_type : destination_type);
155  }
156  const tRegisteredConversionOperation* second_operation = operations[1].operation;
157  if (second_operation && ambiguous_operation_lookup[1])
158  {
159    if (first_operation == &cFOR_EACH_OPERATION)
160    {
161      second_operation = &tRegisteredConversionOperation::Find(second_operation->Name(), intermediate_type, destination_type.GetElementType());
162    }
163    else
164    {
165      second_operation = &tRegisteredConversionOperation::Find(second_operation->Name(), intermediate_type, destination_type);
166    }
167  }
168
169  // ############
170  // Infer conversion options
171  // This includes adding implicit conversions if less than two registered conversions are in chain
172  // ############
173
174  // Infer any undefined types
175  tType type_source = source_type;
176  tType type_destination = destination_type;
177  tType type_intermediate = this->intermediate_type;
178  if (!type_source)
179  {
180    type_source = first_operation && (!first_operation->SupportedSourceTypes().applies_to_underlying_types) ? first_operation->SupportedSourceTypes().single_type : tType();
181  }
182  if (!type_source)
183  {
184    throw std::runtime_error("Source type must be specified");
185  }
186  const tRegisteredConversionOperation* last_operation = second_operation ? second_operation : first_operation;
187  if (!type_destination)
188  {
189    type_destination = last_operation && (!last_operation->SupportedDestinationTypes().applies_to_underlying_types) ? last_operation->SupportedDestinationTypes().single_type : tType();
190  }
191  if (!type_destination)
192  {
193    throw std::runtime_error("Destination type must be specified");
194  }
195  if ((!type_intermediate) && second_operation)
196  {
197    std::vector<rrlib::rtti::tType> intermediate_type_candidates_operation_1 = first_operation->SupportedDestinationTypes(type_source);
198    std::vector<rrlib::rtti::tType> intermediate_type_candidates_operation_2 = first_operation->SupportedSourceTypes(type_destination);
199    size_t candidate_count = 0;
200    for (auto & candidate1 : intermediate_type_candidates_operation_1)
201    {
202      for (auto & candidate2 : intermediate_type_candidates_operation_2)
203      {
204        if (candidate1 == candidate2)
205        {
206          type_intermediate = candidate1;
207          candidate_count++;
208        }
209      }
210    }
211
212    if (candidate_count != 1)
213    {
214      throw std::runtime_error("Intermediate type must be specified (" + std::to_string(candidate_count) + " candidates)");
215    }
216  }
217
218  // Variables for result
219  tConversionOptionWithOrigin temp_conversion_option_1, temp_conversion_option_2;
220  const tConversionOptionWithOrigin* conversion1 = nullptr;
221  const tConversionOptionWithOrigin* conversion2 = nullptr;
222
223  // No conversion operation specified: Look for implicit cast
224  if ((!first_operation))
225  {
226    if (type_source == type_destination)
227    {
228      temp_conversion_option_1.Set(nullptr, tConversionOption(type_source, type_destination, 0));
229      conversion1 = &temp_conversion_option_1;
230    }
231    else
232    {
233      auto implicit_conversion = tStaticCastOperation::GetImplicitConversionOptions(type_source, type_destination);
234      if (implicit_conversion.first.type == tConversionOptionType::NONE)
235      {
236        throw std::runtime_error("Type " + source_type.GetName() + " cannot be implicitly casted to " + destination_type.GetName());
237      }
238      temp_conversion_option_1.Set(&tStaticCastOperation::GetInstance(), implicit_conversion.first);
239      conversion1 = &temp_conversion_option_1;
240      if (implicit_conversion.second.type != tConversionOptionType::NONE)
241      {
242        temp_conversion_option_2.Set(&tStaticCastOperation::GetInstance(), implicit_conversion.second);
243        conversion2 = &temp_conversion_option_2;
244      }
245    }
246  }
247
248  // For each operation
249  else if (first_operation == &cFOR_EACH_OPERATION)
250  {
251    rrlib::rtti::tType internal_type_source = type_source;
252    while (internal_type_source && (!(internal_type_source.IsListType() || internal_type_source.IsArray())))
253    {
254      internal_type_source = tRegisteredConversionOperation::GetUnderlyingTypeOperationsAreInheritedFrom(internal_type_source);
255    }
256    rrlib::rtti::tType internal_type_destination = type_destination;
257    while (internal_type_destination && (!(internal_type_destination.IsListType() || internal_type_destination.IsArray())))
258    {
259      internal_type_destination = tRegisteredConversionOperation::GetUnderlyingTypeOperationsAreInheritedFrom(internal_type_destination);
260    }
261
262    if ((!internal_type_source) || (!internal_type_destination))
263    {
264      throw std::runtime_error("ForEach operation only applicable on list types and array types");
265    }
266    if (!second_operation)
267    {
268      temp_conversion_option_2 = GetImplicitConversionOption(internal_type_source.GetElementType(), internal_type_destination.GetElementType());
269      if (temp_conversion_option_2.type == tConversionOptionType::NONE)
270      {
271        throw std::runtime_error("Type " + internal_type_source.GetElementType().GetName() + " cannot be implicitly casted to " + internal_type_destination.GetElementType().GetName() + ". Second operation for ForEach must be specified.");
272      }
273    }
274    else
275    {
276      temp_conversion_option_2 = GetConversionOption(second_operation, internal_type_source.GetElementType(), internal_type_destination.GetElementType(), operations[1].parameter.get());
277      if (temp_conversion_option_2.type == tConversionOptionType::NONE)
278      {
279        throw std::runtime_error("Type " + source_type.GetElementType().GetName() + " cannot be converted to " + destination_type.GetElementType().GetName() + " with the selected operations.");
280      }
281    }
282    conversion2 = &temp_conversion_option_2;
283    temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_destination, operations[0].parameter.get());
284    conversion1 = &temp_conversion_option_1;
285  }
286
287  // Two conversion operations specified: Check types
288  else if (second_operation)
289  {
290    temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_intermediate, operations[0].parameter.get());
291    temp_conversion_option_2 = GetConversionOption(second_operation, type_intermediate, type_destination, operations[1].parameter.get());
292    if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE)
293    {
294      conversion1 = &temp_conversion_option_1;
295      conversion2 = &temp_conversion_option_2;
296    }
297  }
298
299  // One conversion option specified: Is it enough - or do we need additional implicit cast?
300  else
301  {
302    temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_destination, operations[0].parameter.get());
303    if (temp_conversion_option_1.type != tConversionOptionType::NONE)
304    {
305      conversion1 = &temp_conversion_option_1;
306    }
307    else
308    {
309      // Try implicit cast as second
310      if (tRegisteredConversionOperation::InheritsOperationsFrom(first_operation->SupportedSourceTypes().single_type, type_source) && ((first_operation->SupportedDestinationTypes().single_type && (!first_operation->SupportedDestinationTypes().applies_to_underlying_types)) || type_intermediate))
311      {
312        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedDestinationTypes().single_type;
313        temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_intermediate, operations[0].parameter.get());
314        temp_conversion_option_2 = GetImplicitConversionOption(type_intermediate, type_destination);
315      }
316      else if (((first_operation->SupportedSourceTypes().single_type && (!first_operation->SupportedSourceTypes().applies_to_underlying_types)) || type_intermediate) && tRegisteredConversionOperation::InheritsOperationsFrom(first_operation->SupportedDestinationTypes().single_type, destination_type))
317      {
318        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedSourceTypes().single_type;
319        temp_conversion_option_1 = GetImplicitConversionOption(type_source, type_intermediate);
320        temp_conversion_option_2 = GetConversionOption(first_operation, type_intermediate, type_destination, operations[1].parameter.get());
321      }
322      if (temp_conversion_option_1.type == tConversionOptionType::NONE && (!intermediate_type))
323      {
324        RRLIB_LOG_PRINT(DEBUG_VERBOSE_1, "Attempting to resolve intermediate type for ambiguous cast from ", type_source, " to ", type_destination, " with operation ", first_operation->Name());
325        std::vector<tType> from_source_options = first_operation->SupportedDestinationTypes(source_type);
326        std::vector<tType> to_destination_options = first_operation->SupportedSourceTypes(destination_type);
327        int combinations_found = 0;
328        for (auto & type : from_source_options)
329        {
330          auto implicit_option = GetImplicitConversionOption(type, type_destination);
331          if (implicit_option.type != tConversionOptionType::NONE)
332          {
333            combinations_found++;
334            type_intermediate = type;
335            temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_intermediate, operations[0].parameter.get());
336            temp_conversion_option_2 = GetImplicitConversionOption(type_intermediate, type_destination);
337          }
338        }
339        for (auto & type : to_destination_options)
340        {
341          auto implicit_option = GetImplicitConversionOption(type_source, type);
342          if (implicit_option.type != tConversionOptionType::NONE)
343          {
344            combinations_found++;
345            type_intermediate = type;
346            temp_conversion_option_1 = GetImplicitConversionOption(type_source, type_intermediate);
347            temp_conversion_option_2 = GetConversionOption(first_operation, type_intermediate, type_destination, operations[1].parameter.get());
348          }
349        }
350        if (combinations_found > 1)
351        {
352          std::stringstream error_message;
353          error_message << "Found " << combinations_found << " variants to convert " << type_source << " to " << type_destination << " with operation " << first_operation->Name() << " and an implicit cast";
354          if (combinations_found)
355          {
356            error_message << ". Intermediate type must be specified";
357          }
358          RRLIB_LOG_PRINT(DEBUG_VERBOSE_1, error_message.str());
359          throw std::runtime_error(error_message.str());
360        }
361        else if (combinations_found == 1)
362        {
363          RRLIB_LOG_PRINT(DEBUG_VERBOSE_1, "Resolved intermediate type ", temp_conversion_option_1.destination_type, " in cast from ", type_source, " to ", type_destination, " with operation ", first_operation->Name(), " and an implicit cast (", from_source_options.size(), " options from source; ", to_destination_options.size(), " to destination)");
364        }
365        else
366        {
367          RRLIB_LOG_PRINT(DEBUG_VERBOSE_1, "Not found");
368        }
369      }
370
371      if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE)
372      {
373        conversion1 = &temp_conversion_option_1;
374        conversion2 = &temp_conversion_option_2;
375      }
376      else
377      {
378        throw std::runtime_error("Intermediate type must be specified");
379      }
380    }
381  }
382  if (!conversion1)
383  {
384    throw std::runtime_error("Type " + source_type.GetName() + " cannot be casted to " + destination_type.GetName() + " with the selected operations");
385  }
386
387
388  // ############
389  // Compile conversion operation from conversion options
390  // ############
391  typedef tCompiledConversionOperation::tFlag tFlag;
392  assert(conversion1);
393  const tConversionOptionWithOrigin* last_conversion = conversion2 ? conversion2 : conversion1;
394
395  // Do some sanity checks
396  if ((conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && conversion1->const_offset_reference_to_source_object > std::numeric_limits<unsigned int>::max() / 2) ||
397      (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))
398  {
399    throw std::runtime_error("Invalid fixed offset in conversion option");
400  }
401
402  // Prepare result
403  tCompiledConversionOperation result;
404  result.operations[0].operation = first_operation;
405  result.operations[1].operation = second_operation;
406  result.destination_type = last_conversion->destination_type;
407  static_cast<tConversionOperationSequence&>(result).intermediate_type = this->intermediate_type;
408
409  // Handle special case: only const offsets
410  if (conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && last_conversion->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
411  {
412    result.type_after_first_fixed_offset = result.destination_type;
413    result.intermediate_type = result.destination_type;
414    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object + (conversion2 ? conversion2->const_offset_reference_to_source_object : 0));
415    result.flags = tFlag::cRESULT_INDEPENDENT | tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY | tFlag::cDEEPCOPY_ONLY;
416  }
417  else
418  {
419    // Handle cases where first operation is const offset
420    bool first_op_is_const_offset = conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT;
421    result.type_after_first_fixed_offset = first_op_is_const_offset ? conversion1->destination_type : conversion1->source_type;
422    if (first_op_is_const_offset)
423    {
424      result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object);
425
426      // first operation is done, so move second to first
427      conversion1 = nullptr;
428      std::swap(conversion1, conversion2);
429      result.flags |= tFlag::cFIRST_OPERATION_OPTIMIZED_AWAY;
430    }
431    result.intermediate_type = conversion1->destination_type;
432
433    // Single operation REFERENCES_SOURCE
434    if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT && (!conversion2))
435    {
436      result.conversion_function_first = allow_reference_to_source ? conversion1->final_conversion_function : conversion1->first_conversion_function;
437      result.flags = allow_reference_to_source ? tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY : (tFlag::cRESULT_INDEPENDENT | tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION);
438    }
439    // First operation is standard or REFERENCES_SOURCE
440    else if (conversion1->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION || conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
441    {
442      result.conversion_function_first = conversion2 ? conversion1->first_conversion_function : conversion1->final_conversion_function;
443      result.flags = tFlag::cRESULT_INDEPENDENT;
444      if (conversion2 && conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
445      {
446        result.conversion_function_final = conversion2->final_conversion_function;
447      }
448      else if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
449      {
450        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))
451        {
452          result.conversion_function_first = conversion1->final_conversion_function; // second operation can be optimized away
453          result.intermediate_type = result.destination_type;
454          if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
455          {
456            assert(allow_reference_to_source);
457            result.flags = tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
458          }
459        }
460        else
461        {
462          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
463          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
464        }
465      }
466      else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
467      {
468        result.conversion_function_final = conversion2->first_conversion_function;
469        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
470      }
471    }
472    // First operation is variable offset
473    else if (conversion1->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
474    {
475      bool reference_result = allow_reference_to_source && ((!conversion2) || conversion2->type != tConversionOptionType::STANDARD_CONVERSION_FUNCTION);
476      if (reference_result)
477      {
478        if (conversion2 && conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
479        {
480          result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
481          result.conversion_function_first = conversion1->first_conversion_function;
482          result.conversion_function_final = conversion2->final_conversion_function;
483        }
484        else
485        {
486          result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY;
487          result.get_destination_reference_function_first = conversion2->destination_reference_function;
488          if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
489          {
490            result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
491          }
492          else if (conversion2 && conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
493          {
494            result.get_destination_reference_function_final = conversion2->destination_reference_function;
495          }
496        }
497      }
498      else
499      {
500        result.conversion_function_first = conversion1->first_conversion_function;
501        result.flags |= tFlag::cRESULT_INDEPENDENT;
502        if (!conversion2)
503        {
504          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
505        }
506        else if (conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
507        {
508          result.conversion_function_final = conversion2->final_conversion_function;
509        }
510        else if (conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
511        {
512          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
513          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
514        }
515        else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
516        {
517          result.conversion_function_final = conversion2->first_conversion_function;
518          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
519        }
520      }
521    }
522  }
523
524  // ############
525  // Convert any parameters provided as strings to their required types
526  // ############
527  for (size_t i = 0; i < 2; i++)
528  {
529    const tRegisteredConversionOperation* operation = i == 0 ? first_operation : second_operation;
530    if (operation && operation->Parameter() && GetParameterValue(i))
531    {
532      const tTypedConstPointer& value = GetParameterValue(i);
533      if (value.GetType() == operation->Parameter().GetType())
534      {
535        CopyParameter(value, result.operations[i].parameter);
536      }
537      else if (value.GetType() == tDataType<std::string>())
538      {
539        serialization::tStringInputStream stream(*value.Get<std::string>());
540        result.operations[i].parameter.reset(operation->Parameter().GetType().CreateGenericObject());
541        result.operations[i].parameter->Deserialize(stream);
542      }
543      else
544      {
545        throw std::runtime_error(std::string("Parameter ") + operation->Parameter().GetName() + " has invalid type");
546      }
547    }
548  }
549
550  // Optional operation-specific processing
551  if ((result.conversion_function_first || result.get_destination_reference_function_first) && conversion1 && conversion1->origin)
552  {
553    auto custom_operation_data = result.CustomOperationData(0);
554    conversion1->origin->OnCompile(*conversion1, custom_operation_data);
555  }
556  if ((result.conversion_function_final || result.get_destination_reference_function_final) && conversion2 && conversion2->origin)
557  {
558    auto custom_operation_data = result.CustomOperationData(1);
559    conversion2->origin->OnCompile(*conversion2, custom_operation_data);
560  }
561
562  return result;
563}
564
565void tConversionOperationSequence::CopyParameter(const tTypedConstPointer& source, std::unique_ptr<tGenericObject>& destination)
566{
567  if (source)
568  {
569    if ((!destination) || destination->GetType() != source.GetType())
570    {
571      destination.reset(source.GetType().CreateGenericObject());
572    }
573    destination->DeepCopyFrom(source);
574  }
575  else
576  {
577    destination.reset();
578  }
579}
580
581void tConversionOperationSequence::SetParameterValue(size_t operation_index, const tTypedConstPointer& new_value)
582{
583  assert(operation_index < 2);
584  CopyParameter(new_value, operations[operation_index].parameter);
585}
586
587serialization::tOutputStream& operator << (serialization::tOutputStream& stream, const tConversionOperationSequence& sequence)
588{
589  stream.WriteByte(static_cast<uint8_t>(sequence.Size()));
590  for (size_t i = 0; i < sequence.Size(); i++)
591  {
592    auto operation = sequence[i];
593    auto parameter_value = sequence.GetParameterValue(i);
594    uint8_t flags = (operation.second ? cFULL_OPERATION : 0) | (parameter_value && operation.second && (!operation.second->ParameterHandlingAsStringIsDefault()) && parameter_value.GetType() == operation.second->Parameter().GetType() ? cPARAMETER : (parameter_value ? cSTRING_PARAMETER : 0));
595    stream.WriteByte(flags);
596    if (operation.second)
597    {
598      stream << *operation.second;
599    }
600    else
601    {
602      assert(operation.first);
603      stream << operation.first;
604    }
605    if (flags & cPARAMETER)
606    {
607      parameter_value.Serialize(stream);
608    }
609    else if (flags & cSTRING_PARAMETER)
610    {
611      if (parameter_value.GetType() == tDataType<std::string>())
612      {
613        stream << *parameter_value.Get<std::string>();
614      }
615      else
616      {
617        parameter_value.Serialize(stream, serialization::tDataEncoding::STRING);
618      }
619    }
620  }
621  if (sequence.Size() > 1)
622  {
623    stream << sequence.IntermediateType();
624  }
625  return stream;
626}
627
628serialization::tInputStream& operator >> (serialization::tInputStream& stream, tConversionOperationSequence& sequence)
629{
630  size_t size = stream.ReadByte();
631  if (size > 2)
632  {
633    throw std::runtime_error("Invalid sequence size");
634  }
635  for (size_t i = 0; i < 2; i++)
636  {
637    if (i >= size)
638    {
639      sequence.operations[i].operation = nullptr;
640      sequence.operations[i].parameter.reset();
641      sequence.ambiguous_operation_lookup[i] = false;
642    }
643    else
644    {
645      uint8_t flags = stream.ReadByte();
646      if (flags & cFULL_OPERATION)
647      {
648        sequence.operations[i].operation = tRegisteredConversionOperation::Deserialize(stream, true);
649        sequence.ambiguous_operation_lookup[i] = false;
650      }
651      else
652      {
653        std::string name = stream.ReadString();
654        auto search_result = tRegisteredConversionOperation::Find(name);
655        if (!search_result.first)
656        {
657          throw std::runtime_error("No conversion operation named " + name + " found");
658        }
659        sequence.operations[i].operation = search_result.first;
660        sequence.ambiguous_operation_lookup[i] = search_result.second;
661      }
662      if (flags & cPARAMETER)
663      {
664        if ((!sequence.operations[i].operation) || (!sequence.operations[i].operation->Parameter().GetType()))
665        {
666          throw std::runtime_error("No parameter defined in conversion operation to deserialize");
667        }
668        if ((!sequence.operations[i].parameter) || sequence.operations[i].parameter->GetType() != sequence.operations[i].operation->Parameter().GetType())
669        {
670          sequence.operations[i].parameter.reset(sequence.operations[i].operation->Parameter().GetType().CreateGenericObject());
671        }
672        sequence.operations[i].parameter->Deserialize(stream);
673      }
674      else if (flags & cSTRING_PARAMETER)
675      {
676        if (!sequence.operations[i].operation)
677        {
678          throw std::runtime_error("No conversion operation found");
679        }
680        if ((!sequence.operations[i].parameter) || sequence.operations[i].parameter->GetType() != tDataType<std::string>())
681        {
682          sequence.operations[i].parameter.reset(tDataType<std::string>().CreateGenericObject());
683        }
684        stream >> sequence.operations[i].parameter->GetData<std::string>();
685      }
686      else
687      {
688        sequence.operations[i].parameter.reset();
689      }
690    }
691  }
692  if (size > 1)
693  {
694    stream >> sequence.intermediate_type;
695  }
696  else
697  {
698    sequence.intermediate_type = rrlib::rtti::tType();
699  }
700  return stream;
701}
702
703//----------------------------------------------------------------------
704// End of namespace declaration
705//----------------------------------------------------------------------
706}
707}
708}
Note: See TracBrowser for help on using the repository browser.