type(v) for v1.1

Post your working scripts, libraries and tools
lexikos
Posts: 7766
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

type(v) for v1.1

21 Feb 2014, 01:10

type(v)

Returns the type of a value: "Integer", "String", "Float" or "Object"

Code: Select all

; Object version - depends on current float format including a decimal point.
type(v) {
    if IsObject(v)
        return "Object"
    return v="" || [v].GetCapacity(1) ? "String" : InStr(v,".") ? "Float" : "Integer"
}

Code: Select all

; COM version - reports the wrong type for integers outside 32-bit range.
type(ByRef v) {
	if IsObject(v)
		return "Object"
	a := ComObjArray(0xC, 1)
	a[0] := v
	DllCall("oleaut32\SafeArrayAccessData", "ptr", ComObjValue(a), "ptr*", ap)
	type := NumGet(ap+0, "ushort")
	DllCall("oleaut32\SafeArrayUnaccessData", "ptr", ComObjValue(a))
	return type=3?"Integer" : type=8?"String" : type=5?"Float" : type
}
Note: Requires v1.1. It might work with v2, but I wouldn't recommend it.

Note: Results can be hard to predict due to binary number caching in v1. For instance, y := x + 0 can change the type of x from "String" to "Integer" or "Float".

Note: type(1.0) returns "String" because floating-point literals in v1 actually are strings. type(1.0+0) returns "Float".


Differentiating between object types

type() in v2 returns specific object classes, like FileObject or ComObject. This function doesn't. There are a couple of workarounds:
  • ComObjValue(x) != "" is true if x is a ComObject.
  • ObjGetCapacity(x) != "" is true if x is an Object (associative array).
User avatar
joedf
Posts: 8259
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: type(v) for v1.1

22 Feb 2014, 18:16

nice!
obeeb
Posts: 140
Joined: 20 Feb 2014, 19:15

Re: type(v) for v1.1

22 Feb 2014, 21:10

Great function.
Another note: It will incorrectly return float for integers bigger than 2147483647 (maximum number for signed 32 bit integers)
lexikos
Posts: 7766
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: type(v) for v1.1

23 Feb 2014, 02:38

Right. Technically, it returns the type which will be used if the value is passed to a COM object.
lexikos
Posts: 7766
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: type(v) for v1.1

10 Mar 2014, 04:10

I've updated the first post with a new version of the function which doesn't have the limitation obeeb mentioned:

Code: Select all

MsgBox % type("")     ; String
MsgBox % type(1)      ; Integer
MsgBox % type(1/1)    ; Float
MsgBox % type("1")    ; String
MsgBox % type(2**42)  ; Integer

type(v) {
    if IsObject(v)
        return "Object"
    return v="" || [v].GetCapacity(1) ? "String" : InStr(v,".") ? "Float" : "Integer"
}
This relies on the following:
  • Unlike a variable, an array element can't contain a string and a pure number simultaneously.
  • A field's "capacity" is the size of string it can hold before it needs to expand. If it doesn't hold a string it has no capacity.
  • SetFormat hasn't been used to remove the decimal point from the floating-point string format. (If var is float has the same limitation, since it always converts the value to a string for analysis.)
v="" || [v].GetCapacity(1) could be replaced with [v].GetCapacity(1) != "" or v="" || [v].GetAddress(1) to the same effect.
MrDoge
Posts: 111
Joined: 27 Apr 2020, 21:29

Re: type(v) for v1.1

14 Dec 2021, 23:31

added support for VarSetCapacity "Buffer"
the trick is that COPY of Buffer is different from Byref Buffer, while copy of String is identical

Code: Select all

if (v "") { ;concatenation works too, you can MsgBox % Buffer, but a Buffer won't concat
    return "String" ;non empty String, we cover empty String down below
} else {
    return "Buffer"
}
and added support for Array
isArray() by jeeswg, then I edited it..

Type.ahk

Code: Select all

