source: rrlib_rtti_conversion/tConversionOperationSequence.cpp @ 9:f909044a9f7b

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

Adds option to specify type conversions operations that are not usually combined (you could call combinations deprecated) and fixes various bugs found with unit test in finroc_plugins_runtime_construction

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