Changeset 32:78645c8d0c81 in rrlib_rtti_conversion


Ignore:
Timestamp:
05.04.2020 06:43:39 (11 months ago)
Author:
Max Reichardt <mreichardt@…>
Branch:
17.03
Children:
33:1a3073d75f88, 34:8fc7f3c50c2e
Phase:
public
Message:

Implements significant update. In particular, conversion operations can now flag whether they also apply if their source and/or destination types are underlying types of the actual types. Furthermore, adds ADJUST_LENGTH generic operation and upgrades FOR_EACH regarding arrays as destinations. The most relevant use case in practice is likely the possibility to convert among virtually any combinations rrlib math vector types.

Files:
5 edited

Legend:

Unmodified
Added
Removed
  • defined_conversions.cpp

    r12 r32  
    338338{ 
    339339public: 
    340   tGetListElement() : tRegisteredConversionOperation(util::tManagedConstCharPointer("[]", false), tSupportedTypeFilter::GET_LIST_ELEMENT, tSupportedTypeFilter::GET_LIST_ELEMENT, nullptr, tParameterDefinition("Index", tDataType<unsigned int>(), true)) 
     340  tGetListElement() : tRegisteredConversionOperation(util::tManagedConstCharPointer("[]", false), tUnderlyingType(tSupportedTypeFilter::GET_LIST_ELEMENT), tSupportedTypeFilter::GET_LIST_ELEMENT, nullptr, tParameterDefinition("Index", tDataType<unsigned int>(), true)) 
    341341  {} 
    342342 
    343343  virtual tConversionOption GetConversionOption(const tType& source_type, const tType& destination_type, const tGenericObject* parameter) const override 
    344344  { 
    345     if (source_type.IsListType() && source_type.GetElementType() == destination_type) 
    346     { 
    347       return tConversionOption(source_type, destination_type, &FirstConversionFunction, &GetDestinationReference); 
    348     } 
    349     unsigned int index = 0; 
    350     if (parameter && parameter->GetType() == tDataType<std::string>()) 
    351     { 
    352       index = serialization::Deserialize<unsigned int>(parameter->GetData<std::string>()); 
    353     } 
    354     else if (parameter) 
    355     { 
    356       assert(parameter->GetType() == tDataType<unsigned int>()); 
    357       index = parameter->GetData<unsigned int>(); 
    358     } 
    359     { 
    360       rtti::tType current_type = source_type; 
    361       while ((current_type.GetTypeTraits() & rtti::trait_flags::cINHERITS_UNDERLYING_TYPE_ELEMENT_ACCESS_OPERATIONS) && (!current_type.IsArray()) && current_type.GetUnderlyingType() != current_type) 
    362       { 
    363         current_type = current_type.GetUnderlyingType(); 
    364       } 
    365       if (current_type.IsArray() && current_type.GetElementType() == destination_type && index < current_type.GetArraySize()) 
    366       { 
    367         return tConversionOption(source_type, destination_type, index * current_type.GetElementType().GetSize()); 
    368       } 
     345    tType check_type = source_type; 
     346    while (check_type) 
     347    { 
     348      if (check_type.IsListType() && check_type.GetElementType() == destination_type) 
     349      { 
     350        return tConversionOption(source_type, destination_type, &FirstConversionFunction, &GetDestinationReference); 
     351      } 
     352      if (check_type.IsArray() && check_type.GetElementType() == destination_type) 
     353      { 
     354        unsigned int index = 0; 
     355        if (parameter && parameter->GetType() == tDataType<std::string>()) 
     356        { 
     357          index = serialization::Deserialize<unsigned int>(parameter->GetData<std::string>()); 
     358        } 
     359        else if (parameter) 
     360        { 
     361          assert(parameter->GetType() == tDataType<unsigned int>()); 
     362          index = parameter->GetData<unsigned int>(); 
     363        } 
     364 
     365        if (index < check_type.GetArraySize()) 
     366        { 
     367          return tConversionOption(source_type, destination_type, index * check_type.GetElementType().GetSize()); 
     368        } 
     369      } 
     370      check_type = GetUnderlyingTypeOperationsAreInheritedFrom(check_type); 
    369371    } 
    370372    return tConversionOption(); 
     
    375377    auto index_parameter = operation.GetParameterValue(); 
    376378    unsigned int index = index_parameter ? (*index_parameter.Get<unsigned int>()) : 0; 
    377     auto result = source_object.GetVectorElement(index); 
     379    auto result = tTypedConstPointer(source_object.GetRawDataPointer(), operation.GetCustomData().computation_types[0]).GetVectorElement(index); 
    378380    if (!result) 
    379381    { 
     
    387389    auto index_parameter = operation.GetParameterValue(); 
    388390    unsigned int index = index_parameter ? (*index_parameter.Get<unsigned int>()) : 0; 
    389     auto intermediate = source_object.GetVectorElement(index); 
     391    auto intermediate = tTypedConstPointer(source_object.GetRawDataPointer(), operation.GetCustomData().computation_types[0]).GetVectorElement(index); 
    390392    if (!intermediate) 
    391393    { 
     
    394396    operation.Continue(intermediate, destination_object); 
    395397  } 
     398 
     399  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     400  { 
     401    custom_data.computation_types[0] = conversion_option.source_type; 
     402    while (!(custom_data.computation_types[0].IsListType() || custom_data.computation_types[0].IsArray())) 
     403    { 
     404      custom_data.computation_types[0] = GetUnderlyingTypeOperationsAreInheritedFrom(custom_data.computation_types[0]); 
     405      if (!custom_data.computation_types[0]) 
     406      { 
     407        throw std::runtime_error("Source type must be list or array"); 
     408      } 
     409    } 
     410  } 
     411 
    396412} cGET_LIST_ELEMENT; 
    397413 
     
    399415{ 
    400416public: 
    401   tForEach() : tRegisteredConversionOperation(util::tManagedConstCharPointer("For Each", false), tSupportedTypeFilter::FOR_EACH, tSupportedTypeFilter::FOR_EACH) 
     417  tForEach() : tRegisteredConversionOperation(util::tManagedConstCharPointer("For Each", false), tUnderlyingType(tSupportedTypeFilter::FOR_EACH), tUnderlyingType(tSupportedTypeFilter::FOR_EACH)) 
    402418  {} 
    403419 
    404420  virtual tConversionOption GetConversionOption(const tType& source_type, const tType& destination_type, const tGenericObject* parameter) const override 
    405421  { 
    406     if ((source_type.IsListType() || source_type.IsArray()) && destination_type) 
    407     { 
    408       return tConversionOption(source_type, destination_type, false, &FirstConversionFunction, &FinalConversionFunction); 
     422    tType check_type = source_type; 
     423    while (check_type) 
     424    { 
     425      if ((check_type.IsListType() || check_type.IsArray()) && destination_type) 
     426      { 
     427        return tConversionOption(source_type, destination_type, false, &FirstConversionFunction, &FinalConversionFunction); 
     428      } 
     429      check_type = GetUnderlyingTypeOperationsAreInheritedFrom(check_type); 
    409430    } 
    410431    return tConversionOption(); 
    411432  } 
    412433 
    413   static void FirstConversionFunction(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
    414   { 
     434  static void FirstConversionFunction(const tTypedConstPointer& source_object_original, const tTypedPointer& destination_object_original, const tCurrentConversionOperation& operation) 
     435  { 
     436    tTypedConstPointer source_object = tTypedConstPointer(source_object_original.GetRawDataPointer(), operation.GetCustomData().computation_types[0]); 
    415437    const tType& source_type = source_object.GetType(); 
    416     const tType& destination_type = destination_object.GetType(); 
     438    tType destination_type = destination_object_original.GetType(); 
     439    while (!(destination_type.IsArray() || destination_type.IsListType())) 
     440    { 
     441      destination_type = GetUnderlyingTypeOperationsAreInheritedFrom(destination_type); 
     442      if (!destination_type) 
     443      { 
     444        throw std::runtime_error("Destination type must be list or array type"); 
     445      } 
     446    } 
     447    const tTypedPointer destination_object(destination_object_original.GetRawDataPointer(), destination_type); 
    417448    const tType source_element_type = source_type.GetElementType(); 
    418449    const tType destination_element_type = destination_type.GetElementType(); 
     
    491522      } 
    492523    } 
    493     else if (source_type.IsArray() && destination_type.IsArray()) 
    494     { 
    495       if (size != destination_type.GetArraySize()) 
    496       { 
    497         throw std::runtime_error("Arrays must have the same size"); 
    498       } 
    499  
    500       size_t source_element_offset = source_type.GetSize() / size; 
    501       size_t destination_element_offset = destination_type.GetSize() / size; 
    502       for (size_t i = 0; i < size; i++) 
    503       { 
    504         tTypedConstPointer source(static_cast<const char*>(source_object.GetRawDataPointer()) + i * source_element_offset, source_element_type); 
    505         tTypedPointer destination(static_cast<char*>(destination_object.GetRawDataPointer()) + i * destination_element_offset, destination_element_type); 
    506         operation.Continue(source, destination); 
     524    else if (destination_type.IsArray()) 
     525    { 
     526      size_t destination_elements = destination_type.GetArraySize(); 
     527      size_t source_elements = source_type.GetTypeClassification() == tTypeClassification::LIST ? source_object.GetVectorSize() : source_type.GetArraySize(); 
     528      size_t copy_elements = std::min(destination_elements, source_elements); 
     529      size_t fill_elements = destination_elements - copy_elements; 
     530      size_t destination_element_offset = destination_object.GetType().GetSize() / destination_elements; 
     531 
     532      if (source_type == cBOOL_VECTOR_TYPE) 
     533      { 
     534        const std::vector<bool>* source = source_object.Get<std::vector<bool>>(); 
     535        bool wrapped_bool = false; 
     536        tTypedPointer temp_source_pointer(&wrapped_bool); 
     537        for (size_t i = 0; i < copy_elements; i++) 
     538        { 
     539          wrapped_bool = (*source)[i]; 
     540          tTypedPointer destination(static_cast<char*>(destination_object.GetRawDataPointer()) + i * destination_element_offset, destination_element_type); 
     541          operation.Continue(temp_source_pointer, destination); 
     542        } 
     543      } 
     544      else if (source_type.GetTypeClassification() == tTypeClassification::ARRAY) 
     545      { 
     546        size_t sources_element_offset = source_object.GetType().GetSize() / source_elements; 
     547        for (size_t i = 0; i < copy_elements; i++) 
     548        { 
     549          tTypedConstPointer source(static_cast<const char*>(source_object.GetRawDataPointer()) + i * sources_element_offset, source_element_type); 
     550          tTypedPointer destination(static_cast<char*>(destination_object.GetRawDataPointer()) + i * destination_element_offset, destination_element_type); 
     551          operation.Continue(source, destination); 
     552        } 
     553      } 
     554      else if (copy_elements > 0) 
     555      { 
     556        tTypedConstPointer source_first = source_object.GetVectorElement(0); 
     557        tTypedPointer destination_first(static_cast<char*>(destination_object.GetRawDataPointer()), destination_element_type); 
     558        operation.Continue(source_first, destination_first); 
     559        if (copy_elements > 1) 
     560        { 
     561          tTypedConstPointer source_next = source_object.GetVectorElement(1); 
     562          tTypedPointer destination_second(static_cast<char*>(destination_object.GetRawDataPointer()) + destination_element_offset, destination_element_type); 
     563          operation.Continue(source_next, destination_second); 
     564          size_t offset_source = static_cast<const char*>(source_next.GetRawDataPointer()) - static_cast<const char*>(source_first.GetRawDataPointer()); 
     565          for (size_t i = 2; i < copy_elements; i++) 
     566          { 
     567            tTypedConstPointer source_next = tTypedConstPointer(static_cast<const char*>(source_next.GetRawDataPointer()) + offset_source, source_element_type); 
     568            tTypedPointer destination_next(static_cast<char*>(destination_object.GetRawDataPointer()) + i * destination_element_offset, destination_element_type); 
     569            operation.Continue(source_next, destination_next); 
     570          } 
     571        } 
     572      } 
     573 
     574      if (fill_elements) 
     575      { 
     576        if (destination_element_type.GetTypeTraits() & trait_flags::cIS_DEFAULT_CONSTRUCTION_ZERO_MEMORY) 
     577        { 
     578          memset(static_cast<char*>(destination_object.GetRawDataPointer()) + copy_elements * destination_element_offset, 0, fill_elements * destination_element_offset); 
     579        } 
     580        else 
     581        { 
     582          for (size_t i = 0; i < fill_elements; i++) 
     583          { 
     584            destination_element_type.EmplaceInstance(static_cast<char*>(destination_object.GetRawDataPointer()) + (copy_elements + i) * destination_element_offset); 
     585          } 
     586        } 
    507587      } 
    508588    } 
     
    567647    throw std::logic_error("Not supported as single or second operation"); 
    568648  } 
     649 
     650  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     651  { 
     652    custom_data.computation_types[0] = conversion_option.source_type; 
     653    while (!(custom_data.computation_types[0].IsListType() || custom_data.computation_types[0].IsArray())) 
     654    { 
     655      custom_data.computation_types[0] = GetUnderlyingTypeOperationsAreInheritedFrom(custom_data.computation_types[0]); 
     656      if (!custom_data.computation_types[0]) 
     657      { 
     658        throw std::runtime_error("Source type must be list or array"); 
     659      } 
     660    } 
     661  } 
     662 
    569663} cFOR_EACH; 
    570664 
     
    572666{ 
    573667public: 
    574   tArrayToVector() : tRegisteredConversionOperation(util::tManagedConstCharPointer("ToVector", false), tSupportedTypeFilter::ARRAY_TO_VECTOR, tSupportedTypeFilter::ARRAY_TO_VECTOR) 
     668  tArrayToVector() : tRegisteredConversionOperation(util::tManagedConstCharPointer("ToVector", false), tUnderlyingType(tSupportedTypeFilter::ARRAY_TO_VECTOR), tSupportedTypeFilter::ARRAY_TO_VECTOR) 
    575669  {} 
    576670 
    577671  virtual tConversionOption GetConversionOption(const tType& source_type, const tType& destination_type, const tGenericObject* parameter) const override 
    578672  { 
    579     if (source_type.IsArray() && destination_type.IsListType() && source_type.GetElementType() == destination_type.GetElementType()) 
    580     { 
    581       return tConversionOption(source_type, destination_type, false, &FirstConversionFunction, &FinalConversionFunction); 
     673    tType check_type = source_type; 
     674    while (check_type) 
     675    { 
     676      if (check_type.IsArray() && destination_type.IsListType() && check_type.GetElementType() == destination_type.GetElementType()) 
     677      { 
     678        return tConversionOption(source_type, destination_type, false, &FirstConversionFunction, &FinalConversionFunction); 
     679      } 
     680      check_type = GetUnderlyingTypeOperationsAreInheritedFrom(check_type); 
    582681    } 
    583682    return tConversionOption(); 
     
    595694  static void FinalConversionFunction(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
    596695  { 
    597     const tType& source_type = source_object.GetType(); 
     696    const tType& source_type = operation.GetCustomData().computation_types[0]; 
    598697    const tType source_element_type = source_type.GetElementType(); 
    599698    size_t size = source_type.GetArraySize(); 
     
    620719    } 
    621720  } 
     721 
     722  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     723  { 
     724    custom_data.computation_types[0] = conversion_option.source_type; 
     725    while (!custom_data.computation_types[0].IsArray()) 
     726    { 
     727      custom_data.computation_types[0] = GetUnderlyingTypeOperationsAreInheritedFrom(custom_data.computation_types[0]); 
     728      if (!custom_data.computation_types[0]) 
     729      { 
     730        throw std::runtime_error("Source type must be array"); 
     731      } 
     732    } 
     733  } 
    622734} cARRAY_TO_VECTOR; 
    623735 
     
    625737{ 
    626738public: 
    627   tGetTupleElement() : tRegisteredConversionOperation(util::tManagedConstCharPointer("get", false), tSupportedTypeFilter::GET_TUPLE_ELEMENT, tSupportedTypeFilter::GET_TUPLE_ELEMENT, nullptr, tParameterDefinition("Index", tDataType<unsigned int>(), true)) 
     739  tGetTupleElement() : tRegisteredConversionOperation(util::tManagedConstCharPointer("get", false), tUnderlyingType(tSupportedTypeFilter::GET_TUPLE_ELEMENT), tSupportedTypeFilter::GET_TUPLE_ELEMENT, nullptr, tParameterDefinition("Index", tDataType<unsigned int>(), true)) 
    628740  {} 
    629741 
     
    640752      index = parameter->GetData<unsigned int>(); 
    641753    } 
    642     auto tuple_types = source_type.GetTupleTypes(); 
    643     if (index < tuple_types.second && destination_type == tType(tuple_types.first[index].type_info)) 
    644     { 
    645       return tConversionOption(source_type, destination_type, tuple_types.first[index].offset); 
    646     } 
     754 
     755    tType check_type = source_type; 
     756    while (check_type) 
     757    { 
     758      auto tuple_types = check_type.GetTupleTypes(); 
     759      if (index < tuple_types.second && destination_type == tType(tuple_types.first[index].type_info)) 
     760      { 
     761        return tConversionOption(source_type, destination_type, tuple_types.first[index].offset); 
     762      } 
     763      check_type = GetUnderlyingTypeOperationsAreInheritedFrom(check_type); 
     764    } 
     765 
    647766    return tConversionOption(); 
    648767  } 
     
    687806{ 
    688807public: 
    689   tListSize() : tRegisteredConversionOperation(util::tManagedConstCharPointer("size()", false), tSupportedTypeFilter::LISTS, tDataType<size_t>()) 
     808  tListSize() : tRegisteredConversionOperation(util::tManagedConstCharPointer("size()", false), tUnderlyingType(tSupportedTypeFilter::LISTS), tDataType<size_t>()) 
    690809  {} 
    691810 
    692811  virtual tConversionOption GetConversionOption(const tType& source_type, const tType& destination_type, const tGenericObject* parameter) const override 
    693812  { 
    694     if (source_type.IsListType() && destination_type == tDataType<size_t>()) 
    695     { 
    696       return tConversionOption(source_type, destination_type, false, &FirstConversionFunction, &FinalConversionFunction); 
     813    tType check_type = source_type; 
     814    while (check_type) 
     815    { 
     816      if (check_type.IsListType() && destination_type == tDataType<size_t>()) 
     817      { 
     818        return tConversionOption(source_type, destination_type, false, &FirstConversionFunction, &FinalConversionFunction); 
     819      } 
     820      check_type = GetUnderlyingTypeOperationsAreInheritedFrom(check_type); 
    697821    } 
    698822    return tConversionOption(); 
     
    701825  static void FirstConversionFunction(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
    702826  { 
    703     size_t size = source_object.GetVectorSize(); 
     827    size_t size = tTypedConstPointer(source_object.GetRawDataPointer(), operation.GetCustomData().computation_types[0]).GetVectorSize(); 
    704828    operation.Continue(tTypedConstPointer(&size), destination_object); 
    705829  } 
     
    707831  static void FinalConversionFunction(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
    708832  { 
    709     (*destination_object.Get<size_t>()) = source_object.GetVectorSize(); 
     833    (*destination_object.Get<size_t>()) = tTypedConstPointer(source_object.GetRawDataPointer(), operation.GetCustomData().computation_types[0]).GetVectorSize(); 
     834  } 
     835 
     836  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     837  { 
     838    custom_data.computation_types[0] = conversion_option.source_type; 
     839    while (!custom_data.computation_types[0].IsListType()) 
     840    { 
     841      custom_data.computation_types[0] = GetUnderlyingTypeOperationsAreInheritedFrom(custom_data.computation_types[0]); 
     842      if (!custom_data.computation_types[0]) 
     843      { 
     844        throw std::runtime_error("Source type must be list"); 
     845      } 
     846    } 
    710847  } 
    711848} cLIST_SIZE; 
     
    723860const tVoidFunctionConversionOperation<std::string, std::vector<char>, decltype(&StringToVectorConversionFunction), &StringToVectorConversionFunction> cSTRING_TO_VECTOR("ToVector"); 
    724861const tVoidFunctionConversionOperation<std::vector<char>, std::string, decltype(&VectorToStringConversionFunction), &VectorToStringConversionFunction> cVECTOR_TO_STRING("MakeString"); 
     862 
     863class tAdjustLength : public tRegisteredConversionOperation 
     864{ 
     865public: 
     866  tAdjustLength() : tRegisteredConversionOperation(util::tManagedConstCharPointer("AdjustLength", false), tUnderlyingType(tSupportedTypeFilter::ADJUST_LENGTH), tUnderlyingType(tSupportedTypeFilter::ADJUST_LENGTH)) 
     867  {} 
     868 
     869  static tType GetSupportedType(const tType& type, bool source_type) 
     870  { 
     871    return (!type) ? tType() : ((type.IsArray() || (source_type && type.IsListType())) ? type : GetSupportedType(GetUnderlyingTypeOperationsAreInheritedFrom(type), source_type)); 
     872  } 
     873 
     874  virtual tConversionOption GetConversionOption(const tType& source_type, const tType& destination_type, const tGenericObject* parameter) const override 
     875  { 
     876    tType supported_source_type = GetSupportedType(source_type, true); 
     877    tType supported_destination_type = GetSupportedType(destination_type, true); 
     878    if (supported_source_type && supported_destination_type && supported_source_type.GetElementType() == supported_destination_type.GetElementType()) 
     879    { 
     880      const auto cQUICK_FLAGS = trait_flags::cIS_DEFAULT_CONSTRUCTION_ZERO_MEMORY | trait_flags::cSUPPORTS_BITWISE_COPY; 
     881      bool quick = (supported_source_type.GetElementType().GetTypeTraits() & cQUICK_FLAGS) == cQUICK_FLAGS; 
     882      assert(supported_destination_type.IsArray()); 
     883      if (supported_source_type.IsArray() && quick) 
     884      { 
     885        if (supported_destination_type.GetSize() > supported_source_type.GetSize()) 
     886        { 
     887          return tConversionOption(source_type, destination_type, false, &FirstConversionFunctionMemset<&FinalConversionFunctionArrayMemcpy<true>>, &FinalConversionFunctionArrayMemcpy<true>); 
     888        } 
     889        else 
     890        { 
     891          return tConversionOption(source_type, destination_type, false, &FirstConversionFunctionMemset<&FinalConversionFunctionArrayMemcpy<false>>, &FinalConversionFunctionArrayMemcpy<false>); 
     892        } 
     893      } 
     894      else if (supported_source_type == tDataType<std::vector<bool>>()) 
     895      { 
     896        return tConversionOption(source_type, destination_type, false, &FirstConversionFunctionMemset<&FinalConversionFunctionBoolVector>, &FinalConversionFunctionBoolVector); 
     897      } 
     898      else if (supported_source_type.IsArray()) 
     899      { 
     900        return tConversionOption(source_type, destination_type, false, &FirstConversionFunctionStandard<&FinalConversionFunctionArray>, &FinalConversionFunctionArray); 
     901      } 
     902 
     903      assert(supported_source_type.IsListType()); 
     904      if (quick) 
     905      { 
     906        return tConversionOption(source_type, destination_type, false, &FirstConversionFunctionMemset<&FinalConversionFunctionVector<true>>, &FinalConversionFunctionVector<true>); 
     907      } 
     908      return tConversionOption(source_type, destination_type, false, &FirstConversionFunctionStandard<&FinalConversionFunctionVector<false>>, &FinalConversionFunctionVector<false>); 
     909    } 
     910    return tConversionOption(); 
     911  } 
     912 
     913  template <tConversionOption::tConversionFunction Tfinal_conversion> 
     914  static void FirstConversionFunctionMemset(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     915  { 
     916    tType inter_type = operation.compiled_operation.IntermediateType(); 
     917    char intermediate_memory[inter_type.GetSize()]; 
     918    memset(intermediate_memory, 0, sizeof(intermediate_memory)); 
     919    tTypedPointer intermediate_object(intermediate_memory, inter_type); 
     920    (*Tfinal_conversion)(source_object, intermediate_object, operation); 
     921    operation.Continue(intermediate_object, destination_object); 
     922  } 
     923 
     924  template <tConversionOption::tConversionFunction Tfinal_conversion> 
     925  static void FirstConversionFunctionStandard(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     926  { 
     927    tType inter_type = operation.compiled_operation.IntermediateType(); 
     928    char intermediate_memory[inter_type.GetSize(true)]; 
     929    auto intermediate_object = inter_type.EmplaceGenericObject(intermediate_memory); 
     930    (*Tfinal_conversion)(source_object, *intermediate_object, operation); 
     931    operation.Continue(*intermediate_object, destination_object); 
     932  } 
     933 
     934  template <bool Tdestination_larger> 
     935  static void FinalConversionFunctionArrayMemcpy(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     936  { 
     937    if (Tdestination_larger) 
     938    { 
     939      size_t destination_size = destination_object.GetType().GetSize(); 
     940      size_t copy_size = std::min(source_object.GetType().GetSize(), destination_size); 
     941      size_t fill_size = destination_size - source_object.GetType().GetSize(); 
     942      memcpy(destination_object.GetRawDataPointer(), source_object.GetRawDataPointer(), copy_size); 
     943      memset(static_cast<char*>(destination_object.GetRawDataPointer()) + copy_size, 0, fill_size); 
     944    } 
     945    else 
     946    { 
     947      memcpy(destination_object.GetRawDataPointer(), source_object.GetRawDataPointer(), destination_object.GetType().GetSize()); 
     948    } 
     949  } 
     950 
     951  static void FinalConversionFunctionArray(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     952  { 
     953    auto& custom_data = operation.GetCustomData(); 
     954    const tType source_type = custom_data.computation_types[0]; 
     955    const tType destination_type = custom_data.computation_types[1]; 
     956    const tType element_type = source_type.GetElementType(); 
     957    size_t element_size = element_type.GetSize(); 
     958    bool destination_larger = destination_type > source_type; 
     959    size_t copy_size = std::min(source_type.GetArraySize(), destination_type.GetArraySize()); 
     960 
     961    for (size_t i = 0; i < copy_size; i++) 
     962    { 
     963      tTypedConstPointer source_element(static_cast<const char*>(source_object.GetRawDataPointer()) + i * element_size, element_type); 
     964      tTypedPointer destination_element(static_cast<char*>(destination_object.GetRawDataPointer()) + i * element_size, element_type); 
     965      destination_element.DeepCopyFrom(source_element); 
     966    } 
     967 
     968    if (destination_larger) 
     969    { 
     970      for (size_t i = copy_size; i < destination_type.GetArraySize(); i++) 
     971      { 
     972        element_type.EmplaceInstance(static_cast<char*>(destination_object.GetRawDataPointer()) + i * element_size); 
     973      } 
     974    } 
     975  } 
     976 
     977  static void FinalConversionFunctionBoolVector(const tTypedConstPointer& source_object, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     978  { 
     979    const std::vector<bool>* source = source_object.GetUnchecked<std::vector<bool>>(); 
     980    const tType destination_type = operation.GetCustomData().computation_types[1]; 
     981    size_t destination_elements = destination_type.GetArraySize(); 
     982 
     983    size_t copy_elements = std::min(source->size(), destination_elements); 
     984    size_t fill_elements = destination_elements - copy_elements; 
     985 
     986    for (size_t i = 0; i < copy_elements; i++) 
     987    { 
     988      *(static_cast<bool*>(destination_object.GetRawDataPointer()) + i) = (*source)[i]; 
     989    } 
     990    memset(static_cast<bool*>(destination_object.GetRawDataPointer()) + copy_elements, 0, fill_elements); 
     991  } 
     992 
     993  template <bool Tmemcpy> 
     994  static void FinalConversionFunctionVector(const tTypedConstPointer& source_object_original, const tTypedPointer& destination_object, const tCurrentConversionOperation& operation) 
     995  { 
     996    auto& custom_data = operation.GetCustomData(); 
     997    const tType destination_type = custom_data.computation_types[1]; 
     998    const tType element_type = destination_type.GetElementType(); 
     999    tTypedConstPointer source_object(source_object_original.GetRawDataPointer(), custom_data.computation_types[0]); 
     1000 
     1001    size_t copy_elements = std::min(source_object.GetVectorSize(), destination_type.GetArraySize()); 
     1002    size_t fill_elements = destination_type.GetArraySize() - copy_elements; 
     1003 
     1004    if (copy_elements > 0) 
     1005    { 
     1006      tTypedConstPointer source_first = source_object.GetVectorElement(0); 
     1007      if (Tmemcpy) 
     1008      { 
     1009        memcpy(destination_object.GetRawDataPointer(), source_first.GetRawDataPointer(), element_type.GetSize()); 
     1010      } 
     1011      else 
     1012      { 
     1013        tTypedPointer destination_element(static_cast<char*>(destination_object.GetRawDataPointer()), element_type); 
     1014        destination_element.DeepCopyFrom(source_first); 
     1015      } 
     1016 
     1017      if (copy_elements > 1) 
     1018      { 
     1019        tTypedConstPointer source_next = source_object.GetVectorElement(1); 
     1020        if (Tmemcpy) 
     1021        { 
     1022          memcpy(static_cast<char*>(destination_object.GetRawDataPointer()) + element_type.GetSize(), source_next.GetRawDataPointer(), element_type.GetSize()); 
     1023        } 
     1024        else 
     1025        { 
     1026          tTypedPointer destination_element(static_cast<char*>(destination_object.GetRawDataPointer()) + element_type.GetSize(), element_type); 
     1027          destination_element.DeepCopyFrom(source_next); 
     1028        } 
     1029        size_t offset_source = static_cast<const char*>(source_next.GetRawDataPointer()) - static_cast<const char*>(source_first.GetRawDataPointer()); 
     1030        for (size_t i = 2; i < copy_elements; i++) 
     1031        { 
     1032          source_next = tTypedConstPointer(static_cast<const char*>(source_next.GetRawDataPointer()) + offset_source, element_type); 
     1033          if (Tmemcpy) 
     1034          { 
     1035            memcpy(static_cast<char*>(destination_object.GetRawDataPointer()) + i * element_type.GetSize(), source_next.GetRawDataPointer(), element_type.GetSize()); 
     1036          } 
     1037          else 
     1038          { 
     1039            tTypedPointer destination_element(static_cast<char*>(destination_object.GetRawDataPointer()) + i * element_type.GetSize(), element_type); 
     1040            destination_element.DeepCopyFrom(source_next); 
     1041          } 
     1042        } 
     1043      } 
     1044    } 
     1045 
     1046    if (fill_elements) 
     1047    { 
     1048      if (element_type.GetTypeTraits() & trait_flags::cIS_DEFAULT_CONSTRUCTION_ZERO_MEMORY) 
     1049      { 
     1050        size_t offset = copy_elements * element_type.GetSize(); 
     1051        memset(static_cast<char*>(destination_object.GetRawDataPointer()) + offset, 0, destination_type.GetSize() - offset); 
     1052      } 
     1053      else 
     1054      { 
     1055        for (size_t i = 0; i < fill_elements; i++) 
     1056        { 
     1057          element_type.EmplaceInstance(static_cast<char*>(destination_object.GetRawDataPointer()) + (copy_elements + i) * element_type.GetSize()); 
     1058        } 
     1059      } 
     1060    } 
     1061  } 
     1062 
     1063  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const 
     1064  { 
     1065    custom_data.computation_types[0] = GetSupportedType(conversion_option.source_type, true); 
     1066    custom_data.computation_types[1] = GetSupportedType(conversion_option.destination_type, true); 
     1067  } 
     1068 
     1069} cADJUST_LENGTH; 
    7251070 
    7261071} 
     
    7401085const tRegisteredConversionOperation& cSTRING_TO_VECTOR_OPERATION = cSTRING_TO_VECTOR; 
    7411086const tRegisteredConversionOperation& cMAKE_STRING_OPERATION = cVECTOR_TO_STRING; 
    742  
     1087const tRegisteredConversionOperation& cADJUST_LENGTH_OPERATION = cADJUST_LENGTH; 
    7431088 
    7441089 
  • defined_conversions.h

    r8 r32  
    8181 
    8282extern const tRegisteredConversionOperation& cGET_LIST_ELEMENT_OPERATION;       //!< Get Element with specified index (parameter) from list type (std::vector) or array type (std::array) 
    83 extern const tRegisteredConversionOperation& cFOR_EACH_OPERATION;               //!< Special conversion operation for std::vectors and std::arrays that applies second conversion operation on all elements (can convert std::vector<T> to std::vector<U>, std::array<T> to std::array<U>, and std::array<T> to std::vector<U>) 
     83extern const tRegisteredConversionOperation& cFOR_EACH_OPERATION;               //!< Special conversion operation for std::vectors and std::arrays that applies second conversion operation on all elements (can convert std::vector<T> to std::vector<U>, std::vector<T> to std::array<U>, std::array<T> to std::array<U>, and std::array<T> to std::vector<U>) 
    8484extern const tRegisteredConversionOperation& cARRAY_TO_VECTOR_OPERATION;        //!< Converts std::array<T> to std::vector<T> 
    8585extern const tRegisteredConversionOperation& cGET_TUPLE_ELEMENT_OPERATION;      //!< Get element of std::pair or std::tuple object 
     
    8989extern const tRegisteredConversionOperation& cSTRING_TO_VECTOR_OPERATION;       //!< Converts std::string to char vector 
    9090extern const tRegisteredConversionOperation& cMAKE_STRING_OPERATION;            //!< Creates std::string from std::vector<char> 
     91extern const tRegisteredConversionOperation& cADJUST_LENGTH_OPERATION;          //!< Converts between std::arrays of different lengths (including types with arrays as underlying types), also supports std::vector as source 
    9192 
    9293//---------------------------------------------------------------------- 
  • tConversionOperationSequence.cpp

    r30 r32  
    178178  if (!type_source) 
    179179  { 
    180     type_source = first_operation ? first_operation->SupportedSourceTypes().single_type : tType(); 
     180    type_source = first_operation && (!first_operation->SupportedSourceTypes().applies_to_underlying_types) ? first_operation->SupportedSourceTypes().single_type : tType(); 
    181181  } 
    182182  if (!type_source) 
     
    187187  if (!type_destination) 
    188188  { 
    189     type_destination = last_operation ? last_operation->SupportedDestinationTypes().single_type : tType(); 
     189    type_destination = last_operation && (!last_operation->SupportedDestinationTypes().applies_to_underlying_types) ? last_operation->SupportedDestinationTypes().single_type : tType(); 
    190190  } 
    191191  if (!type_destination) 
     
    195195  if ((!type_intermediate) && second_operation) 
    196196  { 
    197     type_intermediate = first_operation ? first_operation->SupportedDestinationTypes().single_type : tType(); 
    198     if (!type_intermediate) 
    199     { 
    200       type_intermediate = second_operation ? second_operation->SupportedSourceTypes().single_type : tType(); 
    201     } 
    202     if (!type_intermediate) 
    203     { 
    204       throw std::runtime_error("Intermediate type must be specified"); 
     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)"); 
    205215    } 
    206216  } 
     
    239249  else if (first_operation == &cFOR_EACH_OPERATION) 
    240250  { 
    241     if (!((type_source.IsListType() && type_destination.IsListType()) || 
    242           (type_source.IsArray() && type_destination.IsArray() && type_source.GetArraySize() == destination_type.GetArraySize()) || 
    243           (type_source.IsArray() && type_destination.IsListType()))) 
     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)) 
    244263    { 
    245264      throw std::runtime_error("ForEach operation only applicable on list types and array types"); 
     
    247266    if (!second_operation) 
    248267    { 
    249       temp_conversion_option_2 = GetImplicitConversionOption(type_source.GetElementType(), type_destination.GetElementType()); 
     268      temp_conversion_option_2 = GetImplicitConversionOption(internal_type_source.GetElementType(), internal_type_destination.GetElementType()); 
    250269      if (temp_conversion_option_2.type == tConversionOptionType::NONE) 
    251270      { 
    252         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."); 
     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."); 
    253272      } 
    254273    } 
    255274    else 
    256275    { 
    257       temp_conversion_option_2 = GetConversionOption(second_operation, type_source.GetElementType(), type_destination.GetElementType(), operations[1].parameter.get()); 
     276      temp_conversion_option_2 = GetConversionOption(second_operation, internal_type_source.GetElementType(), internal_type_destination.GetElementType(), operations[1].parameter.get()); 
    258277      if (temp_conversion_option_2.type == tConversionOptionType::NONE) 
    259278      { 
     
    289308    { 
    290309      // Try implicit cast as second 
    291       if (first_operation->SupportedSourceTypes().single_type == source_type && (first_operation->SupportedDestinationTypes().single_type || type_intermediate)) 
     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)) 
    292311      { 
    293312        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedDestinationTypes().single_type; 
     
    295314        temp_conversion_option_2 = GetImplicitConversionOption(type_intermediate, type_destination); 
    296315      } 
    297       else if ((first_operation->SupportedSourceTypes().single_type || type_intermediate) && first_operation->SupportedDestinationTypes().single_type == destination_type) 
     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)) 
    298317      { 
    299318        type_intermediate = type_intermediate ? type_intermediate : first_operation->SupportedSourceTypes().single_type; 
  • tRegisteredConversionOperation.cpp

    r30 r32  
    193193    if (name == operation->Name()) 
    194194    { 
    195       auto option = operation->GetConversionOption((!source_type) && operation->SupportedSourceTypes().filter == tSupportedTypeFilter::SINGLE ? operation->SupportedSourceTypes().single_type : source_type, 
    196                     (!destination_type) && operation->SupportedDestinationTypes().filter == tSupportedTypeFilter::SINGLE ? operation->SupportedDestinationTypes().single_type : destination_type, nullptr); 
     195      auto option = operation->GetConversionOption((!source_type) && operation->SupportedSourceTypes().filter == tSupportedTypeFilter::SINGLE && (!operation->SupportedSourceTypes().applies_to_underlying_types) ? operation->SupportedSourceTypes().single_type : source_type, 
     196                    (!destination_type) && operation->SupportedDestinationTypes().filter == tSupportedTypeFilter::SINGLE && (!operation->SupportedDestinationTypes().applies_to_underlying_types) ? operation->SupportedDestinationTypes().single_type : destination_type, nullptr); 
    197197      if (option.type != tConversionOptionType::NONE) 
    198198      { 
     
    223223    throw std::logic_error("Method must be overridden if no single conversion option is specified"); 
    224224  } 
    225   if (single_conversion_option->source_type == source_type && single_conversion_option->destination_type == destination_type) 
     225 
     226  if (InheritsOperationsFrom(single_conversion_option->source_type, source_type) && InheritsOperationsFrom(single_conversion_option->destination_type, destination_type)) 
    226227  { 
    227228    return *single_conversion_option; 
    228229  } 
    229230  return tConversionOption(); 
     231} 
     232 
     233bool tRegisteredConversionOperation::InheritsOperationsFrom(const tType& underlying_type, const tType& type) 
     234{ 
     235  if (underlying_type == type) 
     236  { 
     237    return true; 
     238  } 
     239  tType check_type = GetUnderlyingTypeOperationsAreInheritedFrom(type); 
     240  return check_type ? InheritsOperationsFrom(underlying_type, check_type) : false; 
    230241} 
    231242 
     
    248259 
    249260  std::vector<tType> result; 
     261  const auto& type_register = tType::GetTypeRegister(); 
    250262  if (single_conversion_option) 
    251263  { 
    252     if (single_conversion_option->source_type == source_type) 
     264    if (InheritsOperationsFrom(single_conversion_option->source_type, source_type)) 
    253265    { 
    254266      result.push_back(single_conversion_option->destination_type); 
     267      if (supported_destination_types.applies_to_underlying_types) 
     268      { 
     269        for (auto it = type_register.Begin(), end = type_register.End(); it != end; ++it) 
     270        { 
     271          if ((*it) != single_conversion_option->destination_type && InheritsOperationsFrom(single_conversion_option->destination_type, *it)) 
     272          { 
     273            result.push_back(*it); 
     274          } 
     275        } 
     276      } 
    255277    } 
    256278  } 
    257279  else 
    258280  { 
    259     const auto& type_register = tType::GetTypeRegister(); 
    260281    for (auto it = type_register.Begin(), end = type_register.End(); it != end; ++it) 
    261282    { 
     
    282303 
    283304  std::vector<tType> result; 
     305  const auto& type_register = tType::GetTypeRegister(); 
    284306  if (single_conversion_option) 
    285307  { 
    286     if (single_conversion_option->destination_type == destination_type) 
     308    if (InheritsOperationsFrom(single_conversion_option->destination_type, destination_type)) 
    287309    { 
    288310      result.push_back(single_conversion_option->source_type); 
     311      if (supported_source_types.applies_to_underlying_types) 
     312      { 
     313        for (auto it = type_register.Begin(), end = type_register.End(); it != end; ++it) 
     314        { 
     315          if ((*it) != single_conversion_option->source_type && InheritsOperationsFrom(single_conversion_option->source_type, *it)) 
     316          { 
     317            result.push_back(*it); 
     318          } 
     319        } 
     320      } 
    289321    } 
    290322  } 
    291323  else 
    292324  { 
    293     const auto& type_register = tType::GetTypeRegister(); 
    294325    for (auto it = type_register.Begin(), end = type_register.End(); it != end; ++it) 
    295326    { 
  • tRegisteredConversionOperation.h

    r30 r32  
    8888  GET_LIST_ELEMENT,    //!< Types supported by get list element 
    8989  ARRAY_TO_VECTOR,     //!< Types supported by array to vector operation 
    90   GET_TUPLE_ELEMENT    //!< Types supported by get tuple element operation 
     90  GET_TUPLE_ELEMENT,   //!< Types supported by get tuple element operation 
     91  ADJUST_LENGTH        //!< Types supported by adjust length operation 
    9192}; 
    9293 
     
    101102  union 
    102103  { 
    103     tConversionFunction conversion_function = nullptr; 
    104     // to be extended 
     104    tConversionFunction conversion_function; 
     105    rrlib::rtti::tType computation_types[2]; 
    105106  }; 
     107 
     108  tCustomOperationData() 
     109  { 
     110    memset(this, 0, sizeof(tCustomOperationData)); 
     111  } 
    106112}; 
    107113 
     
    136142 
    137143    /*! In case 'filter' is SINGLE, contains the single supported type; otherwise it is ignored */ 
    138     rrlib::rtti::tType single_type; 
     144    tType single_type; 
     145 
     146    /*! Whether type filter applies to underlying types also (provided conversion to underlying type is implicit or cINHERITS_ELEMENT_ACCESS_CONVERSION_OPERATIONS is set in trait) */ 
     147    bool applies_to_underlying_types; 
    139148 
    140149    /*! Constructor for multiple type support */ 
    141     tSupportedTypes(tSupportedTypeFilter filter) : filter(filter), single_type() {} 
     150    tSupportedTypes(tSupportedTypeFilter filter) : filter(filter), single_type(), applies_to_underlying_types(false) {} 
    142151 
    143152    /*! Custructor for single type support */ 
    144     tSupportedTypes(const rrlib::rtti::tType& single_type) : filter(tSupportedTypeFilter::SINGLE), single_type(single_type) {} 
    145  
    146  
    147 //    /*! 
    148 //     * Is type supported? 
    149 //     * 
    150 //     * \param type Type to check 
    151 //     * \return Whether type is supported 
    152 //     */ 
    153 //    bool IsTypeSupported(const rrlib::rtti::tType& type) 
    154 //    { 
    155 //      switch (filter) 
    156 //      { 
    157 //      case tSupportedTypeFilter::SINGLE: 
    158 //        return type == single_type; 
    159 //        break; 
    160 //      case tSupportedTypeFilter::BINARY_SERIALIZABLE: 
    161 //        return type.GetTypeTraits() & trait_flags::cIS_BINARY_SERIALIZABLE; 
    162 //        break; 
    163 //      case tSupportedTypeFilter::STRING_SERIALIZABLE: 
    164 //        return type.GetTypeTraits() & trait_flags::cIS_STRING_SERIALIZABLE; 
    165 //        break; 
    166 //      case tSupportedTypeFilter::STATIC_CAST_SUPPORT: 
    167 //        return // TODO? Do we actually need this method (since type support ; 
    168 //        break; 
    169 //      } 
    170 //    } 
     153    tSupportedTypes(const tType& single_type) : filter(tSupportedTypeFilter::SINGLE), single_type(single_type), applies_to_underlying_types(false) {} 
     154  }; 
     155 
     156  /*! Convenience tSupportedTypes subclass that can be used to set applies_to_underlying_types on construction */ 
     157  struct tUnderlyingType : tSupportedTypes 
     158  { 
     159    template <typename T> 
     160    tUnderlyingType(const T& t) : 
     161      tSupportedTypes(t) 
     162    { 
     163      this->applies_to_underlying_types = true; 
     164    } 
    171165  }; 
    172166 
     
    335329  virtual void OnCompile(const tConversionOption& conversion_option, tCustomOperationData& custom_data) const; 
    336330 
     331  /** 
     332   * Obtains underlying type whose respectively flagged conversion operations are inherited 
     333   * 
     334   * \param type Type to obtain underlying type of 
     335   * \return Underlying type whose respectively flagged conversion operations are inherited - or null type if there is no such type 
     336   */ 
     337  static tType GetUnderlyingTypeOperationsAreInheritedFrom(const tType& type) 
     338  { 
     339    return (type.GetTypeTraits() & (trait_flags::cINHERITS_UNDERLYING_TYPE_ELEMENT_ACCESS_OPERATIONS | trait_flags::cIS_CAST_TO_UNDERLYING_TYPE_IMPLICIT)) ? type.GetUnderlyingType() : tType(); 
     340  } 
     341 
     342  /** 
     343   * \param underlying_type Candidate for underlying type 
     344   * \param type Type to check 
     345   * \return Whether type inherits operations from underlying_type 
     346   */ 
     347  static bool InheritsOperationsFrom(const tType& underlying_type, const tType& type); 
     348 
    337349//---------------------------------------------------------------------- 
    338350// Private fields and methods 
Note: See TracChangeset for help on using the changeset viewer.