-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ntuple] Map integral types to fixed width equivalents #16039
Conversation
Test Results 13 files 13 suites 2d 20h 45m 59s ⏱️ Results for commit a764b19. ♻️ This comment has been updated with latest results. |
e245189
to
d573122
Compare
(no changes yet, just to test that the incremental CI is now properly fixed for PRs) |
d573122
to
1049206
Compare
Seems to work, all tests pass --> ready for review 😃 |
0f885e1
to
d607160
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In principle LGTM. Only question about the use of RIntegralField
in the visitor.
virtual void VisitInt16Field(const RField<std::int16_t> &field) { VisitField(field); } | ||
virtual void VisitInt32Field(const RField<std::int32_t> &field) { VisitField(field); } | ||
virtual void VisitInt64Field(const RField<std::int64_t> &field) { VisitField(field); } | ||
virtual void VisitInt8Field(const RIntegralField<std::int8_t> &field) { VisitField(field); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we pass an RField<integer type>
here and for the others? RIntegralField
is an implementation details, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I agreed yesterday evening, but when looking into it I remembered why I decided to pass RIntegralField
: the solution proposed in this PR builds on a surjective, but not injective function that maps any integral type (the T
in RField<T>
) onto one of the (fewer) fixed width integer types (the T
in RIntegralField
, mapped via RIntegralTypeMap
). We could continue to pass RField
s, but we wouldn't know which of RField<long>
, RField<long long>
and RField<std::int64_t>
to implement in the interface (Linux maps typedef long int64_t;
while macOS does typedef long long int64_t;
).
In practical terms it would also not be nice to implement because AcceptVisitor
is currently a part of RIntegralField
, where *this
doesn't have type RField<T>
. We would either have to add dynamic casting in there, or move AcceptVisitor
to the templated RField<T>
. That in turn would again require specializations because the name of the Visit
function depends on the type...
@@ -80,7 +83,7 @@ class RFieldProvider : public RProvider { | |||
} | |||
|
|||
template<typename T> | |||
void FillHistogram(const RField<T> &field) | |||
void FillHistogramImpl(const ROOT::Experimental::RFieldBase &field, ROOT::Experimental::RNTupleView<T, false> &view) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not need to change this, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on my other comment, I think we are forced to change this... 😐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's not forget the follow-up from your comment to remove the tainting of the ROOT namespace
It was the only type translation for the "signed" modifier. If this is needed, it should probably be handled programmatically in GetNormalizedTypeName().
In order to map standard integer types to fixed width equivalents, we want to introduce a C++ type mapping which requires one level of template indirection. For the moment, RField<T> just inherits from RIntegralType<T> for integral types T. Co-authored-by: Florine de Geus <[email protected]>
This already used to work for some types, such as int, which happen to have the same underlying storage as a fixed width type. This change adds a general RIntegralTypeMap with specializations for standard integer types. As a result, RNTuple gains support for (unsigned) long long on Linux, which is now mapped to [u]int64_t. Co-authored-by: Florine de Geus <[email protected]>
Remove or change entries from type translation map and explicitly instantiate RField<T> for standard integer types, which will be mapped to RIntegralType using RIntegralTypeMap since the last commit. Co-authored-by: Florine de Geus <[email protected]>
This is important because the call to RColumn::Map[V] must use the fixed width integer type, but the returned pointer type must match the (unmapped) RField type. Test this via RNTupleView's of standard integer types.
d607160
to
a764b19
Compare
I only did two minor changes to the existing commits, as discussed yesterday evening: diff --git a/tree/ntuple/v7/inc/ROOT/RField.hxx b/tree/ntuple/v7/inc/ROOT/RField.hxx
index 7c9ccebeaa..411ef5733c 100644
--- a/tree/ntuple/v7/inc/ROOT/RField.hxx
+++ b/tree/ntuple/v7/inc/ROOT/RField.hxx
@@ -2179,9 +2179,8 @@ public:
template <typename T>
class RIntegralField {
- // Cannot say static_assert(false) because not all compilers implement CWG2518 yet...
- static_assert(std::is_integral_v<T>, "RIntegralField requires integral type");
- static_assert(!std::is_integral_v<T>, "unsupported integral type");
+ // Instantiating this base template definition should never happen and is an error!
+ RIntegralField() = delete;
};
template <>
@@ -2573,6 +2572,8 @@ template <typename T>
class RField<T, typename std::enable_if<std::is_integral_v<T>>::type> final
: public RIntegralField<typename Internal::RIntegralTypeMap<T>::type> {
using MappedType = typename Internal::RIntegralTypeMap<T>::type;
+ static_assert(sizeof(T) == sizeof(MappedType), "invalid size of mapped type");
+ static_assert(std::is_signed_v<T> == std::is_signed_v<MappedType>, "invalid signedness of mapped type");
public:
RField(std::string_view name) : RIntegralField<MappedType>(name) {} Eventually, after implementing the changes, I decided to hold off moving some member functions to the templated |
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types can be non-normalized. By first explicitly creating fields for the items and using the type names from those fields, this issue is resolved.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types can be non-normalized. By first explicitly creating fields for the items and using the type names from those fields, this issue is resolved.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
With the addition of `RIntegralTypeMap` (PR #16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type name deduction for type-erased fields was moved from the type translation map to `RField`'s template specialization. Upon creation of type-erased STL map(-like) types, originally only the type translation map was used. This now can cause issues where the map's item types may be non-normalized. By using the type names of the subfields of the `std::pair` item field, we ensure the correct inner type names are used.
This already used to work for some types, such as
int
, which happen to have the same underlying storage as a fixed width type. This change adds a generalRIntegralTypeMap
with specializations for standard integer types. As a result, RNTuple gains support for(unsigned) long long
on Linux, which is now mapped to[u]int64_t
.