Division By Zero Exploration pt 2

This is a continuation of an earlier post called, Division By Zero Exploration, which was a continuation of an even earlier post, Division By Zero.

In the exploration post, I concluded that, if dividing by zero is valid, then, “it is basically possible to make anything equal anything”. Further thought has lead to an additional conclusion: one does not necessarily equal one. This becomes evident from the basic properties of Q and 0, and specifically, infinity times zero equals one, or Q*0=1. If this holds true, then one does not necessarily equal one. Here we go.

What I mean is that: one may not equal one, all the time. This is because we can rewrite Q*0 = 1 an infinite amount of ways by adding additional zeros or Qs. ( At least, if 0*0 = 0 and Q*Q=Q). The way the expression is factored causes it to have a potential series of possible answers, some true, and others false.

There are two basic expansions, each which results in two different possible outcomes, for a potential of three answers. The first is Q*0*0. If it holds true that 0*0 always equals zero, then this should be a rational procedure. This results in two possible factorings: (Q*0)*0 and Q*(0*0). The first factoring equals 1, the second equals 0. The other basic expansion is Q*Q*0. This ends up resulting in either 1 or Q.

When we add in the x variable, Q*0*X, we get outcomes 1 or X.

Using this process, I go back to the previous example of a=b and start with a+b=b. From here, there are a lot of potential procedures, but the simplest path to a=b is two steps, I think.

a + 1*b = b

a + Q*0*b = b

Q*0*b resolves to 1 which resolves to Q*0*0

a + Q*0*0 = b

Q*0*0 resolves to 0

a + 0 = b

Of course, there are other outcomes from this procedure that are false. These are the other generated outcomes:

a + b = b

a + 1 = b

This leads to another conclusion, zero does not necessarily equal zero, nor does Q equal Q.

I think inventing math is my new hobby, comments are appreciated.

Structured Bindings and replacing by reference

C++ 17 has Structured Bindings, what they do is create a temporary object and provide access to the object’s members locally.

struct S{
    int a{5};
    float b{3.2};
};

auto [x,y] = S();

What happens is we create a temporary S object, and then copy the values memberwise into the binding structure. x now equals 5 and b equals 3.2. the S instance goes away.

Since functions only can return one value normally, to get additional values out, we use by-reference variables.

void f( int& a, float& b ){
    a = 10; b = 3.2f;
}

This leads to a programming style where we do two things:

  1. create variables to hold the return values before the call
  2. pass the variable to the function as a parameter.
int x=0;
float y=0;
f( x, y );
//x == 10; y = 3.2f;

It has never been glancingly apparent that the values are not only being passed by reference, which is often the case for performance, but they are also output from the function with changed values.

Structured Bindings provides a solution to this potential issue:

auto f(){
    int a{10};
    float b{3.2f};

    return std::make_tuple(a,b);
}

auto [x,y] = f();

Since tuples can have as many parameters as we want, they are an easy way to accomplish creating the return structure for the binding.

In C++11 there is a less effective way to do return type Binding:

int x; float y;
std::tie( x, y ) = f();

Structured Bindings also works with arrays, and can be used to create variable aliases as well.

auto f(){
    int a{10};
    float b{3.2f};

    return std::make_tuple(a,b);
}

auto f2( const int& i ){
    return std::make_tuple(std::ref(i));
}

auto [x, y] = f();

auto [r] = f2(x);

++x;

x = r + 1;
//r is a reference to x, x = 12

This does not mean that by-reference should never be used – it should be where appropriate. What it means is that if a variable is just a return argument, and not also a function argument, then maybe it should be passed out via a Structured Binding and not passed in by reference.

Asymptotic Movement

I discovered a GDC talk on a topic called: Asymptotic Averaging. This is sort of a form of interpolation, and can be used to eliminate jerky motions – in particular, they focus on camera movement. Naturally, when you want to change a camera’s direction, you just set the direction. However, if you are doing this frequently or if the distance is large, it results in a jerk or jump which can be disorienting to some extent. This is demonstrated easily by having a camera follow a player character.

