Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/igood/igranges

C++ Ranges for Unreal
https://github.com/igood/igranges

cpp20 linq ranges ue5-plugin unreal-engine-plugin

Last synced: 3 months ago
JSON representation

C++ Ranges for Unreal

Awesome Lists containing this project

README

        

# ![Logo](Resources/Icon128.png) for Unreal

[![MIT License](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](/LICENSE)

**IGRanges** is an Unreal Engine 5 plugin that leverages the [Ranges library (C++20)](https://en.cppreference.com/w/cpp/ranges) to provide [LINQ (C#)](https://learn.microsoft.com/en-us/dotnet/csharp/linq/) style code patterns.

Unreal's container types (e.g. `TArray`) do not support Ranges out of the box (yet?), so this plugin first adds the necessary customization point objects to make them compatible.\
Beyond that, a handful of common mapping & filtering operations have been implemented in ways that are familiar to programmers acquainted with UE & LINQ.

----

## C++20 Ranges, But Think LINQ

A quick primer for Ranges from [cppreference.com](https://en.cppreference.com/w/cpp/ranges):
> The ranges library is an extension and generalization of the algorithms and iterator libraries that makes them more powerful by making them composable and less error-prone.\
The library creates and manipulates range views, lightweight objects that indirectly represent iterable sequences (ranges).

### 👩‍💻 Example A:
The following example "pipes" an array of integers through views to generate a sequence that is a modified subset of the original values.

#### 👩‍💻 Example A.1:
```cpp
int myInts[] = { 0, 1, 2, 3, 4, 5 };
auto even = [](int i) { return i % 2 == 0; };
auto square = [](int i) { return i * i; };
// the "pipe" syntax of composing the views:
for (int i : myInts | std::views::filter(even) | std::views::transform(square))
std::cout << i << ' ';
// Output: 0 4 16
```

This example is very *C++*-looking. It's not very *UE*-looking & it doesn't read quite as clearly as LINQ.\
If we were to rewrite it in a UE style with IGRanges, then it might look something like this:

#### 👩‍💻 Example A.2:
```cpp
TArray MyInts = { 0, 1, 2, 3, 4, 5 };
auto IsEven = [](int i) { return i % 2 == 0; };
auto Square = [](int i) { return i * i; };
FString Result;
for (int32 i : MyInts | Where(IsEven) | Select(Square))
{
Result.AppendInt(i);
Result += TEXT(", ");
}
UE_LOG(LogTemp, Log, TEXT("Result{%s}"), *Result);
// Output: Result{0, 4, 16,}
```

Or, depending on style preferences, it might look something like this:

#### 👩‍💻 Example A.3:
```cpp
TArray MyInts = { 0, 1, 2, 3, 4, 5 };
auto Sqevens = MyInts
| Where([](int i) { return i % 2 == 0; })
| Select([](int i) { return i * i; });
FString Result;
for (int32 i : Sqevens)
{
Result.AppendInt(i);
Result += TEXT(", ");
}
```

### 👩‍💻 Example B:

A better demonstration of IGRanges is shown in the following example.\
The declarative syntax can make it easier to understand the intent of the code.\
In this case, we have a collection of pointers to Actors (some of which might be null) & we want to get the World from one of them.

ImperativeDeclarative

```cpp
TArray MaybeActors = ...;
UWorld* World = nullptr;
for (const AActor* Actor : MaybeActors)
{
if (Actor != nullptr)
{
World = Actor->GetWorld();
break;
}
}
if (World != nullptr) ...
```

```cpp
TArray MaybeActors = ...;
UWorld* World = MaybeActors
| NonNull()
| Select(&AActor::GetWorld)
| FirstOrDefault();
if (World != nullptr) ...

```

### 👩‍💻 Example C:

Filtering & collecting elements also becomes very easy with IGRanges.

ImperativeDeclarative

```cpp
TArray SomeObjects = ...;
TArray Foos;
for (UObject* Obj : SomeObjects)
{
if (UFoo* Foo = Cast(Obj))
{
Foos.Add(Foo);
}
}
```

```cpp
TArray SomeObjects = ...;
TArray Foos = SomeObjects | OfType() | ToArray();

```

----

### ✨ Features

- `Where`, `WhereNot`, `SafeWhere`, `SafeWhereNot`
- `NonNull`, `NonNullRef`
- `Select`, `SelectNonNull`
- `Cast`, `CastExact`, `CastChecked`, `CastCheckedRef`
- `OfType`, `OfTypeRef`, `OfTypeExact`, `OfTypeExactRef`, `OfType`, `OfTypeRef`
- `FirstOrDefault`
- `Count`
- `Sum`
- `Accumulate`
- `ToArray`
- `ToSet`
- `All`, `Any`, `None`
- `Selectors::CDO`
- `Filters::IsChildOf`, `Filters::IsChildOf`

----

### 🔗 Related Links

- [Ranges library (C++20)](https://en.cppreference.com/w/cpp/ranges)
- [\ | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/standard-library/ranges)
- [Language Integrated Query (LINQ) - C# | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/csharp/linq/)