source: rrlib_rtti_conversion/tConversionOperationSequence.cpp @ 49:cdd4cf3eda0c

17.03 tip
Last change on this file since 49:cdd4cf3eda0c was 49:cdd4cf3eda0c, checked in by Max Reichardt <mreichardt@…>, 12 months ago

Allows to specify destination object pointer type passed to Convert function. Custom types may allow conversion operations to provide additional information (timestamps are an example).

File size: 31.1 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 std::type_info& destination_pointer_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  result.destination_pointer_type = &(first_operation == &cFOR_EACH_OPERATION ? typeid(tTypedPointer) : destination_pointer_type);  // Special For-Each operation works with standard typed pointers only
409
410  // Handle special case: only const offsets
411  if (conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT && last_conversion->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
412  {
413    result.type_after_first_fixed_offset = result.destination_type;
414    result.intermediate_type = result.destination_type;
415    result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object + (conversion2 ? conversion2->const_offset_reference_to_source_object : 0));
416    result.flags = tFlag::cRESULT_INDEPENDENT | tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY | tFlag::cDEEPCOPY_ONLY;
417  }
418  else
419  {
420    // Handle cases where first operation is const offset
421    bool first_op_is_const_offset = conversion1->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT;
422    result.type_after_first_fixed_offset = first_op_is_const_offset ? conversion1->destination_type : conversion1->source_type;
423    if (first_op_is_const_offset)
424    {
425      result.fixed_offset_first = static_cast<unsigned int>(conversion1->const_offset_reference_to_source_object);
426
427      // first operation is done, so move second to first
428      conversion1 = nullptr;
429      std::swap(conversion1, conversion2);
430      result.flags |= tFlag::cFIRST_OPERATION_OPTIMIZED_AWAY;
431    }
432    result.intermediate_type = conversion1->destination_type;
433
434    // Single operation REFERENCES_SOURCE
435    if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT && (!conversion2))
436    {
437      result.conversion_function_first = allow_reference_to_source ? conversion1->final_conversion_function : conversion1->first_conversion_function;
438      result.flags = allow_reference_to_source ? tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY : (tFlag::cRESULT_INDEPENDENT | tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION);
439    }
440    // First operation is standard or REFERENCES_SOURCE
441    else if (conversion1->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION || conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
442    {
443      result.conversion_function_first = conversion2 ? conversion1->first_conversion_function : conversion1->final_conversion_function;
444      result.flags = tFlag::cRESULT_INDEPENDENT;
445      if (conversion2 && conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
446      {
447        result.conversion_function_final = conversion2->final_conversion_function;
448      }
449      else if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
450      {
451        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))
452        {
453          result.conversion_function_first = conversion1->final_conversion_function; // second operation can be optimized away
454          result.intermediate_type = result.destination_type;
455          if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
456          {
457            assert(allow_reference_to_source);
458            result.flags = tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
459          }
460        }
461        else
462        {
463          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
464          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
465        }
466      }
467      else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
468      {
469        result.conversion_function_final = conversion2->first_conversion_function;
470        result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
471      }
472    }
473    // First operation is variable offset
474    else if (conversion1->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
475    {
476      bool reference_result = allow_reference_to_source && ((!conversion2) || conversion2->type != tConversionOptionType::STANDARD_CONVERSION_FUNCTION);
477      if (reference_result)
478      {
479        if (conversion2 && conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT)
480        {
481          result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_INTERNALLY;
482          result.conversion_function_first = conversion1->first_conversion_function;
483          result.conversion_function_final = conversion2->final_conversion_function;
484        }
485        else
486        {
487          result.flags |= tFlag::cRESULT_REFERENCES_SOURCE_DIRECTLY;
488          result.get_destination_reference_function_first = conversion2->destination_reference_function;
489          if (conversion2 && conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
490          {
491            result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
492          }
493          else if (conversion2 && conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
494          {
495            result.get_destination_reference_function_final = conversion2->destination_reference_function;
496          }
497        }
498      }
499      else
500      {
501        result.conversion_function_first = conversion1->first_conversion_function;
502        result.flags |= tFlag::cRESULT_INDEPENDENT;
503        if (!conversion2)
504        {
505          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
506        }
507        else if (conversion2->type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION)
508        {
509          result.conversion_function_final = conversion2->final_conversion_function;
510        }
511        else if (conversion2->type == tConversionOptionType::CONST_OFFSET_REFERENCE_TO_SOURCE_OBJECT)
512        {
513          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_FIRST_FUNCTION;
514          result.fixed_offset_final = conversion2->const_offset_reference_to_source_object;
515        }
516        else if (conversion2 && (conversion2->type == tConversionOptionType::VARIABLE_OFFSET_REFERENCE_TO_SOURCE_OBJECT || conversion2->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT))
517        {
518          result.conversion_function_final = conversion2->first_conversion_function;
519          result.flags |= tFlag::cDO_FINAL_DEEPCOPY_AFTER_SECOND_FUNCTION;
520        }
521      }
522    }
523  }
524
525  // ############
526  // Convert any parameters provided as strings to their required types
527  // ############
528  for (size_t i = 0; i < 2; i++)
529  {
530    const tRegisteredConversionOperation* operation = i == 0 ? first_operation : second_operation;
531    if (operation && operation->Parameter() && GetParameterValue(i))
532    {
533      const tTypedConstPointer& value = GetParameterValue(i);
534      if (value.GetType() == operation->Parameter().GetType())
535      {
536        CopyParameter(value, result.operations[i].parameter);
537      }
538      else if (value.GetType() == tDataType<std::string>())
539      {
540        serialization::tStringInputStream stream(*value.Get<std::string>());
541        result.operations[i].parameter.reset(operation->Parameter().GetType().CreateGenericObject());
542        result.operations[i].parameter->Deserialize(stream);
543      }
544      else
545      {
546        throw std::runtime_error(std::string("Parameter ") + operation->Parameter().GetName() + " has invalid type");
547      }
548    }
549  }
550
551  // Optional operation-specific processing
552  if ((result.conversion_function_first || result.get_destination_reference_function_first) && conversion1 && conversion1->origin)
553  {
554    auto custom_operation_data = result.CustomOperationData(0);
555    conversion1->origin->OnCompile(*conversion1, custom_operation_data);
556  }
557  if ((result.conversion_function_final || result.get_destination_reference_function_final) && conversion2 && conversion2->origin)
558  {
559    auto custom_operation_data = result.CustomOperationData(1);
560    conversion2->origin->OnCompile(*conversion2, custom_operation_data);
561  }
562
563  return result;
564}
565
566void tConversionOperationSequence::CopyParameter(const tTypedConstPointer& source, std::unique_ptr<tGenericObject>& destination)
567{
568  if (source)
569  {
570    if ((!destination) || destination->GetType() != source.GetType())
571    {
572      destination.reset(source.GetType().CreateGenericObject());
573    }
574    destination->DeepCopyFrom(source);
575  }
576  else
577  {
578    destination.reset();
579  }
580}
581
582void tConversionOperationSequence::SetParameterValue(size_t operation_index, const tTypedConstPointer& new_value)
583{
584  assert(operation_index < 2);
585  CopyParameter(new_value, operations[operation_index].parameter);
586}
587
588serialization::tOutputStream& operator << (serialization::tOutputStream& stream, const tConversionOperationSequence& sequence)
589{
590  stream.WriteByte(static_cast<uint8_t>(sequence.Size()));
591  for (size_t i = 0; i < sequence.Size(); i++)
592  {
593    auto operation = sequence[i];
594    auto parameter_value = sequence.GetParameterValue(i);
595    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));
596    stream.WriteByte(flags);
597    if (operation.second)
598    {
599      stream << *operation.second;
600    }
601    else
602    {
603      assert(operation.first);
604      stream << operation.first;
605    }
606    if (flags & cPARAMETER)
607    {
608      parameter_value.Serialize(stream);
609    }
610    else if (flags & cSTRING_PARAMETER)
611    {
612      if (parameter_value.GetType() == tDataType<std::string>())
613      {
614        stream << *parameter_value.Get<std::string>();
615      }
616      else
617      {
618        parameter_value.Serialize(stream, serialization::tDataEncoding::STRING);
619      }
620    }
621  }
622  if (sequence.Size() > 1)
623  {
624    stream << sequence.IntermediateType();
625  }
626  return stream;
627}
628
629serialization::tInputStream& operator >> (serialization::tInputStream& stream, tConversionOperationSequence& sequence)
630{
631  size_t size = stream.ReadByte();
632  if (size > 2)
633  {
634    throw std::runtime_error("Invalid sequence size");
635  }
636  for (size_t i = 0; i < 2; i++)
637  {
638    if (i >= size)
639    {
640      sequence.operations[i].operation = nullptr;
641      sequence.operations[i].parameter.reset();
642      sequence.ambiguous_operation_lookup[i] = false;
643    }
644    else
645    {
646      uint8_t flags = stream.ReadByte();
647      if (flags & cFULL_OPERATION)
648      {
649        sequence.operations[i].operation = tRegisteredConversionOperation::Deserialize(stream, true);
650        sequence.ambiguous_operation_lookup[i] = false;
651      }
652      else
653      {
654        std::string name = stream.ReadString();
655        auto search_result = tRegisteredConversionOperation::Find(name);
656        if (!search_result.first)
657        {
658          throw std::runtime_error("No conversion operation named " + name + " found");
659        }
660        sequence.operations[i].operation = search_result.first;
661        sequence.ambiguous_operation_lookup[i] = search_result.second;
662      }
663      if (flags & cPARAMETER)
664      {
665        if ((!sequence.operations[i].operation) || (!sequence.operations[i].operation->Parameter().GetType()))
666        {
667          throw std::runtime_error("No parameter defined in conversion operation to deserialize");
668        }
669        if ((!sequence.operations[i].parameter) || sequence.operations[i].parameter->GetType() != sequence.operations[i].operation->Parameter().GetType())
670        {
671          sequence.operations[i].parameter.reset(sequence.operations[i].operation->Parameter().GetType().CreateGenericObject());
672        }
673        sequence.operations[i].parameter->Deserialize(stream);
674      }
675      else if (flags & cSTRING_PARAMETER)
676      {
677        if (!sequence.operations[i].operation)
678        {
679          throw std::runtime_error("No conversion operation found");
680        }
681        if ((!sequence.operations[i].parameter) || sequence.operations[i].parameter->GetType() != tDataType<std::string>())
682        {
683          sequence.operations[i].parameter.reset(tDataType<std::string>().CreateGenericObject());
684        }
685        stream >> sequence.operations[i].parameter->GetData<std::string>();
686      }
687      else
688      {
689        sequence.operations[i].parameter.reset();
690      }
691    }
692  }
693  if (size > 1)
694  {
695    stream >> sequence.intermediate_type;
696  }
697  else
698  {
699    sequence.intermediate_type = rrlib::rtti::tType();
700  }
701  return stream;
702}
703
704//----------------------------------------------------------------------
705// End of namespace declaration
706//----------------------------------------------------------------------
707}
708}
709}
Note: See TracBrowser for help on using the repository browser.