Here’s a video of a jerky camera from my current project:

Asymptotic means always approaching a destination but never reaching it. In math it occurs as a curve approaches an asymptote but never actually reaches it. Besides being easy to implement, we get a curve to our movement as well.

The easy to implement concept is from the following form:

//changeMultiplier = 0.01f;

void translate( const Vector3& destination ){
	offsetPosition = destination - camera->currentPosition; 
}

void reduce(Vector3& position) {

	position = offsetPosition;

	position *= changeMultiplier;

	offsetPosition *= (1 - changeMultiplier);
}

void update(){
	Vector3 position;
	reduce(position)
	camera->translate( position )
}

In this variation we are working with a translation. What happens is offsetPosition is set to the translation offset (dest-current). Then each update (frame) we call reduce, which returns a smaller amount of the translation to perform each time. This translation continues to shrink and approaches zero.

Using variations of this, it is easy to produce Trailing and Leading Asymptotic Movement.


Here’s a vid of Trailing Camera Rotation + Trailing Camera Movement:

Here’s a vid of Leading Camera Rotation + Trailing Camera Movement:

Fold Expressions and Lamda Overloads

C++17 has a powerful template feature – Fold Expressions – that combines with a C++11 feature – Parameter Pack. This allows us to do away with C-style variable arguments (variadic functions). I introduce Lamda Overloading as well, to work with these two concepts in a wonderful way.

First off, the Parameter Pack. This is a template feature that allows a variable amount of parameters to be passed to the template. Here’s a sum function declaration:

template<typename ...Args>
auto sum(Args... args);

In the template area, there are a variable amount of parameters, called Args. In the function area, the arguments accepted are the various parameters, Args. Each element in that pack of parameters will be referred to as, args, inside the function body.

Now to add folding.

template<typename ...Args>
auto sum(Args... args) {

    return (args + ...);
}

A fold is contained within parenthesis. Sequentially, left most parameters to right most are included, each called args. Next, each specific parameter, each args, is combined with some operator, in this case, addition. Finally, ellipses signals repeat for each arg.

int i = sum(0,3,5);

Expands to the following, at compile-time:

return (0 + 3 + 5);

So what we’re going to do is write a more modern printf sort of function without using a c variadic function. There are a lot of ways to do this, but because I love lamdas, I want to include lamdas as well. Here we go:

So, in order to vary the generation of strings based on the type of the input parameter, at compile-time, we need to use additional templates. The first thing necessary was an ability to do function overloading with lamdas, hence the LamdaOverload class. This class basically incorporates a bunch of lamdas under one name and overloads the execution operator for each one.

template<typename... Bases>
struct LambdaOverload : public Bases... {
    using Bases::operator()...;

    LambdaOverload(const Bases&... bases) : Bases(bases)... { }
};

template<typename ...Args>
void new_printf(Args... args) {

    auto deduce = LambdaOverload(
        [](const char* c) -> std::string { return c; },
        [](const std::string& str)->const std::string& { return str; },
        [](const auto& t) { return std::to_string(t); }
    );
    std::string o = (( deduce(args) + " " ) + ...);
    OutputDebugStringA(o.c_str());
    std::cout << o  << std::endl;
}

Next, inside the new_printf function, we define the deduce LamdaOverload object. This object is used to define routes from data types to code-calling. There are different types accepted, and one is a catch-all type which is going to be used for natives that work with the std::to_string function. Note that if the catch-all doesn’t work with std::to_string, an error will be generated.

You can add more overloads to LamdaOverload to accept custom types as well. For instance,

 [](const MyClass& c) ->std::string { return c.toString(); }

The call ends up looking like so:

new_printf("the value of x: ", int(x), std::string("and"), 0.1f );

Division By Zero Exploration

This post isn’t so much about programming as much about an earlier post about Division By Zero. So, I thought, if the reciprocal operation to 1 / 0, 0 / 1, is valid, then maybe division by zero can be postponed and considered later. Similar to the imaginary concept, i or sqrt(-1).

