Is there a way to specify an unknown type in GDScript?
GDScript docs use the type Variant for this (for example in Array.count method).
Say, I'd like to write an identity function. I can do it like so:
func identity(x):
return x
But I'd like to declare that both the parameter type and return value could be anything. Something like:
func identity(x: Variant) -> Variant:
return x
This doesn't work though. Variant is not a known type name. I tried various names, bot nothing seems to work.
Is the only option to leave off the type?
Yes, the only option is to not specify the type.
This function:
func identity(x):
return x
Takes Variant and returns Variant.
There is a Variant class defined in Godot in C++. Which, as you have found out, we cannot use it by name in GDScript.
Please notice that the docs use a notation based on C++. For instance int count (Variant value) does not only differ from GDScript because of Variant, but also in that you specify the type after the parameters not before the name, also you use func.
This is how int count (Variant value) looks in GDScript: func count(value) -> int:, and this is the C++ definition: int Array::count(const Variant &p_value) const (source). Compare the C++ definition with the one on the documentation.
For another example, this is how Array duplicate (bool deep=false) looks like in GDSCript: func duplicate(deep:bool=false) -> Array:, and this is the C++ definition: Array Array::duplicate(bool p_deep) const. (source). Notice the C++ definition does not indicate the parameter will be optional, that information is added for the script binding.
I'm porting my code to the new unified xamarin.ios api.
I have this as a parameter in a method.
nfloat fontSize = 0f
The compiler complains...
Error CS1750: Optional parameter expression of type `float' cannot be converted to parameter type `System.nfloat'
How do I write a literal for an optional parameter of nfloat?
You can't.
The native types in Xamarin.iOS/Xamarin.Mac (nfloat, nint, nuint) are not intrinsic managed types, and thus you can't create constants of those types.
Is it possible, or desirable, to set objects/data to an "Empty" or "Missing" variant?
I want to be able to conditionally pass optional arguments to a function. Sometimes I want to use an optional argument, sometimes I don't.
In Python, you could easily pass through whichever optional arguments you wanted by using **kwdargs to unpack a dictionary or list into your function arguments. Is there something similar (or a way to hack it in VBA) so you can pass in Empty/Missing optional arguments?
In particular, I'm trying to use Application.Run with an arbitrary number of arguments.
EDIT:
I'm basically trying to do this:
Public Function bob(Optional arg1 = 0, Optional arg2 = 0, Optional arg3 = 0, Optional arg4 = 0)
bob = arg1 + arg2 + arg3 + arg4
End Function
Public Function joe(Optional arg1)
joe = arg1 * 4
End Function
Public Sub RunArbitraryFunctions()
'Run a giant list of arbitrary functions pseudocode
Dim flist(1 To 500)
flist(1) = "bob"
flist(2) = "joe"
flist(3) = "more arbitrary functions of arbitrary names"
flist(N) = ".... and so on"
Dim arglist1(1 To 4) 'arguments for bob
Dim arglist2(1 To 1) 'arguments for joe
Dim arglist3(1 To M number of arguments for each ith function)
For i = 1 To N
'Execute Application.Run,
'making sure the right number of arguments are passed in somehow.
'It'd also be nice if there was a way to automatically unpack arglisti
Application.Run flist(i) arglisti(1), arglisti(2), arglisti(3), ....
Next i
End Sub
Because the number of arguments changes for each function call, what is the acceptable way to make sure the right number of inputs are input into Application.Run?
The equivalent Python code would be
funclist = ['bob', 'joe', 'etc']
arglists = [[1,2,3],[1,2],[1,2,3,4,5], etc]
for args, funcs in zip(arglists, funclist):
func1 = eval(funcs)
output = func1(*args)
in VBA you use ParamArray to enter option inputs to functions.
See Pearson Material
There are two ways in which a routine can change the number of arguments that has to be provided to it:
declare some of the trailing arguments as Optional
declare the last argument as ParamArray
A single routine can use either or both.
An Optional parameter may have a strict type (e.g. Optional s As String), but then it will be impossible to detect whether it was passed. If you don't pass a value for such argument, the correct flavour of "blank" will be used, which is indistinguishable from passing that blank value manually.
So, having Public Sub Bob(Optional S As String), you cannot detect from inside of Bob whether it was called as Bob or as Bob vbNullString.
An optional parameter may have a default value, which suffers from the same problem. So, having Public Sub Bob(Optional S As String = "Default Value"), you cannot detect if Bob was called as Bob or as Bob "Default Value".
To be able to truly detect whether an optional parameter was passed, they have to be typed as Variant. Then a special function, IsMissing, can be used inside the routine to detect if a parameter was passed.
Public Sub Bob(Optional a, Optional b, Optional c, Optional d)
Debug.Print IsMissing(a), IsMissing(b), IsMissing(c), IsMissing(d)
End Sub
Bob 1, , 3 ' Prints False, True, False, True
ParamArray can only be the last argument, and it allows an infinite* number of arguments to be passed starting from this position. All these arguments arrive packed in a single Variant array (no option for static typing here).
The IsMissing function does not work on the ParamArray argument (always returns False). The way to know how many arguments were passed is to compare UBound(args) with LBound(args). Note that this only tells you how many argument "slots" were used, but some of them can be in fact missing!
Public Sub BobArray(ParamArray a())
Dim i As Long
For i = LBound(a) To UBound(a)
Debug.Print IsMissing(a(i)), ;
Next
Debug.Print
End Sub
BobArray ' Prints empty line (the For loop is not entered due to UBound < LBound)
Sheet1.BobArray 1, 2, 3 ' Prints False, False, False
Sheet1.BobArray 1, , 3 ' Prints False, True, False
Note that you cannot pass "missing" value for the trailing arguments of the ParamArray, i.e. this is illegal:
Sheet1.BobArray 1, , 3, ' Does not compile
However, you can work around this using the trick described below.
An interesting use case that you touch in your question is preparing an array of all arguments in advance, passing it to the function, filling all the arguments "placeholders", but still expecting the function to detect that some of the arguments are missing (not passed).
Normally this is not possible, because if anything is passed (even "blank" values, such as Empty, Null, Nothing of vbNullString), then it still counts as passed, and IsMissing() will return False.
Fortunately, the special Missing value is nothing but a specially constructed Variant, and even without knowing how to construct that value manually, we can trick the compiler to give it away:
Public Function GetMissingValue(Optional ByVal IgnoreMe As Variant) As Variant
If IsMissing(IgnoreMe) Then
GetMissingValue = IgnoreMe
Else
Err.Raise 5, , "I told you to ignore me, didn't I"
End If
End Function
Dim missing As Variant
missing = GetMissingValue()
Dim arglist1(1 To 4) As Variant
arglist1(1) = 42
arglist1(2) = missing
arglist1(3) = missing
arglist1(4) = "!"
Bob arglist1(1), arglist1(2), arglist1(3), arglist1(4) ' Prints False, True, True, False
Now, we can work around the inability to pass "missing" to the trailing "slots" of ParamArray:
Dim arglist1(1 To 4) As Variant
arglist1(1) = 42
arglist1(2) = missing
arglist1(3) = missing
arglist1(4) = missing
BobArray arglist1(1), arglist1(2), arglist1(3), arglist1(4) ' Prints False, True, True, True
Note, however, that this workaround will only work if you call BobArray directly. If you use Application.Run, it will not work because the Run method will discard any trailing "missing" arguments before passing them onto the called routine:
Dim arglist1(1 To 4) As Variant
arglist1(1) = 42
arglist1(2) = missing
arglist1(3) = missing
arglist1(4) = missing
Application.Run "BobArray", arglist1(1), arglist1(2), arglist1(3), arglist1(4)
' Prints False, because only one argument is passed
Further to #GSerg's very comprehensive answer (I don't have enough reputation just to comment), the 'special' value assigned to a Missing argument has the 'appearance' of being an Error value - it converts to "Error 448" (Named argument not found) using CStr(), and responds to IsError() as TRUE. However, an attempt to preset the argument using CvErr(448) before passing to a procedure (in the hope that it will be recognised as Missing) fails, perhaps because the value is 'not quite' the same as the Error value in some way.
#GSerg suggested a method of 'recording' the value actually passed by the compiler when an argument is missing and using that to preset a dummy argument prior to passing to the procedure needing to be fooled. This method, indeed, does work and I have simply extended #GSerg's function to replace his error message (if it is inadvertently called with an argument) by a recursive call without an argument which ensures a successful outcome either way. Usage is simply to preset the dummy variable(s) before passing to a procedure (where it/they will then be treated as missing): Dummy_Var = Missing().
Public Function Missing(Optional ByVal X As Variant) As Variant
If IsMissing(X) Then 'correctly called
Missing = X
Else 'bad user call
Missing = Missing() 'recursive call (no arg!)
End If
End Function
I have just done a quick trial with Application.Run. Early embedded 'missing' arguments (ie, followed by 'normal' ones) appear to be successfully registered as 'missing' in the called procedure. So, too, however, are final trailing 'missing' arguments - whether actually passed by the Run method, or truncated (as suggested by #GSerg), but still filled in by the compiler as genuinely missing.
Interestingly, and usefully (to a niche market), additional 'missing' arguments (beyond those defined by the procedure) appear to be tolerated by the compiler without generating the 'Wrong number of arguments' message associated with extra 'normal' arguments. This opens up the possibility of procedure calls using Application.Run (when a variable number of arguments is desired) being implemented by a single universal call (with up to 30 arguments if necessary) padded out with fake 'missing' arguments instead of having to provided several alternative calls of different lengths and/or argument configurations to cope with exact procedure definitions.
So addressing the question of optionally using arguments it looks like my question in Calling vba macro from python with unknown number of arguments, check it out accordingly.
Hence:
Using Python:
def run_vba_macro(str_path, str_modulename, str_macroname, **kwargs):
if os.path.exists(str_path):
xl=win32com.client.DispatchEx("Excel.Application")
wb=xl.Workbooks.Open(str_path, ReadOnly=0)
xl.Visible = True
if kwargs:
params_for_excel = list(kwargs.values())
xl.Application.Run(os.path.basename(str_path)+"!"+str_modulename+'.'+str_macroname,
*params_for_excel,)
else:
xl.Application.Run(os.path.basename(str_path)
+"!"+str_modulename
+'.'+str_macroname)
wb.Close(SaveChanges=0)
xl.Application.Quit()
del xl
#example
kwargs={'str_file':r'blablab'}
run_vba_macro(r'D:\arch_v14.xlsm',
str_modulename="Module1",
str_macroname='macro1',
**kwargs)
#other example
kwargs={'arg1':1,'arg2':2}
run_vba_macro(r'D:\arch_v14.xlsm',
str_modulename="Module1",
str_macroname='macro_other',
**kwargs)
Using VBA:
Sub macro1(ParamArray args() as Variant)
MsgBox("success the str_file argument was passed as =" & args(0))
End Sub
Sub macro_other(ParamArray args() as Variant)
MsgBox("success the arguments have passed as =" & str(args(0)) & " and " & str(args(1)))
End Sub
Also another use case only using VBA is here for reference. It is a question that has not been answered and is around for long, although recently it was updated by the community server automatically with some good ideas related links accordingly.
Here is an answer you can do it if you use this:
Sub pass_one()
Call flexible("a")
End Sub
Sub pass_other()
Call flexible("a", 2)
End Sub
Sub flexible(ParamArray args() As Variant)
Dim i As Long
MsgBox ("I have received " & _
Str(UBound(args) + 1) & _
" parameters.")
For i = 0 To UBound(args)
MsgBox (TypeName(args(i)))
Next i
End Sub
Only for developers that also use Python:
If you are using Python's kwargs, simply starr expression and pass a Python tuple.
Here it is (it is related with my question in Calling vba macro from python with unknown number of arguments)
Cheers.
I am trying to make a if statement where I want to compare 2 strings, whether they are equal or not in condition.
This is what I have
if vysledok.Equals(meno) then
Application.MessageBox('Zadane meno existuje, zadajte prosím iné meno','DUPLICITNÝ UŽÍVATEĽ',0)
else
...
However vysledok.Equals(meno) is underlined and it says this:
'string' does not contain a member named 'Equals' at line ...
Type of expression must be BOOLEAN at line ...
I have to mention that I am new to delphi :)
Thank you for advice
In modern Delphi, the helper for the string type, defined in SysUtils, provides an Equals method. So, in XE3 or later, if you use SysUtils, your code will compile. From which we can surmise that you are using an older version of Delphi, or have not used SysUtils.
In older versions of Delphi you compare strings using the equality operator:
if vysledok = meno then
In fact, the implementation of the Delphi string helper Equals method does nothing more than compare using this equality operator.
Should you want a case insensitive comparison use SameText():
if SameText(vysledok, meno) then
This should work too:
if AnsiUpperCase(vysledok) = AnsiUpperCase(meno) then
I have two statements:
String aStr = new String("ABC");
String bStr = "ABC";
I read in book that in first statement JVM creates two bjects and one reference variable, whereas second statement creates one reference variable and one object.
How is that? When I say new String("ABC") then It's pretty clear that object is created.
Now my question is that for "ABC" value to we do create another object?
Please clarify a bit more here.
Thank you
You will end up with two Strings.
1) the literal "ABC", used to construct aStr and assigned to bStr. The compiler makes sure that this is the same single instance.
2) a newly constructed String aStr (because you forced it to be new'ed, which is really pretty much non-sensical)
Using a string literal will only create a single object for the lifetime of the JVM - or possibly the classloader. (I can't remember the exact details, but it's almost never important.)
That means it's hard to say that the second statement in your code sample really "creates" an object - a certain object has to be present, but if you run the same code in a loop 100 times, it won't create any more objects... whereas the first statement would. (It would require that the object referred to by the "ABC" literal is present and create a new instance on each iteration, by virtue of calling the constructor.)
In particular, if you have:
Object x = "ABC";
Object y = "ABC";
then it's guaranteed (by the language specification) than x and y will refer to the same object. This extends to other constant expressions equal to the same string too:
private static final String A = "a";
Object z = A + "BC"; // x, y and z are still the same reference...
The only time I ever use the String(String) constructor is if I've got a string which may well be backed by a rather larger character array which I don't otherwise need:
String x = readSomeVeryLargeString();
String y = x.substring(5, 10);
String z = new String(y); // Copies the contents
Now if the strings that y and x refer to are eligible for collection but the string that z refers to isn't (e.g. it's passed on to other methods etc) then we don't end up holding all of the original long string in memory, which we would otherwise.