; /*
; TESTS
emptyString:=""
assert(type(emptyString), "String") ;String

VarSetCapacity(structZero, 0)
; MsgBox % VarSetCapacity(structZero) ;0
assert(type(structZero), "String") ;String..
; THEY'RE THE SAME ADDRESS &emptyString==&structZero==5369930888, seeing "" can mean that the Buffer has been freed, and no one uses an empty Buffer
; MsgBox % &emptyString "`n" &structZero




VarSetCapacity(struct1, 16)
; Msgbox % VarSetCapacity(struct1) ;16
assert(type(struct1), "Buffer") ;Buffer


VarSetCapacity(struct2, 1)
; Msgbox % VarSetCapacity(struct2) ;6 even if 1
assert(type(struct2), "Buffer") ;Buffer




randomBuff(len, Byref RandomBuffer) {
    static hModule_Advapi32 := DllCall("LoadLibrary", "Str", "Advapi32.dll", "Ptr")
    static Proc_RtlGenRandom := DllCall("GetProcAddress", "Ptr", hModule_Advapi32, "AStr", "SystemFunction036", "Ptr")
    RandomBufferLength:=Ceil(0.75*len)
    VarSetCapacity(RandomBuffer, RandomBufferLength)
    ok:=DllCall(Proc_RtlGenRandom, "Ptr", &RandomBuffer, "UInt", RandomBufferLength)
}
randomBuff(100, RandomBuffer)
assert(type(RandomBuffer), "Buffer")


assert(type("abc"), "String")
assert(type("1"), "String")
assert(type(1.1), "String")


assert(type(1), "Integer")


assert(type(1/1), "Float")


assert(type([]), "Array")
assert(type([1,2,3]), "Array")
assert(type({1:1, 3:3, 4:4}), "Object")
assert(type({1:"a", 3:"c", x:"x"}), "Object")
assert(type({1:"a", "2":"b", 3:"c"}), "Object")

assert(type(1), "fwiefowief") ;test if my assert function works :)

assert(actual, expected) {
    if !(actual==expected)
        MsgBox % "Line " Exception("", -1).Line " Failed`nactual:      " actual "`nexpected: " expected
    ;https://www.autohotkey.com/board/topic/71487-can-a-function-grab-the-calling-lines-line-number/#post_id_453358
}
; */

; "0 size String" v=="" && [v].GetCapacity(1)==0 && VarSetCapacity()==0
; "0 size Buffer" v=="" && [v].GetCapacity(1)==0 && VarSetCapacity()==0
; "16 size Buffer with no string representation" v=="" && [v].GetCapacity(1)==0 && VarSetCapacity()>0

; "String" v!="" && [v].GetCapacity(1)>0 && VarSetCapacity()>0

; Byref "Buffer" v!="" && [v].GetCapacity(1)>0 && VarSetCapacity()>0
; I think Byref Buffer could be v=="" (by chance)
; the trick is that COPY of Buffer is different from Byref Buffer, while copy of String is identical
; COPY OF BUFFER v=="" && [v].GetCapacity(1)==0 && VarSetCapacity()>0

; Number v!="" && [v].GetCapacity(1)=="" && VarSetCapacity()==0