Using just fraction concepts I managed to accomplish a lot of progress and, not being a mathematician, I don’t know how much merit there is to it, but here we go.

First, the assumptions:

0 * 1 = \frac{0}{1}* \frac{1}{1} = \frac{0}{1} = 0

0 / 1 = \frac{0}{1} * \frac{1}{1} = \frac{0}{1} = 0

1 / 0 = \frac{1}{1} * \frac{1}{0} = \frac{1}{0} = Q

Q * Q = \frac{1}{0} * \frac{1}{0} = \frac{1}{0} = Q

Q / Q = \frac{1}{0} * \frac{0}{1} = \frac{0}{0}

0 / 0 = \frac{0}{1} * \frac{1}{0} = 0 * Q

0 * Q = 1 \text{ : } Q = \frac{1}{0}

1 / Q = \frac{1}{1} * \frac{0}{1} = \frac{0}{1} = 0

Q * 1 = \frac{1}{0} * \frac{1}{1} = \frac{1}{0} = Q

Q * x = \frac{1}{0} * \frac{x}{1} = Q * x

Q / x = \frac{1}{0} * \frac{1}{x} = \frac{1}{0} = Q

1 / 0 = Q

0 / 0 = 1

OK, so now something that I found immediately useful to do with Q and seems to make sense, furthermore this is what lead me to think up Q.

y= \frac{1}{x} \text{ ; x = 0 } \\\\Q = \frac{1}{x}\\\\\frac{1}{Q} = x \\\\ 0 = x

Consider the following:

\frac{0}{x} = 0 * x

True, 0 = 0; now considering the reciprocal:

\frac{x}{0} = x * Q

how does this resolve?

x = x * Q * 0 \\\\x = x

What this leads to is another assumption: algebraically, the same way Q can be considered for later, so then, can Zero be considered later.

Consider:

a = b\\\\a^2 = ba\\\\a^2-b^2 = ba-b^2\\\\(a-b)(a+b)=b(a-b)\\\\\frac{(a-b)}{(a-b)}(a+b)=b\frac{(a-b)}{(a-b)}\\\\(a+b)=b\\\\(\frac{a}{a}+\frac{b}{a})a=b\\\\0*(\frac{a}{a}+\frac{b}{a})a=0*b

distribute zero once and then multiply both sides by Q (divide by zero )

Q*0*a=Q*0*b\\\\a = b

Now – I realize – if I had distributed the zero to the a instead, the answer would have come out incorrect.

Once it is possible to divide by zero and turn a zero into a one, it seems like it’s possible to produce lots of wrong answers, but some correct ones as well, it seems like it depends on trial and error, perhaps, or additional processing of some sort.

So, I’ve been messing around with this concept using my limited math skills, and it seems like sometimes Q works, and sometimes, it doesn’t work. It’s like sometimes there’s a good solution and an infinity of wrong ones, or the opposite, or some mix of random, maybe. I just don’t know. At the end of the day, sometimes it works, so maybe there is merit.

What I’ve concluded is that with this concept, a consequence, is that it’s possible to basically make anything equal anything, and that seems like an odd thing to consider.

Comments are appreciated.

Super Simple Lambda Thread Pool

I wanted to quickly create a thread pooler that could be passed lamdas. Turns out it’s super simple. Here are the main things I wanted to accomplish:

  • pass lamdas as tasks to be executed asap.
  • be able to wait for all tasks to execute
  • create a bunch of threads at initialization and them have then wait around for tasks, since it can be time-costly to create a new thread on the fly.
  • be able to create as many poolers as I want for different uses.

The first thing I needed to be able to do was store a bunch of lamdas in a single vector, and any sort of lamda. I decided the easiest way to do this was to use lamda members for the task parameters. This is what add task call could look like:

int x, y,z;

mThreadPool.addTask([x,y,z](){
    //task on x, y, z ...
});

The task should execute on another thread as soon as possible.

So in a loop, I’d call, mThreadPool.addTask a bunch of times, and then I’d have to wait on them like so:

