View Issue Details

IDProjectCategoryView StatusLast Update
0004231NoesisGUIC++ SDKpublic2026-07-31 16:56
ReporterLogris Assigned Tojsantos  
PrioritynormalSeverityfeature 
Status assignedResolutionopen 
Product Version3.2 
Target Version3.2.14 
Summary0004231: Implementation of a range-based loop for ObservableCollection
Description

https://www.noesisengine.com/forums/viewtopic.php?t=3684

PlatformAny

Relationships

related to 0005155 assignedjsantos Range-based for loop in Collection 

Activities

Logris

Logris

2025-08-16 16:35

reporter   ~0010985

Last edited: 2025-08-28 12:37

as an idea, to use a range-like approach for working with a collection

for(auto item : Noesis::Cast<Component>(Noesis::Skip<2>(Items)) {
item->Update();
}

Logris

Logris

2025-08-16 16:35

reporter   ~0010986

Last edited: 2025-08-28 12:34

Logris

Logris

2025-08-28 12:37

reporter   ~0011020

ObservableCollectionIterator.h (6,937 bytes)   
#pragma once

#include <NsGui/ObservableCollection.h>
#include <NsCore/Ptr.h>

namespace Noesis
{
	template<typename T>
	class ObservableCollectionIterator
	{
	public:
		using iterator_category = std::forward_iterator_tag;
		using value_type = T*;
		using difference_type = std::ptrdiff_t;
		using pointer = T**;
		using reference = T*&;

		ObservableCollectionIterator(ObservableCollection<T>* collection, uint32_t index)
			: mCollection(collection), mIndex(index)
		{
		}

		T* operator*() const { return mCollection->Get(mIndex); }
		ObservableCollectionIterator& operator++() { ++mIndex; return *this; }

		bool operator==(const ObservableCollectionIterator& other) const
		{
			return mIndex == other.mIndex;
		}

		bool operator!=(const ObservableCollectionIterator& other) const
		{
			return !(*this == other);
		}
	private:
		ObservableCollection<T>* mCollection;
		uint32_t mIndex;
	};


	template<typename T> 
	ObservableCollectionIterator<T> begin(ObservableCollection<T>* collection) 
	{ 
		return { collection, 0 }; 
	}

	template<typename T> 
	ObservableCollectionIterator<T> begin(const ObservableCollection<T>* collection) 
	{ 
		return { collection, 0 };
	}

	template<typename T>
	ObservableCollectionIterator<T> begin(Ptr<ObservableCollection<T>>& collection)
	{
		return { collection.GetPtr(), 0 };
	}

	template<typename T>
	ObservableCollectionIterator<T> begin(const Ptr<ObservableCollection<T>>& collection)
	{
		return { collection.GetPtr(), 0 };
	}

	template<typename T>
	ObservableCollectionIterator<T>  end(ObservableCollection<T>* collection) 
	{ 
		return { collection, static_cast<uint32_t>(collection->Count()) };
	}

	template<typename T>
	ObservableCollectionIterator<T>  end(const ObservableCollection<T>* collection)
	{
		return { collection, static_cast<uint32_t>(collection->Count()) };
	}

	template<typename T>
	ObservableCollectionIterator<T>  end(Ptr<ObservableCollection<T>>& collection)
	{
		return { collection.GetPtr(), static_cast<uint32_t>(collection->Count())};
	}

	template<typename T>
	ObservableCollectionIterator<T>  end(const Ptr<ObservableCollection<T>>& collection)
	{
		return { collection.GetPtr(), static_cast<uint32_t>(collection->Count()) };
	}
}

namespace Noesis
{
	//Adapters for begin/end
	namespace Detail {
		using std::begin; using std::end;

		template<class Range>
		auto Begin(Range&& r) -> decltype(begin(std::forward<Range>(r))) {
			return begin(std::forward<Range>(r));
		}

		template<class Range>
		auto End(Range&& r) -> decltype(end(std::forward<Range>(r))) {
			return end(std::forward<Range>(r));
		}

		// Skip with offset
		template<class Range>
		auto Begin(Range&& r, std::size_t offset) -> decltype(begin(std::forward<Range>(r))) {
			auto b = begin(std::forward<Range>(r));
			auto e = end(std::forward<Range>(r));
			while (offset-- > 0 && b != e) ++b;
			return b;
		}

		// Skip specializations for ObservableCollection
		template<typename T>
		ObservableCollectionIterator<T> Begin(ObservableCollection<T>* collection, std::size_t offset) {
			if (!collection) return { nullptr, 0 };
			const uint32_t count = collection->Count();
			const uint32_t startIndex = (offset < count) ? static_cast<uint32_t>(offset) : count;
			return { collection, startIndex };
		}

		template<typename T>
		ObservableCollectionIterator<T> Begin(const ObservableCollection<T>* collection, std::size_t offset) {
			return Begin(const_cast<ObservableCollection<T>*>(collection), offset);
		}

		template<typename T>
		ObservableCollectionIterator<T> Begin(Ptr<ObservableCollection<T>>& collection, std::size_t offset) {
			return Begin(collection.GetPtr(), offset);
		}

		template<typename T>
		ObservableCollectionIterator<T> Begin(const Ptr<ObservableCollection<T>>& collection, std::size_t offset) {
			return Begin(collection.GetPtr(), offset);
		}

		template<typename T>
		ObservableCollectionIterator<T> Begin(ObservableCollection<T>& collection, std::size_t offset) {
			return Begin(&collection, offset);
		}

		template<typename T>
		ObservableCollectionIterator<T> Begin(const ObservableCollection<T>& collection, std::size_t offset) {
			return Begin(const_cast<ObservableCollection<T>*>(&collection), offset);
		}
	}
	
	template<typename TDerived, typename TRange>
	class CastRange
	{
		using BaseIterator = decltype(Detail::Begin(std::declval<TRange>()));
		using BaseType = std::remove_pointer_t<decltype(*std::declval<BaseIterator>())>;

		static_assert(std::is_pointer_v<decltype(*std::declval<BaseIterator>())>,
			"CastRange can only be used with ranges of pointers");

		static_assert(IsDerived<TDerived, BaseType>::Result,
			"Derived must inherit from range element type");

	public:
		explicit CastRange(TRange&& range)
			: mRange(std::forward<TRange>(range)) {
		}

		class Iterator
		{
		public:
			using BaseIterator = decltype(Detail::Begin(std::declval<TRange>()));

			Iterator(BaseIterator it) : mIt(it) {
			}

			TDerived* operator*() const {
				return static_cast<TDerived*>(*mIt);
			}

			Iterator& operator++() {
				++mIt;
				return *this;
			}

			bool operator==(const Iterator& other) const {
				return mIt == other.mIt;
			}

			bool operator!=(const Iterator& other) const {
				return mIt != other.mIt;
			}

		private:
			BaseIterator mIt;
		};

		Iterator begin() const {
			return Iterator(Detail::Begin(mRange));
		}

		Iterator end() const {
			return Iterator(Detail::End(mRange));
		}

	private:
		TRange mRange;
	};

	template<typename TDerived, typename TRange>
	auto Cast(TRange&& range)
	{
		return CastRange<TDerived, TRange>(std::forward<TRange>(range));
	}
	// 	// Cast functions for ObservableCollection
	// 
	// 	template<typename TDerived, typename T>
	// 	auto Cast(ObservableCollection<T>* collection)
	// 	{
	// 		return CastRange<TDerived, T>(collection);
	// 	}
	// 
	// 	template<typename TDerived, typename T>
	// 	auto Cast(Ptr<ObservableCollection<T>>& collection)
	// 	{
	// 		return CastRange<TDerived, T>(collection);
	// 	}
	// 
	// 	template<typename TDerived, typename T>
	// 	auto Cast(const Ptr<ObservableCollection<T>>& collection)
	// 	{
	// 		return CastRange<TDerived, T>(collection);
	// 	}
}

namespace Noesis
{
	template<std::size_t Offset, class TRange>
	class SkipRange
	{
		using Range = std::decay_t<TRange>;
	public:
		explicit SkipRange(Range range) : mRange(std::move(range)) {}

		auto begin() const {
			return Detail::Begin(mRange, Offset);
		}

		auto end() const {
			return Detail::End(mRange);
		}

	private:
		Range mRange;
	};

	template<std::size_t Offset, class Range>
	auto Skip(Range&& range) {
		using R = std::decay_t<Range>;
		return SkipRange<Offset, R>(std::forward<Range>(range));
	}

}
ObservableCollectionIterator.h (6,937 bytes)   

Issue History

Date Modified Username Field Change
2025-06-06 13:12 Logris New Issue
2025-06-06 13:27 jsantos Assigned To => jsantos
2025-06-06 13:27 jsantos Status new => assigned
2025-06-06 13:27 jsantos Target Version => 3.2.9
2025-08-16 16:35 Logris Note Added: 0010985
2025-08-16 16:35 Logris Note Added: 0010986
2025-08-16 16:37 Logris Note Edited: 0010986
2025-08-28 12:30 Logris Note Edited: 0010985
2025-08-28 12:34 Logris Note Edited: 0010986
2025-08-28 12:37 Logris Note Edited: 0010985
2025-08-28 12:37 Logris Note Added: 0011020
2025-08-28 12:37 Logris File Added: ObservableCollectionIterator.h
2025-10-02 00:48 jsantos Target Version 3.2.9 => 3.2.10
2025-10-20 18:25 jsantos Target Version 3.2.10 => 3.2.11
2026-01-20 19:32 jsantos Target Version 3.2.11 => 3.2.12
2026-03-04 00:39 jsantos Target Version 3.2.12 => 3.2.13
2026-04-27 12:16 jsantos Target Version 3.2.13 => 3.2.14
2026-07-31 16:56 jsantos Relationship added related to 0005155