Godot/Gdscript: Why is str2var not working on classes with _init method? - serialization

Let's define 2 classes
A.gd
class_name A
var v = null
func _init(v_):
v = v_
B.gd
class_name B
var v = null
Now, when I try to use str2var/var2str, this is what I get
var a = A.new("aaa")
var b = B.new()
b.v = "bbb"
printt("var2str(a):", var2str(a))
printt("var2str(b):", var2str(b))
printt ("var2str(str2var(var2str(a))):", var2str(str2var(var2str(a))))
printt ("var2str(str2var(var2str(b))):", var2str(str2var(var2str(b))))
Output:
var2str(a): Object(Reference,"script":Resource( "res://Scripts/AI/A.gd"),"v":"aaa")
var2str(b): Object(Reference,"script":Resource( "res://Scripts/AI/B.gd"),"v":"bbb")
var2str(str2var(var2str(a))): Object(Reference,"script":null)
var2str(str2var(var2str(b))): Object(Reference,"script":Resource( "res://Scripts/AI/B.gd"),"v":"bbb")
Why is str2var(a) not working?
How should I fix it?

The Solution
Fix it by making the parameter optional, for example:
class_name A
var v = null
func _init(v_ = null):
v = v_
With that there is no error. I get this output:
var2str(a): Object(Reference,"script":Resource( "res://A.gd"),"v":"aaa")
var2str(b): Object(Reference,"script":Resource( "res://B.gd"),"v":"bbb")
var2str(str2var(var2str(a))): Object(Reference,"script":Resource( "res://A.gd"),"v":"aaa")
var2str(str2var(var2str(b))): Object(Reference,"script":Resource( "res://B.gd"),"v":"bbb")
The problem
For abstract, str2var will not pass any arguments to _init. It would not know what to pass anyway.
The rest of the answer is the process of confirming that str2var will result in calling _init with no argument.
When I try your code I get this error:
E 0:00:00.630 _create_instance: Condition "r_error.error != Variant::CallError::CALL_OK" is true. Returned: __null
<C++ Source> modules/gdscript/gdscript.cpp:121 # _create_instance()
<Stack Trace> main.gd:12 # _ready()
We can find the line that throws the error in _create_instance by looking at the source.
Sadly that does not give me much information. So, I decided to search how str2var is implemented.
We find it inside GDScriptFunctions::call, here. Which calls VariantParser::parse, which calls VariantParser::parse_value. We are interested in the case of an "Object" (here). And that results in a call to ClassDB::instance(type). There type will be "Reference", and then it procedes to set all properties as they come. Being the first one "script":Resource("res://A.gd").
When we set the script (here), it will result in a call to GDScript::instance_create. Which calls GDScript::_create_instance (here):
return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this) != NULL, unchecked_error)
With no argument for _init (The NULL is the argument array, and 0 is the number of arguments). This is the signature for GDScript::_create_instance:
GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error)
Of course, initializer->call(instance, p_args, p_argcount, r_error); fails, because _init requires an argument. And we find the line that throws the error further down. Note: initializer is created while parsing the script.

Related

CodeBlock throwing IAE because CassName does not pass is TypeName check

I tried to initialize a property, though CodeBlock#of throws an IllegalArgumentException in CodeBlock#argToType
I looked into the root cause of the error which was at CodeBlock#argToType.
Even if o is a ClassName(which also is a TypeName) it does not pass the is TypeName -> o check and throws the IllegalArguementException.
val initString = "mutableMapOf(Pair(%T, %T), Pair(%T, %T))"
val initArgs = arraysOf(...)
CodeBlock.of(initString, initArgs)
I expected the CodeBlock to be built correctly, but instead it throws the IllegalArguementException
I reproduced you problem and was able to fix it; I think the key question is how you pass initArgs to CodeBlock.of: this method is expecting a second varargs parameter but you're passing a single Array<...> value.
Changing you code as follows seems to work:
fun main(args: Array<String>) {
val initString = "mutableMapOf(Pair(%T, %T), Pair(%T, %T))"
val initArgs = arrayOf(String::class.java, String::class.java, String::class.java, String::class.java)
val result = CodeBlock.of(initString, *initArgs)
println("result is $result")
}
The key point is to pass *initArgs, and not initArgs, as second parameter of CodeBlock.of.
I explicitly initialized initArgs' values witch type values, in order to match %T placeholder expectations.
I hope this can help you!

Rust Arc/Mutex Try Macro Borrowed Content