mThreadPool.wait();

To store the task lamdas inside the ThreadPool, I used an std::function:

typedef std::function<void()> Task;
std::vector< Task  > mTasks;
std::mutex mTaskMutex;

This works because the task receives its parameters from the lamda itself, not from the lamda call operator. There has to be a mutex because multiple threads are going to query the mTasks vector.

I wanted to create the entire thread pool at one time, so there are initialize and shutdown functions:

std::vector<std::thread> mThreads;
std::atomic<bool> mWorking{ false };

void initialize() {

	if( mWorking ) EXCEPT << "ThreadPool Already Running!";

	mThreads.resize(std::thread::hardware_concurrency());

	//create the threads
	mWorking = true;
	for (auto& thread : mThreads) {
		thread = std::thread([&]() { threadWork(); });
	}
}
void stop(){
	mWorking=false;
}
void shutdown() {
	stop();
	for (auto& thread : mThreads) {
		if (thread.joinable()) thread.join();
	}
}

The threadWork function is where threads wait for tasks to perform and execute them as they become available:

std::atomic<bool> mWorking{ false };
std::atomic<int> mTasksExecutingCount{ 0 };

void threadWork() {

	Task task = nullptr;

	while (mWorking) {

		//get a task
		{
			std::scoped_lock lock(mTaskMutex);

			if (mTasks.size()) {
				++mTasksExecutingCount;
				task = std::move(mTasks.back());
				mTasks.pop_back();
			}
		}

		if (task) {
			task();
			--mTasksExecutingCount;
			task = nullptr;
		}
		else
			std::this_thread::yield();
	}
}

The number of tasks currently executing is kept track of with mTasksExecutingCount. When the ThreadPool is waiting, it waits until the count is zero and the mTasks.size is zero.

class ThreadPool {

	std::vector<std::thread> mThreads;

	typedef std::function<void()> Task;
	std::vector< Task  > mTasks;
	std::mutex mTaskMutex;

	std::atomic<bool> mWorking{ false };
	std::atomic<int> mTasksExecutingCount{ 0 };

	void threadWork() {

		Task task = nullptr;

		while (mWorking) {

			//get a task
			{
				std::scoped_lock lock(mTaskMutex);

				if (mTasks.size()) {
					++mTasksExecutingCount;
					task = std::move(mTasks.back());
					mTasks.pop_back();
				}
			}

			if (task) {
				task();
				--mTasksExecutingCount;
				task = nullptr;
			}
			else
				std::this_thread::yield();
		}
	}

public:

	void initialize() {

		if (mWorking || mTasksExecutingCount != 0) EXCEPT << "ThreadPool Already Running!";

		mThreads.resize(std::thread::hardware_concurrency());

		//create the threads
		mWorking = true;
		for (auto& thread : mThreads) {
			thread = std::thread([&]() { threadWork(); });
		}
	}

	void addTask(Task task) {
		std::scoped_lock lock(mTaskMutex);
		mTasks.push_back(task);
	}
	void wait() {
		do {
			{
				std::scoped_lock lock(mTaskMutex);
				if (mTasks.size() == 0 && mTasksExecutingCount == 0) break;
			}

			std::this_thread::yield();
		} while (mWorking);
	}
	void stop() {
		mWorking = false;
	}
	void shutdown() {
		stop();
		for (auto& thread : mThreads) {
			if (thread.joinable()) thread.join();
		}
	}
};

Division By Zero

So we all know that dividing by zero is an error in programming, but do you know why?

It turns out that it not really an error as much as a math problem, and not so much a problem as much as a thing. Here’s how it goes according to my understanding:

  • A function y= 1 * x has a domain of all real numbers
  • A function y= 1 / x has a domain of x != 0, zero is an asymptote 

If you look at the graph of the function, y = 1 / x, and you think about it, 1 / x approaches infinity as x moves toward zero, but it never actually reaches zero. Obviously we can conceive to write, y = 1 / 0, but as an actual operation, it doesn’t make sense as 0 is outside the domain of 1 / x. Why it is outside the domain of x, I’m not entirely sure, but it is, and that’s how it is. At the end of the day, it seems that x=0 and y= 1 / x each draw a separate, non-intersecting line.

