class AutoC::Range
@abstract
Constants
- INFO
Attributes
iterable[R]
Public Class Methods
new(iterable, visibility:)
click to toggle source
Calls superclass method
AutoC::Composite::new
# File lib/autoc/ranges.rb, line 27 def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end
Public Instance Methods
brief(= "@brief Range (iterator) for type
click to toggle source
# File lib/autoc/ranges.rb, line 34 def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end
comparable?(= false)
click to toggle source
# File lib/autoc/ranges.rb, line 21 def comparable? = false def orderable? = false def copyable? = false def to_value = @v ||= Value.new(self) def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO = Code
configure()
click to toggle source
Calls superclass method
AutoC::Composite#configure
# File lib/autoc/ranges.rb, line 63 def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end
copyable?(= false)
click to toggle source
# File lib/autoc/ranges.rb, line 23 def copyable? = false def to_value = @v ||= Value.new(self) def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO = Code.new interface:
default_constructible?(= false)
click to toggle source
# File lib/autoc/ranges.rb, line 19 def default_constructible? = false def destructible? = false def comparable? = false def orderable? = false def copyable? = false def to_value = @v ||= Value.new(self) def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO
destructible?(= false)
click to toggle source
# File lib/autoc/ranges.rb, line 20 def destructible? = false def comparable? = false def orderable? = false def copyable? = false def to_value = @v ||= Value.new(self) def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO =
orderable?(= false)
click to toggle source
# File lib/autoc/ranges.rb, line 22 def orderable? = false def copyable? = false def to_value = @v ||= Value.new(self) def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO = Code.new
render_type_description(stream)
click to toggle source
# File lib/autoc/ranges.rb, line 36 def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end
to_value(= @v ||= Value.new(self))
click to toggle source
# File lib/autoc/ranges.rb, line 25 def to_value = @v ||= Value.new(self) def initialize(iterable, visibility:) super(iterable.identifier(:range, abbreviate: internal?), visibility:) dependencies << (@iterable = iterable) << INFO end def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO = Code.new interface: %{ /** @page Range @brief Generalization of the iterator A range is a means of traversing through the container's contents in which it is similar to the iterator. Current implementation is loosely modeled after the [D language ranges](https://dlang.org/phobos/std_range.html). Note that current ranges' implementation is fairly basic lacking iterable alteration, thread safety etc. On the other hand, all currently implemented ranges are the simple value types which do not require explicit copying/destruction thus making life slightly easier. Therefore they can be passed out in/out the functions as is - just watch out the dangers of passing the iterable values they are bound to. A sample code involving iteration over the contents of a hypothetical `List` iterable value is shown below. @code{.c} List list; ... for(ListRange r = ListRangeNew(&list); !ListRangeEmpty(&r); ListRangePopFront(&r)) { ... = ListRangeTakeFront(&r); } @endcode Currently implemented range archetypes: @subpage InputRange @subpage ForwardRange @subpage BidirectionalRange @subpage DirectAccessRange @since 2.0 @page InputRange @brief Basic unidirectional range An input range is a @ref Range which sports a single direction of traversing the elements. @since 2.0 @page ForwardRange @brief Unidirectional range with checkpoint A forward range is an @ref InputRange which also allows to make a snapshot of the current range's state for possible fallback. @since 2.0 @page BidirectionalRange @brief Basic bidirectional range A bidirectional range is a @ref ForwardRange which can also be traversed backwards. @since 2.0 @page DirectAccessRange @brief Bidirectional range with indexed access to specific elements A random access range is a @ref BidirectionalRange which is also capable of accessing the elements directly using index. @since 2.0 */ }
type_tag(= "
click to toggle source
# File lib/autoc/ranges.rb, line 32 def type_tag = "#{iterable.type_tag}::Range" def brief = "@brief Range (iterator) for type #{iterable.type_tag}" def render_type_description(stream) stream << %{ /** #{defgroup} #{brief} Range is means of traversing though the container contents in a sequential manner. It is basically an extension of iterator. It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end private alias _iterable iterable # Use _iterable.() within method bodies as it is shadowed by the commonly used iterable function parameter def configure super method(:void, :create, { range: lvalue, iterable: iterable.const_rvalue }, instance: :custom_create, constraint:-> { custom_constructible? }).configure do header %{ @brief Create a new range for the specified iterable @param[out] range range to be initialized @param[in] iterable container to iterate over This function creates a range to iterate over all iterable's elements. @note Previous contents of `*range` is overwritten. @see #{type.new} @since 2.0 } end method(self, :new, { iterable: iterable.const_rvalue }).configure do dependencies << custom_create header %{ @brief Return new range iterator for the specified container @param[in] iterable container to iterate over @return new initialized range This function returns a new range created by @ref #{type.custom_create}. It is intended to be used within the ***for(;;)*** statement as follows @code{.c} for(#{type.signature} r = #{type.new}(&it); !#{type.empty}(&r); #{type.pop_front}(&r)) { ... } @endcode where `it` is the iterable to iterate over and `r` is a locally-scoped range bound to it. @since 2.0 } inline_code %{ #{type} r; assert(iterable); #{custom_create.(:r, iterable)}; return r; } end end end # Range # @abstract class InputRange < Range private def configure super method(:int, :empty, { range: const_rvalue }).configure do header %{ @brief Check for range emptiness @param[in] range range to check @return non-zero value if the range is not empty or zero value otherwise An empty range is the range for which there are to accessible elements left. This specifically means that any calls to the element retrieval and position change functions (@ref #{type.take_front}, @ref #{type.view_front}, @ref #{type.pop_front} et al.) are invalid for empty ranges. @since 2.0 } end method(:void, :pop_front, { range: rvalue }).configure do header %{ @brief Advance front position to the next existing element @param[in] range range to advance front position for This function is used to get to the next element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Advancing position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front element @param[in] range range to retrieve element from @return a view of an element at the range's front position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's front position. Refer to @ref #{type.take_front} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_front, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_front inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_front.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front element @param[in] range range to retrieve element from @return a *copy* of element at the range's front position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_front} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # InputRange # @abstract class ForwardRange < InputRange def copyable? = true private def configure super copy.configure do header %{ @brief Create a copy of the range state @param[out] target range to be created @param[in] source range to be cloned This function is meant to an independent copy (a clone) of `*source` range in place of `*target`. Previous contents of `*target` is overwritten. @since 2.0 } inline_code %{ assert(target); assert(source); *target = *source; } end end end # ForwardRange # @abstract class BidirectionalRange < ForwardRange private def configure super method(:void, :pop_back, { range: rvalue }).configure do header %{ @brief Rewind back position to the previous existing element @param[in] range range to rewind back position for This function is used to get to the previous element in the range. @note Prior calling this function one must ensure that the range is not empty (see @ref #{type.empty}). Rewinding position of a range that is already empty results in undefined behaviour. @since 2.0 } end method(iterable.element.const_lvalue, :view_back, { range: const_rvalue }).configure do header %{ @brief Get a view of the back element @param[in] range range to retrieve element from @return a view of an element at the range's back position This function is used to get a constant reference (in form of the C pointer) to the value contained in the iterable container at the range's back position. Refer to @ref #{type.take_back} to get an independent copy of that element. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.element, :take_back, { range: const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << empty << view_back inline_code %{ #{iterable.element} result; #{iterable.element.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_back.(range)}; #{iterable.element.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the back element @param[in] range range to retrieve element from @return a *copy* of element at the range's back position This function is used to get a *copy* of the value contained in the iterable container at the range's front position. Refer to @ref #{type.view_back} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # BidirectionalRange # @abstract class DirectAccessRange < BidirectionalRange private def configure super method(:size_t, :size, { range: const_rvalue }).configure do header %{ @brief Get a number of elements in the range @param[in] range range to query @return a number of elements This function returns a number of elements between the range's front and back positions inclusively. As a consequence, the result changes with every invocation of position change functions (@ref #{type.pop_front}, @ref #{type.pop_back}), so be careful not to cache this value. For empty range this function returns 0. @since 2.0 } end method(:int, :check, { range: const_rvalue, index: :size_t.const_rvalue } ).configure do dependencies << size inline_code %{ assert(range); return index < #{size.(range)}; } header %{ @brief Validate specified range's index @param[in] range range to query @param[in] index index to verify @return non-zero value if speicfied index is valid and zero value otherwise This function performs the range's index validity check. In any case, this function should be used for the index validation prior getting direct access to range's elements with @ref #{type.get}, @ref #{type.view} etc. as these functions do not normally do it themselves for performance reasons. @since 2.0 } end method(iterable.element.const_lvalue, :view, { range: const_rvalue, index: :size_t.const_rvalue }).configure do header %{ @brief Get view of the specific element @param[in] range range to view element from @param[in] index position to access element at @return a view of element at `index` This function is used to get a constant reference (in form of the C pointer) to the value contained in the range at the specific position. Refer to @ref #{type.get} to get a copy of the element. @note The specified `index` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end method(iterable.element, :get, { range: const_rvalue, index: :size_t.const_rvalue }, constraint:-> { iterable.element.copyable? }).configure do dependencies << check << view inline_code %{ #{iterable.element} r; #{iterable.element.const_lvalue} e; assert(#{check.(range, index)}); e = #{view.(range, index)}; #{iterable.element.copy.(:r, '*e')}; return r; } header %{ @brief Get a copy of the specific element @param[in] range range to retrieve element from @param[in] index position to view element at @return a *copy* of element at `index` This function is used to get a *copy* of the value contained in the range at the specific position. Refer to @ref #{type.view} to get a view of the element without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note The specified `position` is required to be within the [0, @ref #{type.size}) range. @since 2.0 } end end end # DirectAccessRange # @abstract class ContiguousRange < DirectAccessRange def initialize(iterable, visibility: :public, parallel: nil) super(iterable, visibility:) @parallel = parallel end def render_interface(stream) if public? case @parallel when nil stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) It can be used the following way: @code{.c} for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } when :openmp stream << %{ /** #{defgroup} #{brief} This is the range for contiguous data structures (vectors, strings etc.) The @ref #{new} and @ref #{custom_create} range constructors create OpenMP-aware range objects which account for parallel iteration in the way @code{.c} #pragma omp parallel for(#{signature} r = #{new}(&it); !#{empty}(&r); #{pop_front}(&r)) { ... } @endcode @see @ref Range @since 2.0 */ } end end if public? stream << %{ /** #{ingroup} @brief Opaque structure holding state of the contiguous container's range @since 2.0 */ } else stream << PRIVATE end stream << %{ typedef struct { #{iterable.element.lvalue} front; /**< @private */ #{iterable.element.lvalue} back; /**< @private */ } #{signature}; } end private OPTIONAL_OMP_H = AutoC::Code.new interface: %{ #ifdef _OPENMP #include <omp.h> #endif } def configure super case @parallel when nil custom_create.configure do inline_code %{ assert(range); assert(iterable); range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + #{_iterable.size.(iterable)} - 1; } end when :openmp dependencies << OPTIONAL_OMP_H custom_create.configure do inline_code %{ size_t size; #ifdef _OPENMP unsigned chunk_count; #endif assert(range); assert(iterable); size = #{_iterable.size.(iterable)}; #ifdef _OPENMP if(omp_in_parallel() && (chunk_count = omp_get_num_threads()) > 1) { int chunk_id = omp_get_thread_num(); size_t chunk_size = size / omp_get_num_threads(); range->front = #{_iterable.storage(iterable)} + chunk_id*chunk_size; range->back = #{_iterable.storage(iterable)} + ( chunk_id < chunk_count - 1 ? (chunk_id + 1)*chunk_size - 1 : size - 1 ); } else { #endif range->front = #{_iterable.storage(iterable)}; range->back = #{_iterable.storage(iterable)} + size - 1; #ifdef _OPENMP } #endif } end else raise "unsupported parallel range specifier #{@parallel}" end empty.configure do inline_code %{ assert(range); assert(range->front); assert(range->back); return range->front > range->back; } end pop_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); ++range->front; } end pop_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); --range->back; } end view_front.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(!#{empty.(range)}); return range->front; } end view_back.configure do dependencies << empty inline_code %{ assert(range); assert(range->back); assert(!#{empty.(range)}); return range->back; } end size.configure do dependencies << empty inline_code %{ assert(range); assert(range->front); assert(range->back); return #{empty.(range)} ? 0 : range->back - range->front + 1; } end view.configure do dependencies << check inline_code %{ assert(range); assert(#{check.(range, index)}); return range->front + index; } end end end # ContiguousRange class AssociativeRange < ForwardRange private def configure super method(iterable.index.const_lvalue, :view_index_front, { range: const_rvalue }).configure do header %{ @brief Get a view of the front index @param[in] range range to retrieve element from @return a view of an index at the range's front position This function is used to get a constant reference (in form of the C pointer) to the index associated with element at the range's front position. Refer to @ref #{type.take_index_front} to get an independent copy of that index. It is generally not safe to bypass the constness and to alter the value in place (although no one prevents to). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end method(iterable.index, :take_index_front, { range: const_rvalue }, constraint:-> { iterable.index.copyable? && iterable.element.copyable? }).configure do dependencies << empty << view_index_front inline_code %{ #{iterable.index} result; #{iterable.index.const_lvalue} e; assert(!#{empty.(range)}); e = #{view_index_front.(range)}; #{iterable.index.copy.(:result, '*e')}; return result; } header %{ @brief Get a copy of the front index associated with element @param[in] range range to retrieve element from @return a *copy* of index at the range's front position This function is used to get a *copy* of the index associated with element at the range's front position. Refer to @ref #{type.view_index_front} to get a view of the index without making an independent copy. This function requires the element type to be *copyable* (i.e. to have a well-defined copy operation). @note Range must not be empty (see @ref #{type.empty}). @since 2.0 } end end end # AssociativeRange Range::INFO = Code.new interface: %{ /** @page Range @brief Generalization of the iterator A range is a means of traversing through the container's contents in which it is similar to the iterator. Current implementation is loosely modeled after the [D language ranges](https://dlang.org/phobos/std_range.html). Note that current ranges' implementation is fairly basic lacking iterable alteration, thread safety etc. On the other hand, all currently implemented ranges are the simple value types which do not require explicit copying/destruction thus making life slightly easier. Therefore they can be passed out in/out the functions as is - just watch out the dangers of passing the iterable values they are bound to. A sample code involving iteration over the contents of a hypothetical `List` iterable value is shown below. @code{.c} List list; ... for(ListRange r = ListRangeNew(&list); !ListRangeEmpty(&r); ListRangePopFront(&r)) { ... = ListRangeTakeFront(&r); } @endcode Currently implemented range archetypes: @subpage InputRange @subpage ForwardRange @subpage BidirectionalRange @subpage DirectAccessRange @since 2.0 @page InputRange @brief Basic unidirectional range An input range is a @ref Range which sports a single direction of traversing the elements. @since 2.0 @page ForwardRange @brief Unidirectional range with checkpoint A forward range is an @ref InputRange which also allows to make a snapshot of the current range's state for possible fallback. @since 2.0 @page BidirectionalRange @brief Basic bidirectional range A bidirectional range is a @ref ForwardRange which can also be traversed backwards. @since 2.0 @page DirectAccessRange @brief Bidirectional range with indexed access to specific elements A random access range is a @ref BidirectionalRange which is also capable of accessing the elements directly using index. @since 2.0 */ }