type(Byref v) { ; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=2306
    if IsObject(v) {
        ; if isArray() https://www.autohotkey.com/boards/viewtopic.php?f=76&t=64332&p=434396#p434396
        if (!ObjCount(v) || ObjMinIndex(v) == 1 && ObjMaxIndex(v) == ObjCount(v) && v.Clone().Delete(1, v.MaxIndex()) == ObjCount(v)) {
            return "Array"
        } else {
            return "Object"
        }
    } else {
        objCapacity:=[v].GetCapacity(1)
        if (objCapacity>0) {
            ;strings will get perfectly copied, but Buffer won't
            ; if ([copy:=v].GetCapacity(1)>0) {
            if (v "") { ;concatenation works too, you can MsgBox % Buffer, but a Buffer won't concat
                return "String" ;non empty String, we cover empty String down below
            } else {
                return "Buffer" ;Buffer with string representation, but can't concat
            }
        } else if (objCapacity==0) {
            if (VarSetCapacity(v)) {
                return "Buffer" ; 16 size Buffer with no String representation
            } else {
                return "String" ; 0 size String And 0 size Buffer, they're the same thing
            }

        } else {
            if (InStr(v,".")) {
                return "Float"
            } else {
                return "Integer"
            }
        }

    }
}
ternary version:

Code: Select all

type(Byref v) { ; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=2306
    return IsObject(v) ? ((!ObjCount(v) || ObjMinIndex(v) == 1 && ObjMaxIndex(v) == ObjCount(v) && v.Clone().Delete(1, v.MaxIndex()) == ObjCount(v)) ? "Array" : "Object") : ((objCapacity:=[v].GetCapacity(1))>0 ? (v "" ? "String" : "Buffer") : objCapacity==0 ? (VarSetCapacity(v) ? "Buffer" : "String") : (InStr(v,".") ? "Float" : "Integer"))
}
___
I'm using this in Array_p()
to print a Buffer like this : < 255 0 1 3 >

I'm using recursion in a Byref function to print deep arrays
I have a question :
; using [Value24][1] because Value24 breaks Byref, why does it break Byref ?
(Ctrl+F to find this in code)

Code: Select all

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance, force
ListLines Off
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetBatchLines, -1
#KeyHistory 0

doPrint:=true
; doPrint:=false

emptyString:=""
printIfTrue(Array_p(emptyString))

VarSetCapacity(structZero, 0)
; MsgBox % VarSetCapacity(structZero) ;0
printIfTrue(Array_p(structZero))
; THEY'RE THE SAME ADDRESS &emptyString==&structZero==5369930888, seeing "" can mean that the Buffer has been freed, and no one uses an empty Buffer
; MsgBox % &emptyString "`n" &structZero




VarSetCapacity(struct1, 16)
; Msgbox % VarSetCapacity(struct1) ;16
printIfTrue(Array_p(struct1))


VarSetCapacity(struct2, 1)
; Msgbox % VarSetCapacity(struct2) ;6 even if 1
printIfTrue(Array_p(struct2))




randomBuff(len, Byref RandomBuffer) {
    static hModule_Advapi32 := DllCall("LoadLibrary", "Str", "Advapi32.dll", "Ptr")
    static Proc_RtlGenRandom := DllCall("GetProcAddress", "Ptr", hModule_Advapi32, "AStr", "SystemFunction036", "Ptr")
    RandomBufferLength:=Ceil(0.75*len)
    VarSetCapacity(RandomBuffer, RandomBufferLength)
    ok:=DllCall(Proc_RtlGenRandom, "Ptr", &RandomBuffer, "UInt", RandomBufferLength)
}
randomBuff(100, RandomBuffer)
printIfTrue(Array_p(RandomBuffer))


printIfTrue(Array_p("abc"))
printIfTrue(Array_p("1"))
printIfTrue(Array_p(1.1))


printIfTrue(Array_p(1))


printIfTrue(Array_p(1/1))


printIfTrue(Array_p([]))
printIfTrue(Array_p([1,2,3]))
printIfTrue(Array_p({1:1, 3:3, 4:4}))
printIfTrue(Array_p({1:"a", 3:"c", x:"x"}))
printIfTrue(Array_p({1:"a", "2":"b", 3:"c"}))

ExitApp

printIfTrue(toPrint) {
    global doPrint
    if (doPrint) {
        MsgBox % toPrint
    }
}

type(Byref v) { ; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=2306
    return IsObject(v) ? ((!ObjCount(v) || ObjMinIndex(v) == 1 && ObjMaxIndex(v) == ObjCount(v) && v.Clone().Delete(1, v.MaxIndex()) == ObjCount(v)) ? "Array" : "Object") : ((objCapacity:=[v].GetCapacity(1))>0 ? (v "" ? "String" : "Buffer") : objCapacity==0 ? (VarSetCapacity(v) ? "Buffer" : "String") : (InStr(v,".") ? "Float" : "Integer"))
}

;Byref for Buffer() < 255 0 1 3 >
Array_p(Byref Arr21, delim:=", ") {
    OutPut:=""

    theType:=type(Arr21)
    switch (theType) {
        case "Array":
            ; For Key23, Value24 in Arr21 { ;we cannot use this because it breaks Byref, why does it break Byref ?
            For Key23 in Arr21 {
                Output .= delim Array_p(Arr21[Key23], delim)
            }
            OutPut := "[" SubStr(OutPut, StrLen(delim)+1) "]" ;remove the first ", "
        case "Object":
            ; using [Value24][1] because Value24 breaks Byref, why does it break Byref ?
            For Key23, Value24 in Arr21 {
                Output .= delim (type(Key23) == "String" ? """" Key23 """" : Key23) ":" Array_p([Value24][1], delim)
            }
            OutPut := "{" SubStr(OutPut, StrLen(delim)+1) "}" ;remove the first ", "
        case "Integer", "Float":
            Output .= Arr21
        case "String":
            Output .= """" Arr21 """"
        case "Buffer":
            Size:=VarSetCapacity(Arr21)
            offset:=0
            OutPut.="< "
            while (offset < Size) {
                OutPut.=NumGet(Arr21, offset, "UChar") " "
                offset++
            }
            OutPut.=">"
    }

    Return OutPut
}

f3::Exitapp

Return to “Scripts and Functions”

Who is online

Users browsing this forum: ahketype, DaveT1, effel, swagfag, xianii and 26 guests