I'm using the Android Jetpack Navigation component, and it seems like it's requiring that I pass the arguments in the wrong order.
I have two fragments (fragment1 and fragment2). Fragment 2 takes four arguments (bool, string, int, CustomParslableClass?). The IDE thinks that that is the correct order (and shows the red squiggles with a message "Type mismatch [...]" if I don't have them in that order). The app used to build fine with that order (a year ago when I last worked on it), but today I downloaded everything and had to update dependencies to build.
This is the normal line of code that the IDE thinks is right (used to work):
val direction = HomeViewPagerFragmentDirections.actionFragment1ToFragment2(false, "Favorites", 17, null)
However, when I build with that order, I get build errors for each argument:
e: filename.kt: (51, 106): The boolean literal does not conform to the expected type String
e: filename.kt: (51, 112): Type mismatch: inferred type is String but Int was expected
e: filename.kt: (51, 125): Type mismatch: inferred type is Int but PlaylistEntry? was expected
e: filename.kt: (51, 136): Null can not be a value of a non-null type Boolean
On the contrary, when I re-order the arguments to the function like so (arg2, arg3, arg4, arg1):
val direction = HomeViewPagerFragmentDirections.actionFragment1ToFragment2("Favorites", 17, null, false)
it builds fine, but the IDE shows red underlines under each argument, saying they're in the wrong order! (Type mismatch. Required: Boolean, Found: String)
Maybe important:
arg1 (bool) has a default value of false
arg2 (string) has no default value
arg3 (int) has no default value
arg4 (CustomParslableClass?) has no default value (but is nullable)
When I change arg2 to have a default value, the IDE expects the same, but the order that works for build changes to (arg3, arg4, arg1, arg2).
A simple clean and rebuild didn't help. I also tried cleaning the project, shutting down Android Studio and restarting, then doing a build. I even tried deleting the action between the two fragments, and re-creating (building between times). All to no avail. Note that when I run the built app, everything works as it should Normally that would be fine, but I don't think the argument order is supposed to be jumping around like that!
With multi args you should set like below
val action = BlankFragmentDirections.actionBlankFragmentToSecondFragment()
action.arg1 = 2
action.arg2 = 3
action.arg3 = "423"
findNavController().navigate(action)
Related
I'm using pint to use and convert units. I wanted to create classes which restricts the quantities only to "[time]" or "[length]" dimensions, so as a first approach I did the following:
from pint import Quantity, DimensionalityError
class Time(Quantity):
def __new__(cls, v: str | Quantity) -> Quantity:
obj = Quantity(v)
if not obj.check("[time]"):
raise DimensionalityError(v, "[time]")
return obj
class Length(Quantity):
def __new__(cls, v: str | Quantity) -> Quantity:
obj = Quantity(v)
if not obj.check("[length]"):
raise DimensionalityError(v, "[length]")
return obj
At runtime it works as expected, i.e: I can do the following:
1hour = Time("1h") # Works ok, variable 1hour contains `<Quantity(1, 'hour')>`
bad = Time("1meter") # As expected, raises pint.errors.DimensionalityError: Cannot convert from '1meter' to '[time]'
1meter = Length("1meter") # Ok
bad_again = Length("1h") # Ok, raises DimensionalityError
However, from a typing perspective, something is wrong:
def myfunc(t: Time) -> str:
return f"The duration is {t}"
print(myfunc(Time("1h"))) # Ok
print(myfunc(Length("1m"))) # Type error?
The second call to myfunc() is a type error, since I'm passing a Length instead of a Time. However mypy is happy with the code. So I have some questions:
Why doesn't mypy catches the error?
How to do it properly?
For 1. I guess that something fishy is happening in pint's Quantity implementation. I tried:
foo = Quantity("3 pounds")
reveal_type(foo)
and the revealed type is Any instead of Quantity which is very suspicious.
So I tried removing the base class Quantity from my Time and Length classes (i.e: they derive now from object instead of Quantity), and in this case, mypy correctly manages the typing errors.
But it fails again as soon as I try something like Length("60km")/Time("1h"). mypy complains that Length object does not implement the required method for performing that division (although the code works ok at runtime because, after all, Length and Time __new__() method is returning a Quantity object which does implement the arithmetical operations).
So, again, is there any workaround for making the idea work both at run-time and for mypy?
func move(motion):
if motion != Vector2():
target_angle = atan2(motion.x, motion.y) - PI/2
Skin.set_rot(target_angle)
I tried converting vectors to ints. I looked up this error and didnt understand fixes.
What does motion != Vector2() mean?
It is comparing two things to see if the are different:
motion a variable parameter of the function move which is Variant (it does not have a declared type).
Vector2() this is the default constructor of the Vector2 type. It should be equivalent to Vector2.ZERO or Vector2(0.0, 0.0).
Now, the error is telling you that you got a float, and that Godot does not know how to compare float and Vector2.
You might have expected that checking if a float is different from a Vector2 would return true (a float is different from a Vector2), but no, the comparison is an error. Yes, I tried. Otherwise the execution flow would enter the conditional and try to execute atan2(motion.x, motion.y) with a float and that would be error.
This means you need to ensure it is a Vector2 before comparing it with another Vector2.
You could have prevented getting a float here by declaring the type of the parameter:
func move(motion:Vector2):
if motion != Vector2():
target_angle = atan2(motion.x, motion.y) - PI/2
Skin.set_rot(target_angle)
And now it would give you an error where you are calling it with a float. Why or where are you calling it with a float? I don't know. Presumably if you are getting the error in runtime you can also find out by looking at the stack on the debugger panel when the error shows up. Otherwise, this is a sure way to find out.
By the way, aren't the parameters of atan2 backwards? Are you aware of motion.angle()?
Anyway, Bugfish is right that you can check if the type of motion is Vector2 in runtime, for example: typeof(motion) == TYPE_VECTOR2.
If I understand correctly you claim you did this: var motion = Vector2(). Well, something else happened somewhere, and we cannot find it for you. I presume you change the value of motion somewhere (that is the point of having a variable) perhaps set it to the return value of a function var motion = get_motion() or similar, and it sometimes returns a float...
Which, reminds me, you can also specify what your methods return. This method returns nothing (void):
func move(motion:Vector2) -> void:
if motion != Vector2():
target_angle = atan2(motion.x, motion.y) - PI/2
Skin.set_rot(target_angle)
And this one returns a Vector2:
func get_motion() -> Vector2:
return Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
Yes, GDScript has static types, use them!
Well it seems like your parameter motion does not contain a Vector2, but a float value. Where do you call the function and did you already check if the functions gets the parameter you wanted to pass it?
Also before using a vatiable with unknown type, You should check the type, before assuming it's an Vector.
One way would be to check it's type:
if typeof(motion) != TYPE_VECTOR2:
return
Recently I had a problem reading information from a .ini file to a int type variable:
var:int = config['example']['var']
The .ini file is something like this:
Using configparser to read the value pass it as '3' instead of 3.
I must add that I know I can convert it to int by using int(var) (that's how I fixed it by the way), but that is not the point of this question.
The variable type is a int and the configparser read the value from the file as a string and successfully changes the variable type to a string, the code worked for multiple days until I had a bug, the interpreter indicates I was trying to compare a string type with an int type, by this point I was so far into the code I wasted 10 minutes pinpointing the source of the bug.
Here's a example code:
class testclass:
def __init__(self, var:int): #Specifying that var should be a int
self._storage:int = var #specifiying that _storage should be a int
#property
def get(self) -> int: #specifiying that get method should return a int
return self._storage
#Expected behaviour
correct = 1
test1 = testclass(correct)
#Passing a string when it should be a integer
wrong = '1'
test2 = testclass(wrong)
print('test1 type = ' + str(type(test1.get)))
print('test2 type = ' + str(type(test2.get)))
And it's output:
test1 type = <class 'int'>
test2 type = <class 'str'>
As you can see I made sure to specify the type of every single method and variable so I couldn't unknowingly pass the wrong type by mistake (which is exactly what I did), but it did not make any difference, whats is the point of :int and -> int and what should I do the lock arguments/variables/methods etc. to a variable type so I can't shoot myself on the foot in the future?
Python doesn't force types. You can pass other types even if you specified a type. However, you can use a Python Linter such as mypy and it will warn you when you pass the wrong type.
Note: The program will run, even if you use mypy, but you will see a warning while writing the code.
I am quite confused how this assembly code works. I have tried looking around for answers but couldn't find anything. I think ldarg.0 is loaded due to an instance void, but i'm not sure why it loads ldarg.1.
Would really appreciate it if someone could explain what's going on.
.method public hidebysig specialname instance void set_phase(string value)
{
.maxstack 3
.locals init (bool V0)
nop
ldarg.1
ldarg.0
ldfld string KGER.Generate::_phase
call bool [mscorlib]System.String::op_Inequality(string, string)
ldc.i4.0
ceq
stloc.0
ldloc.0
brtrue.s loc_4E8
Thanks in advance!
Your code is not complete, however: the portion does the following:
.method public hidebysig specialname instance void set_phase(string value)
{
.maxstack 3
.locals init (bool V0)
This is the method signature. from here you infer two important things: the first is that the method is an instance method. This means that the first implicit argument contains this.
The second important thing is the signature which consists of a single argument of type string: this will be arg1 as arg0 is implicitly used to contain this.
nop
computationally, this does nothing. among other things, nop instructions can be used by debuggers to safely place break points
ldarg.1
this loads arg1 onto the stack. The stack contains (the value of the field named value)
ldarg.0
ldfld string KGER.Generate::_phase
then loads the this argument and immediately uses it to load the KGER.Generate::_phase field. the stack now contains (value, the content of the _phase field)
call bool [mscorlib]System.String::op_Inequality(string, string)
this calls the operator op_Inequality of the class String which is a static method. the stack now contains (result of comparison)
ldc.i4.0
this loads 0 as integer into the stack. as we know that this value will be part of a boolean comparison, remember that 0 is equivalent to false for these purposes
ceq
this compares the two values into the stack and pushes the result as boolean into the stack
stloc.0
this stores the result of the comparison to a local variable (the first one)
ldloc.0
this loads the result of the comparison stored to the abovementioned local variable again into the stack, presumably this is a debug version, and this pair of instructions allows the developer to correctly view the value of the variables while debugging (or it is really needed in a part of code you didn't share)
brtrue.s loc_4E8
this jumps to the location loc_4E8 when the value is true (i.e. 1), which means that if thee two strings are equal, the code will jump.
I' m having a problem parsing the lat and long cords from TinyGPS++ to a Double or a string. The code that i'm using is:
String latt = ((gps.location.lat(),6));
String lngg = ((gps.location.lng(),6));
Serial.println(latt);
Serial.println(lngg);
The output that i'm getting is:
0.06
Does somebody know what i'm doing wrong? Does it have something to do with rounding? (Math.Round) function in Arduino.
Thanks!
There are two problems:
1. This does not compile:
String latt = ((gps.location.lat(),6));
The error I get is
Wouter.ino:4: warning: left-hand operand of comma has no effect
Wouter:4: error: invalid conversion from 'int' to 'const char*'
Wouter:4: error: initializing argument 1 of 'String::String(const char*)'
There is nothing in the definition of the String class that would allow this statement. I was unable to reproduce printing values of 0.06 (in your question) or 0.006 (in a later comment). Please edit your post to have the exact code that compiles, runs and prints those values.
2. You are unintentionally using the comma operator.
There are two places a comma can be used: to separate arguments to a function call, and to separate multiple expressions which evaluate to the last expression.
You're not calling a function here, so it is the latter use. What does that mean? Here's an example:
int x = (1+y, 2*y, 3+(int)sin(y), 4);
The variable x will be assigned the value of the last expression, 4. There are very few reasons that anyone would actually use the comma operator in this way. It is much more understandable to write:
int x;
1+y; // Just a calculation, result never used
2*y; // Just a calculation, result never used
3 + (int) sin(y); // Just a calculation, result never used
x = 4; // A (trivial) calculation, result stored in 'x'
The compiler will usually optimize out the first 3 statements and only generate code for the last one1. I usually see the comma operator in #define macros that are trying to avoid multiple statements.
For your code, the compiler sees this
((gps.location.lat(),6))
And evaluates it as a call to gps.location.lat(), which returns a double value. The compiler throws this value away, and even warns you that it "has no effect."
Next, it sees a 6, which is the actual value of this expression. The parentheses get popped, leaving the 6 value to be assigned to the left-hand side of the statement, String latt =.
If you look at the declaration of String, it does not define how to take an int like 6 and either construct a new String, or assign it 6. The compiler sees that String can be constructed from const char *, so it tells you that it can't convert a numeric 6 to a const char *.
Unlike a compiler, I think I can understand what you intended:
double latt = gps.location.lat();
double lngg = gps.location.lon();
Serial.println( latt, 6 );
Serial.println( lngg, 6 );
The 6 is intended as an argument to Serial.println. And those arguments are correctly separated by a comma.
As a further bonus, it does not use the String class, which will undoubtedly cause headaches later. Really, don't use String. Instead, hold on to numeric values, like ints and floats, and convert them to text at the last possible moment (e.g, with println).
I have often wished for a compiler that would do what I mean, not what I say. :D
1 Depending on y's type, evaluating the expression 2*y may have side effects that cannot be optimized away. The streaming operator << is a good example of a mathematical operator (left shift) with side effects that cannot be optimized away.
And in your code, calling gps.location.lat() may have modified something internal to the gps or location classes, so the compiler may not have optimized the function call away.
In all cases, the result of the call is not assigned because only the last expression value (the 6) is used for assignment.