Bash++ v0.7 introduces some expanded usage of RTTI (Run-Time Type Information) in the language. For some time, type information about objects has been stored at runtime (this is necessary, for example, for virtual methods) but mostly inaccessible to the programmer. From the programmer’s perspective, the only direct window into this information has been the @dynamic_cast operator:

@Object* obj=@dynamic_cast<Object> &@other_obj

And we could then compare @obj to @nullptr to see if the cast was successful. This tells you whether @other_obj’s type is either Object or some derived type along the inheritance chain. This is useful, but limited.

@typeof

Bash++ v0.7 introduces the @typeof operator, which allows you to query the exact type of an object at runtime:

if [[ @typeof &@obj == "MyClass" ]]; then
	echo "obj is exactly of type MyClass"
fi

The @typeof operator expects a pointer to an object, and its output is the name of the class as a string. This allows you to perform exact type comparisons, which can be useful in certain scenarios.

Runtime-Inferred Dynamic Cast Targets

This change, to me, is more interesting.

Up until v0.7, the @dynamic_cast operator has only accepted a class name as its target type. This meant that the target type had to be known at compile time.

With v0.7, the @dynamic_cast operator can now accept a shell variable or object reference which is expected to contain a class name at runtime:

cast_to_class="MyClass"
if [[ @dynamic_cast<$cast_to_class> &@obj != @nullptr ]]; then
	echo "obj is of type ${cast_to_class} or derived from it"
fi

In these cases, the compiler simply accepts the reality that it cannot verify the correctness of the target type at compile time.

Previously, if an invalid class name was given to @dynamic_cast, the compiler would emit an error and halt compilation. This behavior has been changed – the compiler will now emit a warning instead. In the case of a runtime-inferred target type, the compiler cannot verify the correctness of the target type, so it will not emit any warning.

This opens up a lot of new possibilities for us. Immediately with this change, we’re able to introduce Typed containers to the standard library (TypedArray, TypedStack, TypedQueue, etc, as well as Typed versions of all the Shared containers for multi-process concurrency).

The Typed containers work essentially like this:

@class TypedStack : Stack {
	@private type

	# ...

	@public @method push item {
		if [[ @dynamic_cast<@this.type> "$item" == @nullptr ]]; then
			# Error! The given item is not a pointer to an object of the correct type
			# ...
			return 1
		fi
		@super.push "$item"
	}
}

These typed containers use runtime-inferred cast targets to enforce type constraints on the elements they contain. In other words, they will refuse to accept any primitives which are not pointers to objects of the correct type.

If you’re, for example, using a TypedStack of @Object, then it’s completely safe to do something like this:

@Object* top=@stack.top

And use @top as an @Object* without any further casting or verification, because the TypedStack has already guaranteed that everything it contains is an @Object* or derived type.