I'm trying to do several operations with a variable that is shared across threads, encapsulated in an Arc<Mutex>. As not all of the operations may be successful, I'm trying to use the try! macro, or the ? operator, to auto-propagate the errors.
Here's a minimum viable example of my code:
lazy_static! {
static ref BIG_NUMBER: Arc<Mutex<Option<u32>>> = Arc::new(Mutex::new(Some(174)));
}
pub fn unpack_big_number() -> Result<u32, Box<Error>> {
let big_number_arc = Arc::clone(&BIG_NUMBER);
let mutex_guard_result = big_number_arc.lock();
let guarded_big_number_option = mutex_guard_result?;
let dereferenced_big_number_option = *guarded_big_number_option;
let big_number = dereferenced_big_number_option.unwrap();
// do some subsequent operations
let result = big_number + 5;
// happy path
Ok(result)
}
You will notice that in the line where I declare guarded_big_number_option, I have a ? at the end. This line is throwing the following compiler error (which it does not when I replace the ? with .unwrap():
error[E0597]: `big_number_arc` does not live long enough
--> src/main.rs:32:30
|
7 | let mutex_guard_result = big_number_arc.lock();
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
17 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
Now the thing is, it is my understanding that I'm not trying to use big_number_arc beyond its lifetime. I'm trying to extract a potential PoisonError contained within the result. How can I properly extract that error and make this propagation work?
Additionally, if it's any help, here's a screenshot of the type annotations that my IDE, CLion, automatically adds to each line:
lock() function returns LockResult<MutexGuard<T>>. Documentation says the following:
Note that the Err variant also carries the associated guard, and it can be acquired through the into_inner method
so you're essentially trying to return a reference to a local variable (wrapped into PoisonError struct), which is obviously incorrect.
How to fix it? You can convert this error to something with no such references, for example to String:
let guarded_big_number_option = mutex_guard_result.map_err(|e| e.to_string())?;

OOP in Lua - Creating a class?

I'm aware there are a few questions about implementing OOP in Lua on this site, however, this one is a bit different (at least compared to what I found).
I'm trying to create a class called "human", and make it so objects created with the "new" constructor of "human", inherit everything inside human except it's constructor. However, I also don't want to be able to use methods inside of human, on human. So whatever's inside the human class, is only passed to created objects. Here's an example:
-- "Human" class
human = {}
function human.new(name)
local new = {} -- New object
-- Metatable associated with the new object
local newMeta =
{
__index = function(t, k)
local v = human[k] -- Get the value from human
print("Key: ", k)
if type(v) == "function" then -- Takes care of methods
return function(_, ...)
return v(new, ...)
end
else
return v -- Otherwise return the value as it is
end
end
}
-- Defaults
new.Name = name
new.Age = 1
return setmetatable(new, newMeta)
end
-- Methods
function human:printName()
print(self.Name)
end
function human:setAge(new)
self.Age = new
end
-- Create new human called "bob"
-- This works as expected
local bob = human.new("Bob")
print(bob.Name) -- prints 'Bob'
bob:printName() -- prints 'Bob'
bob:setAge(10) -- sets the age to 10
print(bob.Age) -- prints '10'
-- But I don't want something like this allowed:
local other = bob.new("Mike") -- I don't want the constructor passed
-- I'd also like to prevent this from being allowed, for "human" is a class, not an object.
human:printName()
So creating the object with human.new("Bob") works fine, but it also passes the constructor, and I can still use the object methods on the class. I'm very new to the concept of OOP, so I'm sorry if this was a horrible question. But if anyone could help, I'd appreciate that.
I have run into the same issue before. You need two tables. One for object methods and one for class methods. Set the metatable of constructed objects to the object method table. For example:
local Class = {}
local Object = {}
Object.__index = Object
function Class.new()
return setmetatable({}, Object)
end
setmetatable(Class, {__call = Class.new})
function Object.do()
...
end
return Class
And use it
Class = require('Class')
local obj = Class.new() -- this is valid
obj.do() -- this is valid
obj.new() -- this is invalid
Class.do() -- this is invalid

Function returns nil when called inside constructor

I just started programming in lua and I created sort of oop structure following this tutorial: http://tylerneylon.com/a/learn-lua/
Problem is, when I created function that returns object or table of objects and call it inside constructor, it returns nil.
Here is my code for first object:
require "ObjectB"
ObjectA = {}
function ObjectA:new(num)
newInstance = {}
newInstance.var = self:foo(num)
self.__index = self
return setmetatable(newInstance, self)
end
function ObjectA:foo(num)
return ObjectB:new(num)
end
, and for second object:
ObjectB = {}
function ObjectB:new(num)
newInstance = {}
newInstance.num = num
self.__index = self
return setmetatable(newInstance, self)
end
When I do this:
myObject = ObjectA:new(5)
print(myObject.var.num)
, I get error: "Error: main.lua:14: attempt to index field 'var' (a nil value)".
But when I do this:
myObject = ObjectA:new(5)
myObject.var = ObjectA:foo(5) //setting var by calling foo outside of constructor
print(myObject.var.num)
, everything seems to work fine and print result is really 5. Can anyone tell me what is reason for this strange behaviour or what am I doing wrong here?
Variables are global by default, so the two variables newInstance in ObjectA:new and ObjectB:new are the same global variables, you assign it a new value, the previous value is gone.
Instead, use local variables like this:
function ObjectA:new(num)
local newInstance = {}
--the rest
end
and
function ObjectB:new(num)
local newInstance = {}
--the rest
end

Lua metatables and metamethod - How to call a different member function

I have the following Class
local PROGRESS = {}
PROGRESS.__index = function(self,key)
if key~="__group" and self.__group[key] then
return self.__group[key]
else
return rawget(self,key)
end
end
What this does is when You access table[key] it performs a lookup in table.__group (which is an object of another class) and returns table.__group[key] ,if it is not nil.
Now I am trying to do the same for member functions.
i.e If I call table:key() a lookup must be performed in table.__group and if the function is present, then table.__group:key() should be called.
How do I accomplish this?
I tried to do this.
local PROGRESS = {}
PROGRESS.__index = function(self,key)
if key~="__group" and self.__group[key] then
local val = self.__group[key]
if type(val) == "function" then
self.__group:val()
return function() end
end
return self.__group[key]
else
return rawget(self,key)
end
end
But there are 2 things wrong here.
I am unable to retrieve the original function's arguments
Event if I just ACCESS table[key].function without calling it, the function will be called
And I've got the feeling that I am trying to complicate things and the solution is way simpler.
Any help is appreciated.
UPDATE
#Mud
The problem with the original code is that the object passed as 'self' to the member function is an object of the new class. Not of the old class.
Consider this code
GROUP_CLASS = {}
GROUP_CLASS.__index = GROUP_CLASS
function GROUP_CLASS:showSum (a,b) print(self);print(a + b) end
group_object = setmetatable({},GROUP_CLASS)
group_object:showSum(1,2)
local PROGRESS_CLASS = {}
PROGRESS_CLASS.__index = function(self,key,value)
if key~="__group" and self.__group[key] then
return self.__group[key]
else
return rawget(self,key)
end
end
progress_object = setmetatable( {__group = group_object} , PROGRESS_CLASS)
progress_object:showSum(3,3)
--progress_object is passed as first argument to showSum. But i need group_object to be passed
In the above code, When progress_object:showSum(3,3) is called,
is it possible to pass group_object (or in other words progress_object.__group) as self instead of progress_object.
Hope that makes sense.
Response to updated post:
progress_object is passed as first argument to showSum. But i need group_object to be passed
If you're going to ignore the state of the object a method is called on, and substitute the state of some other object, why is it even a method on that object? That's like overriding the addition operator to do multiplication, a recipe for confusion.
In other words, you want this:
progress_object:method("foo")
To resolve, via bizarre internal machinery, into this:
group_object:method("foo")
Why not skip a step and just make the latter call?
If you must, you could achieve this by returning a proxy for the method which replaces self with __group
local PROGRESS_CLASS = {}
PROGRESS_CLASS.__index = function(self,key)
local groupval = self.__group[key]
if key == '__group' or not groupval then
return rawget(self,key)
elseif type(groupval) ~= 'function' then
return groupval
else
return function(...)
if self == ... then -- method call
-- replace self argument with __group
return groupval(self.__group,select(2,...))
else
return groupval(...)
end
end
end
end
Response to original post:
How I am trying to do the same for member functions. i.e If I call table:key() a lookup must be performed in table.__group and if the function is present, then table.__group:key() should be called.
How do I accomplish this?
Do nothing. Your original code handles this.
Lua doesn't know what a "member function" is. A member is a member (i.e. an element in a table), and whether the value of that member is a function is irrelevant.
Remember:
obj:method(a,b,c) is exactly equivalent to obj.method(obj,a,b,c)
obj.method is exactly equivalent to obj["method"].
Your code already resolves obj["method"] into obj.__group["method"]
So you're done.
For instance, say we have:
group = {}
function group:showSum (a,b) print(a + b) end
function group:showProduct(a,b) print(a * b) end
Using your first code, we can write:
foo = setmetatable({__group = group}, PROGRESS)
foo:showSum(3,3) -- 6
foo:showProduct(3,3) -- 9
That's it.
Now, as long as we're here, let's look at what your second function is doing:
local val = self.__group[key]
if type(val) == "function" then
self.__group:val()
return function() end
end
First you grab the function value from __group. At this point you're done. Simply return that value, and the caller is going to call that value (i.e. (...)). Instead, you call __group["val"] which is likely a totally different function from __group[key] (unless key=="val"), then you pass the caller a function which does nothing.