y = 1 / x and x = 0

https://www.desmos.com/calculator/2bw3hvkpap

If the C++ compiler detects that you’ve done a/ 0, it will consider it undefined behavior, and make the entire thing a no-op. If it makes it a no-op, it may not tell you, and this could be a source of a bug. If you encounter a/0 during run-time, you may get some sort of crash or exception.

With the way the compiler can optimize, it can reorder some source operations to affect performance. It’s possible for the compiler to place a divide by zero error before a statement you need or would expect to be executed. You would experience undefined behavior ( x / 0 ) at a place that is not indicated via source code, and that’s bad.

Avoiding this behavior requires a check to detect division by zero before the function is attempted.

C++ lambda and how the world is different now

So, there was the world of C++ before the lambda. And now there’s the world of C++ since the lambda – and I’ve got to say, if you aren’t using lamdas, then your’e probably still in the C++ stone age. This post is going to be all about lamdas and how rad they are. Do Java or .Net have anything as cool? Nope. C++ is where it’s at, and you need to be in the Know.

First off, C++ always had some sort of lamda ability, with functors, and indeed modern lamdas are just functor sugar. But, it’s really nice sugar and the style has gone way past mere ancient functors.

So this is an example functor:

class Functor {

    int a{ 0 };
public:

    int operator()() {
        return ++a;
    }
};

int main()
{
    Functor f;

    f();
    f();
    f();

    std::cout << "value of f: " << f() << std::endl;
    //writes 4
}

An object that has the call operator – can be perceived as a function. What may not be apparent though, is that it can have member variables and its own instance. The instance could be ephemeral or it could persist for a long time.

Functors ended up being needed to write a lot of algorithms in the STL, if not most of them, and they created a lot of slop.

int main()
{
    std::vector<int> v{ 9,0,1,4,1,9,6,4,1 };

    class MyCustomSorter{
    public:
        bool operator()(const int& a, const int& b) {
            return a < b;
        }
    };

    std::sort(v.begin(), v.end(), MyCustomSorter());
}

So now with lamdas we can do it like so:

 std::sort(v.begin(), v.end(), [](const int& a, const int& b)->bool {
     return a < b;
 });

What’s really cool about this line, is that we have actually declared a class-like object inline a function call, but with a lot less code. And what’s cool is that there are three different areas to pass arguments, the first area the example uses is the lambda parameters area, or the parameters to the call operator.

operator()(const int& a, const int& b) == (const int& a, const int& b)

The second area it uses is the return type

bool == -> bool

The third area is to pass in members of the lambda. Stuff that persists until the lamda goes out of scope. We could do the following:

std::vector<int> v{ 9,0,1,4,1,9,6,4,1 };

std::sort(v.begin(), v.end(), [&](const int& a, const int& b)->bool {
    return a < b;
});

Amperstand, &, in this case, causes anything from the superscope to be passed by reference. So given that, we could have a lamda body as follows:

std::vector<int> v{ 9,0,1,4,1,9,6,4,1 };

ObjectA obja;
 
std::sort(v.begin(), v.end(), [&](const int& a, const int& b)->bool {

    if (obja.method())
        return false;

    return a < b;
});

What happens in this case, is that the lamda is compiled with an ObjectA& member, which is set at the lamda creation and remains until it goes out of scope. The scope of these variables can sometimes be tricky as it is possible for a lamda to retain a reference to an out of scope variable (crash).

What we have been given the ability to do here is pretty incredible, we can define objects inline, anywhere, as lamdas. Here’s a threading example, with a lamda inside a lamda:

int result=-1;
		
std::thread threadA([&result]() {
	bool working=true;
	int i = 0;

	auto doWork = [&]()->int {
		while (working) {
			if (++i > 10) working = false;
		}
		return i;
	};

	result = doWork();

});

threadA.join();

