I don’t post enough about the work I’ve been doing on Shibboleth. I don’t have a lot of visual progress to show, but I am making a lot of very good progress. And I should get into the habit of writing down the work I’m doing.

Reflection Unification

In the past, enum reflection had it’s own global data structure that was different from everything else. Everything else has a Reflection <T> structure, whereas enums had a EnumReflection<T> structure. I’ve deleted EnumReflection<T>. Now everything goes through Reflection<T>. Enums still use EnumReflectionDefinition<T> to define themselves and all other data types still use ReflectionDefinition<T> as usual.

In the process of merging these two data structures, I learned that if constexpr doesn’t work exactly as I expected. While the code in a if constexpr will get compiled out, it still gets evaluated by the compiler. To highlight what I mean, let’s look at some code I had to write to get my unification changes to compile.

// If T in Reflection<T> is an enum, this is the function that is defined.
// This is a static function.
template <class T>
const Gaff::IEnumReflectionDefinition& Reflection<T>::GetReflectionDefinition(void)
{
	/* body */
}

// If T in Reflection<T> is NOT an enum, this is the function that is defined.
// This is a static function.
template <class T>
const Gaff::IReflectionDefinition& Reflection<T>::GetReflectionDefinition(void)
{
	/* body */
}

// These functions are overrides for IReflection.
template <class T>
const Gaff::IEnumReflectionDefinition& Reflection<T>::getEnumReflectionDefinition(void) const override
{
	if constexpr (std::is_enum<type>::value) {
		// To calm the compiler, even though this should be compiled out ...
		return reinterpret_cast<const Gaff::IEnumReflectionDefinition&>(GetReflectionDefinition());
	} else {
		// Call base version which does an assert and returns a null reference.
		return IReflection::getEnumReflectionDefinition();
	}
}

template <class T>
const Gaff::IReflectionDefinition& Reflection<T>::getReflectionDefinition(void) const override
{
	if constexpr (std::is_enum<type>::value) {
		// Call base version which does an assert and returns a null reference.
		return IReflection::getReflectionDefinition();
	} else {
		// To calm the compiler, even though this should be compiled out ...
		return reinterpret_cast<const Gaff::IReflectionDefinition&>(GetReflectionDefinition());
	}
}

Even though if constexpr is compiling out the body of the failed conditional, I still got compiler errors stating that it can’t convert const Gaff::IReflectionDefinition& to const Gaff::IEnumReflectionDefinition& and vice versa. (compiler is VS2017 v19.16.27032.1) To get around this compilation error, I reinterpret_cast the references. I would naturally not expect to have to do this, as the failed branches should be removed at compile time and not need to be evaluated. It’s a simple fix, but when you have stuff in macros, it’s hard to debug. So figuring out why this was happening was very time consuming.

Integrating Graphics

I’ve begun work on integrating my graphics library, Gleam, into the engine. My first steps are making basic graphics resources that are loaded through the resource module. The current resources I’ve implemented are BufferResource, MeshResource, and ModelResource. I’m currently working on ShaderResource. In the process of doing this, I’ve noticed how past me grossly mis-implemented layouts and where they should live. I’m currently refactoring them so that they’re much easier to use and are automatically generated.

Shifted Focus and Editor Change

Basically, I was wasting too much time making editor stuff and fighting with wxWidgets. I’ve opted to put building the editor on hold and focus more on the runtime portion of the engine. Given my troubles doing things with wxWidgets, I’m also ditching wxWidgets. I’m instead opting to build the editor in C# using Avalonia and Dock. I’ve chosen these libraries because I still want to keep the codebase as multiplatform friendly as I can. One thing I absolutely hate is C++/CLI. Every codebase I’ve worked with that has used managed-C++ has been a total nightmare. I hate managed-C++, C++/CLI, whatever you want to call it and will avoid it at all costs. I still expect I will have to write a layer so that the C# can communicate with the engine. To achieve this, I will write a normal, unmanaged DLL with a plain old C-style interface.

Things I Wish C++ Had

When I encounter things that would be simpler with a language feature, I’ll put in a section with this label.

Mixins

And to clarify, inheritance is NOT a form of mixin. If you search C++ mixins on Google (or whatever search engine you prefer), you’ll get lots of results of people just doing inheritance. I want honest to God mixins. Something that looks like this.

// I don't care what form the keywords take or where they're placed, but basically stating that this class is to only be used for mixins and cannot be instantiated directly.
// It should also not be an actual type, so you can't return a `Stuff*` or `Stuff&`.
// I'm not overly familiar with C++20 constraints and concepts, but I'd imagine you could be able to use it for forming contracts with template functions.
// Can make template mixins too!
class Stuff mixin
{
public:
	int someA = 10;

	// Could even put the definition into a *.cpp file somewhere instead of implementing in a header.
	void printA(void) const { printf("someA: %i\n", someA); }

private:
	// More stuff

// And all the other usual class stuff you're used to.
};

class MyClass final
{
	mixin Stuff;

public:
	int someB = 20;

	void printB(void) const { printf("someB: %i\n", someB); }
};

int main(void)
{
	MyClass foo;

	foo.printA();
	foo.printB();

	foo.someA = 100;
	foo.someB = 200;

	foo.printA();
	foo.printB();

	return 0;
}

The result of this code would be:

someA: 10
someB: 20
someA: 100
someB: 200

How is this different from inheritance? Well, I’m not bloating the class with an inheritance hierarchy and all the baggage that comes with it. Basically, all the declarations and definitions of the mixin class Stuff is copy-pasted into MyClass. As far as the compiler is concerned, it’s like you literally wrote the same declarations/definitions into MyClass. “But you can do this with macros!” you exclaim. Yes, but I’d prefer to not do the programming equivalent of stabbing myself. Macros are completely undebuggable and their syntax is not clean. At least with this, I can write the Stuff class just like how I write every other class, and I only have to add one line of code to pull all of that into any of my other classes.

Why do I want this feature? Because my reflection system relies upon what are essentially mixins. And right now, I achieve this with, you guessed it, macros. It sucks, but I have very few other options. I could clean this up drastically with mixins and it would become a lot more readable.

Until Next Time

Thanks for reading! I’ll try and make more posts like this one and do them more frequently!