Changeset 22:fed7ec8deba0 in rrlib_rtti_conversion


Ignore:
Timestamp:
05.02.2020 14:40:54 (3 weeks ago)
Author:
Max Reichardt <max.reichardt@…>
Branch:
17.03
Phase:
public
Message:

Further extends cases that static cast operation can handle - in particular (checked) casting to enums from any arithmetic type (and more variants of valid casts involving underlying types). For performance efficiency (regarding both computational overhead and binary size), custom operation data is introduced that may be filled during compilation of conversion operation sequences.

Files:
7 edited

Legend:

Unmodified
Added
Removed
  • tCompiledConversionOperation.h

    r16 r22  
    208208  /*! Flags for conversion operation */ 
    209209  unsigned int flags; 
     210 
     211  /*! 
     212   * Custom operation-specific data (e.g. may be filled during compilation for optimization) 
     213   * (note: if use cases appear that require more or different data, this should be extended) 
     214   */ 
     215  tCustomOperationData custom_operation_data[2]; 
    210216}; 
    211217 
     
    234240} 
    235241 
     242inline const tCustomOperationData& tCurrentConversionOperation::GetCustomData() const 
     243{ 
     244  return compiled_operation.custom_operation_data[operation_index]; 
     245} 
     246 
     247 
    236248//---------------------------------------------------------------------- 
    237249// End of namespace declaration 
  • tConversionOperationSequence.cpp

    r11 r22  
    7777//---------------------------------------------------------------------- 
    7878 
     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 
    79111tConversionOperationSequence::tConversionOperationSequence(const std::string& first, const std::string& second, const tType& intermediate_type) : 
    80112  operations {nullptr, nullptr}, 
     
    175207 
    176208  // Variables for result 
    177   tConversionOption temp_conversion_option_1, temp_conversion_option_2; 
    178   const tConversionOption* conversion1 = nullptr; 
    179   const tConversionOption* conversion2 = nullptr; 
     209  tConversionOptionWithOrigin temp_conversion_option_1, temp_conversion_option_2; 
     210  const tConversionOptionWithOrigin* conversion1 = nullptr; 
     211  const tConversionOptionWithOrigin* conversion2 = nullptr; 
    180212 
    181213  // No conversion operation specified: Look for implicit cast 
     
    184216    if (type_source == type_destination) 
    185217    { 
    186       temp_conversion_option_1 = tConversionOption(type_source, type_destination, 0); 
     218      temp_conversion_option_1.Set(nullptr, tConversionOption(type_source, type_destination, 0)); 
    187219      conversion1 = &temp_conversion_option_1; 
    188220    } 
     
    194226        throw std::runtime_error("Type " + source_type.GetName() + " cannot be implicitly casted to " + destination_type.GetName()); 
    195227      } 
    196       temp_conversion_option_1 = implicit_conversion.first; 
     228      temp_conversion_option_1.Set(&tStaticCastOperation::GetInstance(), implicit_conversion.first); 
    197229      conversion1 = &temp_conversion_option_1; 
    198230      if (implicit_conversion.second.type != tConversionOptionType::NONE) 
    199231      { 
    200         temp_conversion_option_2 = implicit_conversion.second; 
     232        temp_conversion_option_2.Set(&tStaticCastOperation::GetInstance(), implicit_conversion.second); 
    201233        conversion2 = &temp_conversion_option_2; 
    202234      } 
     
    215247    if (!second_operation) 
    216248    { 
    217       temp_conversion_option_2 = tStaticCastOperation::GetImplicitConversionOption(type_source.GetElementType(), type_destination.GetElementType()); 
     249      temp_conversion_option_2 = GetImplicitConversionOption(type_source.GetElementType(), type_destination.GetElementType()); 
    218250      if (temp_conversion_option_2.type == tConversionOptionType::NONE) 
    219251      { 
     
    223255    else 
    224256    { 
    225       temp_conversion_option_2 = second_operation->GetConversionOption(type_source.GetElementType(), type_destination.GetElementType(), operations[1].parameter.get()); 
     257      temp_conversion_option_2 = GetConversionOption(second_operation, type_source.GetElementType(), type_destination.GetElementType(), operations[1].parameter.get()); 
    226258      if (temp_conversion_option_2.type == tConversionOptionType::NONE) 
    227259      { 
     
    230262    } 
    231263    conversion2 = &temp_conversion_option_2; 
    232     temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_destination, operations[0].parameter.get()); 
     264    temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_destination, operations[0].parameter.get()); 
    233265    conversion1 = &temp_conversion_option_1; 
    234266  } 
     
    237269  else if (second_operation) 
    238270  { 
    239     temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_intermediate, operations[0].parameter.get()); 
    240     temp_conversion_option_2 = second_operation->GetConversionOption(type_intermediate, type_destination, operations[1].parameter.get()); 
     271    temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_intermediate, operations[0].parameter.get()); 
     272    temp_conversion_option_2 = GetConversionOption(second_operation, type_intermediate, type_destination, operations[1].parameter.get()); 
    241273    if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE) 
    242274    { 
     
    249281  else 
    250282  { 
    251     temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_destination, operations[0].parameter.get()); 
     283    temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_destination, operations[0].parameter.get()); 
    252284    if (temp_conversion_option_1.type != tConversionOptionType::NONE) 
    253285    { 
     
    260292      { 
    261293        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedDestinationTypes().single_type; 
    262         temp_conversion_option_1 = first_operation->GetConversionOption(type_source, type_intermediate, operations[0].parameter.get()); 
    263         temp_conversion_option_2 = tStaticCastOperation::GetImplicitConversionOption(type_intermediate, type_destination); 
     294        temp_conversion_option_1 = GetConversionOption(first_operation, type_source, type_intermediate, operations[0].parameter.get()); 
     295        temp_conversion_option_2 = GetImplicitConversionOption(type_intermediate, type_destination); 
    264296      } 
    265297      else if ((first_operation->SupportedSourceTypes().single_type || type_intermediate) && first_operation->SupportedDestinationTypes().single_type == destination_type) 
    266298      { 
    267299        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedSourceTypes().single_type; 
    268         temp_conversion_option_1 = tStaticCastOperation::GetImplicitConversionOption(type_source, type_intermediate); 
    269         temp_conversion_option_2 = first_operation->GetConversionOption(type_intermediate, type_destination, operations[1].parameter.get()); 
     300        temp_conversion_option_1 = GetImplicitConversionOption(type_source, type_intermediate); 
     301        temp_conversion_option_2 = GetConversionOption(first_operation, type_intermediate, type_destination, operations[1].parameter.get()); 
    270302      } 
    271303      if (temp_conversion_option_1.type != tConversionOptionType::NONE && temp_conversion_option_2.type != tConversionOptionType::NONE) 
     
    291323  typedef tCompiledConversionOperation::tFlag tFlag; 
    292324  assert(conversion1); 
    293   const tConversionOption* last_conversion = conversion2 ? conversion2 : conversion1; 
     325  const tConversionOptionWithOrigin* last_conversion = conversion2 ? conversion2 : conversion1; 
    294326 
    295327  // Do some sanity checks 
     
    350382        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)) 
    351383        { 
    352           result.conversion_function_first =  conversion1->final_conversion_function; // second operation can be optimized away 
     384          result.conversion_function_first = conversion1->final_conversion_function; // second operation can be optimized away 
    353385          result.intermediate_type = result.destination_type; 
    354386          if (conversion1->type == tConversionOptionType::RESULT_REFERENCES_SOURCE_OBJECT) 
     
    448480  } 
    449481 
     482  // Optional operation-specific processing 
     483  if ((result.conversion_function_first || result.get_destination_reference_function_first) && conversion1 && conversion1->origin) 
     484  { 
     485    conversion1->origin->OnCompile(*conversion1, result.custom_operation_data[0]); 
     486  } 
     487  if ((result.conversion_function_final || result.get_destination_reference_function_final) && conversion2 && conversion2->origin) 
     488  { 
     489    conversion2->origin->OnCompile(*conversion2, result.custom_operation_data[1]); 
     490  } 
     491 
    450492  return result; 
    451493} 
  • tCurrentConversionOperation.h

    r0 r22  
    9696   */ 
    9797  inline tTypedConstPointer GetParameterValue() const; 
     98 
     99  /*! 
     100   * \return Reference to custom data of current conversion operation 
     101   * (note: implemented in tCompiledConversionOperation.h to handle cyclic dependency) 
     102   */ 
     103  inline const tCustomOperationData& GetCustomData() const; 
    98104}; 
    99105 
  • tRegisteredConversionOperation.cpp

    r9 r22  
    230230} 
    231231 
     232void tRegisteredConversionOperation::OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     233{ 
     234} 
     235 
    232236tRegisteredConversionOperation::tRegisteredOperations& tRegisteredConversionOperation::RegisteredOperations() 
    233237{ 
  • tRegisteredConversionOperation.h

    r9 r22  
    6969struct tConversionOption; 
    7070struct tConversionOptionStaticCast; 
     71struct tCurrentConversionOperation; 
    7172 
    7273/*! 
     
    8889  ARRAY_TO_VECTOR,     //!< Types supported by array to vector operation 
    8990  GET_TUPLE_ELEMENT    //!< Types supported by get tuple element operation 
     91}; 
     92 
     93/*! 
     94 * Custom operation-specific data (e.g. may be filled during compilation for optimization) 
     95 * (note: if use cases appear that require more or different data, this should be extended) 
     96 */ 
     97struct tCustomOperationData 
     98{ 
     99  typedef void (*tConversionFunction)(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation); 
     100 
     101  union 
     102  { 
     103    tConversionFunction conversion_function = nullptr; 
     104    // to be extended 
     105  }; 
    90106}; 
    91107 
     
    292308  void AutoDelete(); 
    293309 
     310  /*! 
     311   * Optional post-processing when operation sequence is compiled. 
     312   * (standard implementation is empty) 
     313   * This is called in the last step of tConversionOperationSequence::Compile() - after everything else is done. 
     314   * 
     315   * \param conversion_option Selected conversion option 
     316   * \param custom_data Custom data that may be filled with required data 
     317   */ 
     318  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const; 
     319 
    294320//---------------------------------------------------------------------- 
    295321// Private fields and methods 
     
    298324 
    299325  friend class tStaticCastOperation; 
     326  friend class tConversionOperationSequence; 
    300327 
    301328  /*! Name of conversion operation (must be unique for every supported combination of source and destination types) */ 
  • tStaticCastOperation.cpp

    r20 r22  
    146146} 
    147147 
     148template <typename TEnumUnderlyingType> 
     149void ConvertToEnumFinal(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     150{ 
     151  static_assert(std::is_unsigned<TEnumUnderlyingType>::value && std::is_integral<TEnumUnderlyingType>::value, "Currently only unsigned int types are supported"); 
     152 
     153  // Obtain value 
     154  tConversionOption::tConversionFunction function = operation.GetCustomData().conversion_function; 
     155  TEnumUnderlyingType value = 0; 
     156  if (function) 
     157  { 
     158    (*function)(source_object, tTypedPointer(&value, tDataType<TEnumUnderlyingType>()), operation); 
     159  } 
     160  else 
     161  { 
     162    assert(source_object.GetType().GetSize() == sizeof(TEnumUnderlyingType)); 
     163    memcpy(&value, source_object.GetRawDataPointer(), sizeof(TEnumUnderlyingType)); 
     164  } 
     165 
     166  // Check whether value is in bounds 
     167  auto enum_strings = destination_object.GetType().GetEnumStringsData(); 
     168  if (enum_strings->non_standard_values) 
     169  { 
     170    const TEnumUnderlyingType* values = static_cast<const TEnumUnderlyingType*>(enum_strings->non_standard_values); 
     171    bool found = false; 
     172    for (size_t i = 0; i < enum_strings->size; i++) 
     173    { 
     174      if (values[i] == value) 
     175      { 
     176        found = true; 
     177        break; 
     178      } 
     179    } 
     180    if (!found) 
     181    { 
     182      serialization::tStringOutputStream stream; 
     183      stream << "Error casting value '"; 
     184      source_object.Serialize(stream); 
     185      stream << "' to enum '" << destination_object.GetType().GetName() << "'"; 
     186      throw std::invalid_argument(stream.ToString()); 
     187    } 
     188  } 
     189  else if (value >= enum_strings->size) 
     190  { 
     191    serialization::tStringOutputStream stream; 
     192    stream << "Error casting out of bounds value '"; 
     193    source_object.Serialize(stream); 
     194    stream << "' to enum '" << destination_object.GetType().GetName() << "'"; 
     195    throw std::invalid_argument(stream.ToString()); 
     196  } 
     197 
     198  assert(destination_object.GetType().GetSize() == sizeof(TEnumUnderlyingType)); 
     199  memcpy(destination_object.GetRawDataPointer(), &value, sizeof(TEnumUnderlyingType)); 
     200} 
     201 
     202template <typename TEnumUnderlyingType> 
     203static void ConvertToEnumFirst(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     204{ 
     205  TEnumUnderlyingType intermediate_buffer = 0; 
     206  tTypedPointer intermediate(&intermediate_buffer, operation.compiled_operation.IntermediateType()); 
     207  ConvertToEnumFinal<TEnumUnderlyingType>(source_object, intermediate, operation); 
     208  operation.Continue(intermediate, destination_object); 
     209} 
     210 
     211template <typename TEnumUnderlyingType> 
     212static void OnCompileToEnum(const tRegisteredConversionOperation& static_cast_operation, const tConversionOption& conversion_option, tCustomOperationData& custom_data) 
     213{ 
     214  // Lookup underlying type conversion function 
     215  if (conversion_option.first_conversion_function == &ConvertToEnumFirst<TEnumUnderlyingType> && conversion_option.source_type.GetSize() != sizeof(TEnumUnderlyingType)) 
     216  { 
     217    tConversionOption option = static_cast_operation.GetConversionOption(conversion_option.source_type, tDataType<TEnumUnderlyingType>(), nullptr); 
     218    assert(option.first_conversion_function && option.type == tConversionOptionType::STANDARD_CONVERSION_FUNCTION); 
     219    custom_data.conversion_function = option.final_conversion_function; 
     220  } 
     221} 
     222 
    148223} 
    149224 
     
    169244      return option->conversion_option; 
    170245    } 
    171  
    172     if (option->implicit) 
    173     { 
    174       // Combining (non-implicit) underlying type casts with implicit casts can be done in a single operation 
    175       tType underlying_type_source = source_type; 
    176       while (underlying_type_source.GetUnderlyingType() != underlying_type_source) 
    177       { 
    178         underlying_type_source = underlying_type_source.GetUnderlyingType(); 
    179         if (underlying_type_source == option->conversion_option.source_type && destination_type == option->conversion_option.destination_type) 
    180         { 
    181           return option->conversion_option; 
    182         } 
     246  } 
     247 
     248  // If destination is enum type, use checked conversion 
     249  if (destination_type.GetEnumStringsData()) 
     250  { 
     251    if (source_type.GetTypeTraits() & trait_flags::cIS_ARITHMETIC) 
     252    { 
     253      if (destination_type.GetSize() == 1) 
     254      { 
     255        return tConversionOption(source_type, destination_type, false, &ConvertToEnumFirst<uint8_t>, &ConvertToEnumFinal<uint8_t>); 
     256      } 
     257      else if (destination_type.GetSize() == 2) 
     258      { 
     259        return tConversionOption(source_type, destination_type, false, &ConvertToEnumFirst<uint16_t>, &ConvertToEnumFinal<uint16_t>); 
     260      } 
     261      else if (destination_type.GetSize() == 4) 
     262      { 
     263        return tConversionOption(source_type, destination_type, false, &ConvertToEnumFirst<uint32_t>, &ConvertToEnumFinal<uint32_t>); 
     264      } 
     265      else if (destination_type.GetSize() == 8) 
     266      { 
     267        return tConversionOption(source_type, destination_type, false, &ConvertToEnumFirst<uint64_t>, &ConvertToEnumFinal<uint64_t>); 
     268      } 
     269    } 
     270 
     271    return tConversionOption(); 
     272  } 
     273 
     274  // Combining (non-implicit) underlying type casts with implicit or arithmetic casts can be done in a single operation 
     275  for (auto & option : registered_operations.static_casts) 
     276  { 
     277    bool arithmetic_cast = (option->conversion_option.source_type.GetTypeTraits() & trait_flags::cIS_ARITHMETIC) != 0 && (option->conversion_option.destination_type.GetTypeTraits() & trait_flags::cIS_ARITHMETIC) != 0; 
     278    if ((option->implicit || arithmetic_cast) && destination_type == option->conversion_option.destination_type) 
     279    { 
     280      result = GetCommonUnderlyingType(source_type, option->conversion_option.source_type); 
     281      if (result.common_underlying_type == option->conversion_option.source_type || (result.cast_from_common_to_destination_type_valid && result.common_underlying_type == source_type)) 
     282      { 
     283        return option->conversion_option; 
     284      } 
     285    } 
     286    if ((option->implicit || arithmetic_cast) && source_type == option->conversion_option.source_type) 
     287    { 
     288      result = GetCommonUnderlyingType(option->conversion_option.destination_type, destination_type); 
     289      if (result.common_underlying_type == destination_type || (result.cast_from_common_to_destination_type_valid && result.common_underlying_type == option->conversion_option.destination_type)) 
     290      { 
     291        return option->conversion_option; 
    183292      } 
    184293    } 
     
    251360} 
    252361 
     362void tStaticCastOperation::OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     363{ 
     364  OnCompileToEnum<uint8_t>(*this, conversion_option, custom_data); 
     365  OnCompileToEnum<uint16_t>(*this, conversion_option, custom_data); 
     366  OnCompileToEnum<uint32_t>(*this, conversion_option, custom_data); 
     367  OnCompileToEnum<uint64_t>(*this, conversion_option, custom_data); 
     368} 
     369 
    253370//---------------------------------------------------------------------- 
    254371// End of namespace declaration 
  • tStaticCastOperation.h

    r14 r22  
    8282    static void ConvertFinal(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
    8383    { 
    84       (*destination_object.Get<TDestination>()) = static_cast<TDestination>(*source_object.Get<TSource>()); 
     84      (*destination_object.GetUnchecked<TDestination>()) = static_cast<TDestination>(*source_object.Get<TSource>()); 
    8585    } 
    8686 
     
    8888    { 
    8989      TDestination intermediate = static_cast<TDestination>(*source_object.Get<TSource>()); 
    90       operation.Continue(tTypedConstPointer(&intermediate), destination_object); 
     90      operation.Continue(tTypedConstPointer(&intermediate, operation.compiled_operation.IntermediateType()), destination_object); 
    9191    } 
    9292 
     
    166166    { 
    167167      const std::vector<TSource>& source = *source_object.Get<std::vector<TSource>>(); 
    168       std::vector<TDestination>& destination = *destination_object.Get<std::vector<TDestination>>(); 
     168      std::vector<TDestination>& destination = *destination_object.GetUnchecked<std::vector<TDestination>>(); 
    169169      destination.resize(source.size()); 
    170170      auto it_dest = destination.begin(); 
     
    185185        *it_dest = static_cast<TDestination>(*it); 
    186186      } 
    187       operation.Continue(tTypedConstPointer(&intermediate), destination_object); 
     187      operation.Continue(tTypedConstPointer(&intermediate, operation.compiled_operation.IntermediateType()), destination_object); 
    188188    } 
    189189 
     
    310310 
    311311  virtual tConversionOption GetConversionOption(const tType& source_type, const tType& destination_type, const tGenericObject* parameter) const override; 
     312 
     313  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const override; 
    312314}; 
    313315 
Note: See TracChangeset for help on using the changeset viewer.