So we can define functions inside of functions, which are basically private function methods, and that’s actually really useful. Really, this is an entirely new C++ paradigm. Consider the following as well:

	x++;
	y++;
	z++;
	f1(x, y, z);

	x++;
	y++;
	z++;
	f1(x, y, z);
	f2(x, y, z);

	x++;
	y++;
	z++;
	f1(x, y, z);

Could be improved like so:

	auto f[&]() {
		x++;
		y++;
		z++;
		f1(x, y, z);
	}

	f();

	f();
	f2(x, y, z);

	f();

This is cool because on the fly, I can create a function, that is specific to my current function, will never be used outside of it – absolutely no reason to pollute the external namespace. Yet I can clean up code and make it clearer. Comment what a block of code is doing? How about put that specific block of code in a lamda, and suddenly everything is clearer – the block of code has a name that isn’t a mere comment ( and the external namespace is pristine)! And you get the cool benefit of being able to call it multiple times or comment its call out ( no reason to have huge swaths of commented-out code).

In the vein of private function methods, if-statements can sometimes gain a lot from them; consider the following:

for (auto& user : mUsers) {

	auto isUserReady = [](auto& user)->bool {
		//...//
		return true;
	};

	if (isUserReady(user)) {
		//...//
	}
}

Again, we’re saving the external namespace so much pollution! Generally when working like this, I like to declare the lamda about right before its going to be used, or near to it.

The only downside to this concept is potential code duplication – has this method already been written somewhere else? Could be hard to know all the time without getting a namespace exposure.

What’s become apparent is that we can pass lamdas as parameters to functions, this is hugely powerful as we can write our own functions that accept lamdas.

Want the user to be able to essentially modify your function but without overriding it? Lamda to the rescue.

	template<typename Action>
	void clean(const Action& deleteAction) {
		for (auto& item : mItems) {
			deleteAction(item);
		}
		mItems.clear();
	}

The entire STL tends to work like this.

In C++ 20, lamdas are going to be further improved. From my current project, here is an example of a templated-lamda from C++ 20 preview.

    auto createHlms = [&]<typename T>(T* &hlms) {
        //...//
        T::getDefaultPaths(mainPath, libraryPaths);
        //...///
        hlms = OGRE_NEW T(archive, &archives);
        //...///
    };

    Ogre::HlmsUnlit* hlmsUnlit = nullptr;
    createHlms(hlmsUnlit);

    Ogre::HlmsPbs* hlmsPbs = nullptr;
    createHlms(hlmsPbs);

Staggering Vector

So in my current game project, I want to have a some what real-time simulation in which stuff happens. This boils down to doing stuff periodically in a game loop. In my game, the game loop is running at 30 frames per second. I was having problems maintaining this rate, and I did not want to multi-threading just yet. The solution turns out to be something popular in game programming, called time-slicing. The idea is that instead of doing all work at once, each frame, do it gradually over multiple frames. Right now I’m applying the concept to garbage collection, AI, and a problem I’m calling, “Unit Vision”.

In my game, I’ve got a bunch of Units which need to do stuff such as walk around, attack, and spot each other. Before I implemented time-slicing, part of the game loop looked something like this:

for( auto& unit : mUnits ){
	unit.act();
}

removeDeadUnits();

In testing with like 2000+ units, this ended up taking more time than I’d like, so onto time-slicing.

The idea is pretty simple, split the vector into multiple segments and only iterate over one segment per frame. This sounded like “staggering” to me, so I wrote up what I called a “Stagger Group” or “Stagger Vector”

This is what the object’s data looks like:

template<typename T, typename Data = int>
class StaggerGroup {
public:
	struct Group {
		Data data{ 0 };
		std::vector<T> group;
	};
private:
	std::vector< Group> mGroups;

	std::size_t mGroupIndex{ 0 };

I found that a lot of times I wanted to associate something like a timer with a given vector, so I decided to go generic and stuck in a Data parameter with each vector. Then there are the actual set of groups, and an index to the active group.

The set of methods is pretty small a the moment:

