-
Notifications
You must be signed in to change notification settings - Fork 9
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
Added reference_proxy and bitset_view #13
Changes from all commits
e191772
46cd093
1e515dc
8dc34bd
f599826
8ad25d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,187 @@ | ||||||||||
// | ||||||||||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||
// you may not use this file except in compliance with the License. | ||||||||||
// You may obtain a copy of the License at | ||||||||||
// | ||||||||||
// http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||
// | ||||||||||
// Unless required by applicable law or agreed to in writing, software | ||||||||||
// distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||
// See the License for the specific language governing permissions and | ||||||||||
// limitations under the License. | ||||||||||
|
||||||||||
#pragma once | ||||||||||
|
||||||||||
#include <climits> | ||||||||||
#include <cstdint> | ||||||||||
#include <vector> | ||||||||||
|
||||||||||
#include "sparrow/buffer.hpp" | ||||||||||
#include "sparrow/dynamic_bitset.hpp" | ||||||||||
#include "sparrow/data_type.hpp" | ||||||||||
|
||||||||||
namespace sparrow | ||||||||||
{ | ||||||||||
struct array_data | ||||||||||
{ | ||||||||||
data_descriptor type; | ||||||||||
std::int64_t length = 0; | ||||||||||
std::int64_t offset = 0; | ||||||||||
using block_type = std::uint8_t; | ||||||||||
using bitmap_type = dynamic_bitset<block_type>; | ||||||||||
// bitmap buffer and null_count | ||||||||||
bitmap_type bitmap; | ||||||||||
// Other buffers | ||||||||||
std::vector<buffer<block_type>> buffers; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why define |
||||||||||
std::vector<array_data> child_data; | ||||||||||
Comment on lines
+36
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be used instead?
Suggested change
|
||||||||||
}; | ||||||||||
|
||||||||||
struct null_type | ||||||||||
{ | ||||||||||
}; | ||||||||||
constexpr null_type null; | ||||||||||
|
||||||||||
template <class T> | ||||||||||
class reference_proxy | ||||||||||
{ | ||||||||||
public: | ||||||||||
|
||||||||||
using self_type = reference_proxy<T>; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My preference goes to |
||||||||||
using value_type = T; | ||||||||||
using reference = T&; | ||||||||||
using const_reference = const T&; | ||||||||||
using pointer = T*; | ||||||||||
using size_type = std::size_t; | ||||||||||
|
||||||||||
reference_proxy(reference ref, size_type index, array_data& ad); | ||||||||||
reference_proxy(const self_type&) = default; | ||||||||||
reference_proxy(self_type&&) = default; | ||||||||||
|
||||||||||
self_type& operator=(const self_type&); | ||||||||||
self_type& operator=(self_type&&); | ||||||||||
self_type& operator=(null_type); | ||||||||||
|
||||||||||
template <class U> | ||||||||||
self_type& operator=(const U&); | ||||||||||
|
||||||||||
template <class U> | ||||||||||
self_type& operator+=(const U&); | ||||||||||
|
||||||||||
template <class U> | ||||||||||
self_type& operator-=(const U&); | ||||||||||
|
||||||||||
template <class U> | ||||||||||
self_type& operator*=(const U&); | ||||||||||
|
||||||||||
template <class U> | ||||||||||
self_type& operator/=(const U&); | ||||||||||
|
||||||||||
operator const_reference() const; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This requires that you explicitly cast each time you want to compare the proxy to a value (for instance), which is paricularly ugly (and does not looks naturaly at all to me): template <class T>
struct array_impl
{
using reference = reference_proxy<std::uint8_t>;
const_reference = const std::uint8_t&;
using size_type = std::size_t;
reference operator[](size_type i);
};
array<std::uint8_t> a = ....;
uint8_t expected = 8u;
// With explicit you need to
using const_reference = array<std::uint8_t>::const_reference;
bool b = (const_reference(a[i]) == expected);
// while without, you can simply write:
bool b = a[i] == expected; How having this operator implicit can be harmful? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
See this guideline for the fundations of why it's a very bad idea. It goes in pair with the guideline about conversion operator that must also be explicit. The cases where this would not work as so rare I, so far, never saw a justifyiable example that isn't improved by
That just means that the reference type needs comparison operators with objects of the type being referred to. That conversion is a shortctut that can cause more issues than explicitly stating these operators. Also note that even like that "ugly" is not a reason to make the code potentially silently incorrect, in particular in a C++ library supposedly trying to make correctness easier than the C api. Worst scenario, See as example: https://godbolt.org/z/7jxcvjq3Y I see no reason to make that operator implicit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot a test in my example, here is the udpated version (I only added the test): https://godbolt.org/z/TYx7sGezj |
||||||||||
|
||||||||||
private: | ||||||||||
|
||||||||||
void update_value(value_type value); | ||||||||||
void update_value(null_type); | ||||||||||
|
||||||||||
pointer p_ref; | ||||||||||
size_type m_index; | ||||||||||
array_data* p_array_data; | ||||||||||
}; | ||||||||||
|
||||||||||
/********************************** | ||||||||||
* reference_proxy implementation * | ||||||||||
**********************************/ | ||||||||||
|
||||||||||
template <class T> | ||||||||||
reference_proxy<T>::reference_proxy(reference ref, size_type index, array_data& ad) | ||||||||||
: p_ref(&ref) | ||||||||||
, m_index(index) | ||||||||||
, p_array_data(&ad) | ||||||||||
{ | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
auto reference_proxy<T>::operator=(const self_type& rhs) -> self_type& | ||||||||||
{ | ||||||||||
update_value(*rhs.p_ref); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
auto reference_proxy<T>::operator=(self_type&& rhs) -> self_type& | ||||||||||
{ | ||||||||||
update_value(*rhs.p_ref); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
auto reference_proxy<T>::operator=(null_type rhs) -> self_type& | ||||||||||
{ | ||||||||||
update_value(std::move(rhs)); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
template <class U> | ||||||||||
auto reference_proxy<T>::operator=(const U& u) -> self_type& | ||||||||||
{ | ||||||||||
update_value(u); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
template <class U> | ||||||||||
auto reference_proxy<T>::operator+=(const U& u) -> self_type& | ||||||||||
{ | ||||||||||
update_value(*p_ref + u); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
template <class T> | ||||||||||
template <class U> | ||||||||||
auto reference_proxy<T>::operator-=(const U& u) -> self_type& | ||||||||||
{ | ||||||||||
update_value(*p_ref - u); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
template <class U> | ||||||||||
auto reference_proxy<T>::operator*=(const U& u) -> self_type& | ||||||||||
{ | ||||||||||
update_value(*p_ref * u); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
template <class U> | ||||||||||
auto reference_proxy<T>::operator/=(const U& u) -> self_type& | ||||||||||
{ | ||||||||||
update_value(*p_ref / u); | ||||||||||
return *this; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
reference_proxy<T>::operator const_reference() const | ||||||||||
{ | ||||||||||
return *p_ref; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
void reference_proxy<T>::update_value(value_type value) | ||||||||||
{ | ||||||||||
auto& bitmap = p_array_data->bitmap; | ||||||||||
bitmap.set(m_index, true); | ||||||||||
*p_ref = value; | ||||||||||
} | ||||||||||
|
||||||||||
template <class T> | ||||||||||
void reference_proxy<T>::update_value(null_type) | ||||||||||
{ | ||||||||||
auto& bitmap = p_array_data->bitmap; | ||||||||||
bitmap.set(m_index, false); | ||||||||||
} | ||||||||||
} | ||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -26,12 +26,23 @@ namespace sparrow | |||||
struct buffer_data | ||||||
{ | ||||||
using value_type = T; | ||||||
using reference = T&; | ||||||
using const_reference = const T&; | ||||||
using pointer = T*; | ||||||
using size_type = std::size_t; | ||||||
|
||||||
bool empty() const noexcept; | ||||||
size_type size() const noexcept; | ||||||
|
||||||
reference operator[](size_type); | ||||||
const_reference operator[](size_type) const; | ||||||
|
||||||
reference front(); | ||||||
const_reference front() const; | ||||||
|
||||||
reference back(); | ||||||
const_reference back() const; | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah they should be, and I want to refactor this hierarchy to be consistent with the dynamic bitset. We can add it in an incoming PR though, to keep this one quite "small" (it is actually already big) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding TODOs for these so that we dont forget in further passes. |
||||||
template <class U = T> | ||||||
U* data() noexcept; | ||||||
|
||||||
|
@@ -62,6 +73,7 @@ namespace sparrow | |||||
|
||||||
buffer() = default; | ||||||
explicit buffer(size_type size); | ||||||
buffer(size_type size, value_type value); | ||||||
buffer(pointer data, size_type size); | ||||||
|
||||||
~buffer(); | ||||||
|
@@ -74,9 +86,14 @@ namespace sparrow | |||||
|
||||||
using base_type::empty; | ||||||
using base_type::size; | ||||||
using base_type::operator[]; | ||||||
using base_type::front; | ||||||
using base_type::back; | ||||||
using base_type::data; | ||||||
|
||||||
void resize(size_type new_size); | ||||||
void resize(size_type new_size, value_type value); | ||||||
void clear(); | ||||||
|
||||||
void swap(buffer&) noexcept; | ||||||
bool equal(const buffer& rhs) const; | ||||||
|
@@ -109,6 +126,9 @@ namespace sparrow | |||||
|
||||||
using base_type::empty; | ||||||
using base_type::size; | ||||||
using base_type::operator[]; | ||||||
using base_type::front; | ||||||
using base_type::back; | ||||||
using base_type::data; | ||||||
|
||||||
void swap(buffer_view&) noexcept; | ||||||
|
@@ -136,6 +156,42 @@ namespace sparrow | |||||
return m_size; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
auto buffer_data<T>::operator[](size_type pos) -> reference | ||||||
{ | ||||||
return data()[pos]; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
auto buffer_data<T>::operator[](size_type pos) const -> const_reference | ||||||
{ | ||||||
return data()[pos]; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
auto buffer_data<T>::front() -> reference | ||||||
{ | ||||||
return data()[0]; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
auto buffer_data<T>::front() const -> const_reference | ||||||
{ | ||||||
return data()[0]; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
auto buffer_data<T>::back() -> reference | ||||||
{ | ||||||
return data()[m_size - 1]; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
auto buffer_data<T>::back() const -> const_reference | ||||||
{ | ||||||
return data()[m_size - 1]; | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
template <class U> | ||||||
U* buffer_data<T>::data() noexcept | ||||||
|
@@ -174,6 +230,13 @@ namespace sparrow | |||||
{ | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
buffer<T>::buffer(size_type size, value_type value) | ||||||
: base_type{allocate(size), size} | ||||||
{ | ||||||
std::fill(data(), data() + size, value); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an alternative.
Suggested change
|
||||||
} | ||||||
|
||||||
template <class T> | ||||||
buffer<T>::buffer(pointer data, size_type size) | ||||||
: base_type{data, size} | ||||||
|
@@ -188,7 +251,7 @@ namespace sparrow | |||||
|
||||||
template <class T> | ||||||
buffer<T>::buffer(const buffer<T>& rhs) | ||||||
: base_type{allocate(rhs.m_size), rhs.size()} | ||||||
: base_type{allocate(rhs.size()), rhs.size()} | ||||||
{ | ||||||
std::copy(rhs.data(), rhs.data() + rhs.size(), data()); | ||||||
} | ||||||
|
@@ -226,11 +289,29 @@ namespace sparrow | |||||
if (n != size()) | ||||||
{ | ||||||
buffer<T> tmp(n); | ||||||
std::copy(data(), data() + size(), tmp.data()); | ||||||
size_type copy_size = std::min(size(), n); | ||||||
Klaim marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
std::copy(data(), data() + copy_size, tmp.data()); | ||||||
swap(tmp); | ||||||
} | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
void buffer<T>::resize(size_type n, value_type value) | ||||||
{ | ||||||
size_type old_size = size(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
resize(n); | ||||||
if (old_size < n) | ||||||
{ | ||||||
std::fill(data() + old_size, data() + n, value); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, one can use:
Suggested change
|
||||||
} | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
void buffer<T>::clear() | ||||||
{ | ||||||
resize(size_type(0)); | ||||||
} | ||||||
|
||||||
template <class T> | ||||||
void buffer<T>::swap(buffer<T>& rhs) noexcept | ||||||
{ | ||||||
|
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.
Could you add some documentation for each one of these types?