ZScript: Why is casting a readonly pointer not allowed?
Posted: Tue Nov 13, 2018 1:09 am
I'm not sure if this is a bug, an unimplemented feature, or an intentional restriction, so I thought I'd ask here first. Please read the full post before answering, the real question is closer to the bottom. Sorry the post turned out to be that long
Example problem: I have a class name which I know is of type Actor, but I don't know anything else about it. I need to check if this class is an Inventory, and if it is, then print its default MaxAmount.
Seems easy, right? Let's use GetDefaultByType for this:
Now let's run it and...
Oh noes! What has gone wrong?
Actually, this is completely understandable, so this is NOT the question - please read on. This error has appeared because the value GetDefaultByType returns is not an Actor but a readonly<Actor> - something akin to a const AActor* in C++ code. This is because we are not supposed to modify the instance GetDefaultByType has returned. We can only read its fields, and we can call its functions only if they are marked const. We can't write a new value to any field or call any non-const function. While I think it would be a useful feature to be able to change actors' defaults sometimes, that is a whole another story and thus also not the point of this thread.
Okay, so we can't cast the result to Inventory because it would violate const-correctness since Inventory is not readonly, and if we were allowed to do such a cast, then we could modify the instance. All right, let's try to cast the result to readonly<Inventory> then!
Aaand...
Well, now THIS is weird. Why not allow casting a readonly to another readonly? Const-correctness is almost certainly not violated in this case, as the resulting value would have the same restrictions on it. Okay, maybe we could at least check if the result is an Inventory with the is operator? (NB: It seems that this operator checks for exact type without accounting for inheritance, so it wouldn't suit our needs in this problem but I still want to see if it works)
Let's see...
Nope. We can't. Even though we don't even get a new value from this operator, so there isn't even a theoretical possibility to violate const-correctness in this case. The error message also doesn't seem to make much sense. "Pointer<Class<Object>>"? Why is that there?
Okay, well, I thought, there has to be some way to achieve what I want! And indeed there is. It turns out that I need to cast not the instance that is returned by GetDefaultByType but the class name. Then check if the resulting class is not null. The correct code looks like this:
Yes, this method does sound logical. However, I would really like to know why it is not possible to cast a readonly<Actor> to a readonly<Inventory> or use the is operator on it. It doesn't look like it would make the world explode if it were allowed. Is it simply something that was never implemented because it was never considered, or is there a logical reason behind it?
Example problem: I have a class name which I know is of type Actor, but I don't know anything else about it. I need to check if this class is an Inventory, and if it is, then print its default MaxAmount.
Seems easy, right? Let's use GetDefaultByType for this:
Code: Select all
class Test
{
static void PrintMaxAmountIfInventory(name className)
{
let defaultInstance = GetDefaultByType((class<Actor>)(className));
let inventory = Inventory(defaultInstance);
if (inventory)
{
Console.Printf("Max amount of %s: %d", className, inventory.MaxAmount);
}
else
{
Console.Printf("%s is not of type Inventory.", className);
}
}
}
Code: Select all
Script error, "zscript.txt:ZSCRIPT" line 6:
Cannot cast a readonly pointer
Actually, this is completely understandable, so this is NOT the question - please read on. This error has appeared because the value GetDefaultByType returns is not an Actor but a readonly<Actor> - something akin to a const AActor* in C++ code. This is because we are not supposed to modify the instance GetDefaultByType has returned. We can only read its fields, and we can call its functions only if they are marked const. We can't write a new value to any field or call any non-const function. While I think it would be a useful feature to be able to change actors' defaults sometimes, that is a whole another story and thus also not the point of this thread.
Okay, so we can't cast the result to Inventory because it would violate const-correctness since Inventory is not readonly, and if we were allowed to do such a cast, then we could modify the instance. All right, let's try to cast the result to readonly<Inventory> then!
Code: Select all
... class and function declarations omitted
let defaultInstance = GetDefaultByType((class<Actor>)(className));
let inventory = (readonly<Inventory>)(defaultInstance);
if (inventory)
{
... rest omitted
Code: Select all
Script error, "zscript.txt:ZSCRIPT" line 6:
Unexpected 'readonly'
Expecting '-' or '+' or '++' or '--' or '(' or 'class' or identifier or string constant or 'super' or '~' or '!' or 'sizeof' or 'alignof' or integer constant or unsigned constant or float constant or name constant or 'false' or 'true' or 'null'
Code: Select all
... class and function declarations omitted
let defaultInstance = GetDefaultByType((class<Actor>)(className));
if (defaultInstance is "Inventory")
{
... rest omitted
Code: Select all
Script error, "zscript.txt:ZSCRIPT" line 7:
Cannot convert Pointer<Class<Actor>readonly > to Pointer<Class<Object>>
Okay, well, I thought, there has to be some way to achieve what I want! And indeed there is. It turns out that I need to cast not the instance that is returned by GetDefaultByType but the class name. Then check if the resulting class is not null. The correct code looks like this:
Code: Select all
class Test
{
static void PrintMaxAmountIfInventory(name className)
{
let inventoryClass = (class<Inventory>)(className);
if (inventoryClass)
{
let inventory = GetDefaultByType(inventoryClass);
Console.Printf("Max amount of %s: %d", className, inventory.MaxAmount);
}
else
{
Console.Printf("Class %s is not of type Inventory.", className);
}
}
}