	void setup(std::size_t numGroups, std::size_t poolSize = 500) {
		mGroups.resize(numGroups);

		for (auto& g : mGroups) {
			g.group.reserve(poolSize);
		}
	}

	void place(const T& value) {

		std::vector<T>* selectedGroup = &mGroups[0].group;
		for (auto& g : mGroups) {
			if (g.group.size() < selectedGroup->size()) {
				selectedGroup = &g.group;
			}
		}
		selectedGroup->push_back(value);
	}

	template<typename Action>
	void forEach(Action action) {
		for (auto& g : mGroups) {
			action(g);
		}
	}

	Group* getNextGroup() {
		if (mGroupIndex >= mGroups.size()) mGroupIndex = 0;
		return &mGroups[mGroupIndex++];
	}

	std::size_t getSize() {
		std::size_t size = 0;
		for (const auto& g : mGroups) {
			size += g.group.size();
		}
		return size;
	}

So setup is called and a group of fixed size is created, place finds the lowest vector and places the new item in it. The real magic here is the getNextGroup method, here’s what it looks like in practice – the following code is called each game loop iteration:

		mUnitGroups.forEach([timeSinceLastFrameInMS](auto& group) {

			group.data.elapsedTime += timeSinceLastFrameInMS;
		});

		auto selectedGroup = mUnitGroups.getNextGroup();

		float elapsedTime = selectedGroup->data.elapsedTime;
		selectedGroup->data.elapsedTime = 0.0f;

		for (auto& unit : selectedGroup->group) {

			if (unit->isDead()) continue;

			unit->act(elapsedTime );
		}

I also use Staggering for garbage collection like so:

		auto cleanGroup = mPendingCleanGroups.getNextGroup();
		for (auto& item: cleanGroup->group) {
			deleteAction(item);
		}
		cleanGroup->group.clear();

The garbage collection is actually done in another type of object, Recycling Vector, which I’m going to post about next.


SOA sorting

So in my current project, I’m using a library which uses a programming methodology called, SOA, or structure of arrays, which is different than OOP ( object-oriented-programming). Basically, in SOA, instead of having an object with 10 properties, and a vector of those objects, you have a structure with 10 vectors each containing one property.

For example, in OOP, we’d have the following:

class Object{
	vec3 position;
	vec3 normal;
	bool b;
};

std::vector<Object> objects;

But in SOA, we might have this instead:

class SOAObject{
	std::vector< vec3 > positions;
	std::vector< vec3 > normals;
	std::vector< bool > bs;
};
SOAObject soaObject;

All of the vectors are the same length. Why you may want to program like this I am going to leave to another post, what I want to describe here is how you’d go about sorting the SOAObject structure.

To sort the objects vector, that’s straight forward:

std::sort( objects.begin(), objects.end(),[&](const auto& a, const auto& b) -> bool{
	return a.position.length() < b.position.length();
});

To use the magical std::sort on the SOA, however, it’s not so straight forward. And in my case, the SOA vectors are not std::vectors, so that’s even more of a pain.

So this is how to do it:

//can be pre-generated
std::vector< std::size_t > indexes(soaObject.positions.size());
std::iota(indexes.begin(), indexes.end(), 0);

//actual sorting
std::sort( indexes.begin(), indexes.end(),[&](const auto& a, const auto& b) -> bool{
	return soaObject.positions[a].length() < soaObject.positions[b].length();
});

//indexes is now contains sorted indexes, such that the sorted sequence is accessed as follows:
for( auto i: indexes){
	std::cout << soaObject.positions[i] << ", " << soaObject.normals[i] << ", " << soaObject.bs[i] << "; ";
}

The std::iota function fills the range with a sequence of increasing numbers starting at 0. This specifies all vector indexes as they exist by default. Then, sorting the index array based on whatever SOA comparison, we leave the originial SOA arrays unchanged, but still get a sorted set of indexes. This allow us to access the array in a sorted manner.

This approach can also be useful to normal OOP where you may not actually want to sort ( copy/move), elements of the vector.