TypeInfo(Type) returns the info about the specified type, is there any way to know the typeinfo of a var?
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= TypeInfo(S);
Info:= TypeInfo(Instance);
Info:= TypeInfo(Obj);
end
This code returns:
[DCC Error] Unit1.pas(354): E2133 TYPEINFO standard function expects a type identifier
I know a non instantiated var is only a pointer address. At compile time, the compiler parses and do the type safety check.
At run time, is there any way to know a little more about a var, only passing its address?
-
Not that I know of. You can get RTTI (Run Time Type Information) on published properties of a class, but not for "normal" variables like strings and integers and so forth. The information is simply not there.
Besides, the only way you could pass a var without passing a type is to use either a generic TObject parameter, a generic type (D2008, as in ), or as an untyped parameter. I can't think of another way of passing it that would even compile.
Ulrich Gerhardt : You can have "procedure Test(var AUntypedParam)" (or with "const" or "out" instead of "var").Ken White : Sorry. My answer should have said "untyped parameter" instead of "untyped pointer". Corrected. -
No.
First, there's no such thing as a "non-instantiated variable." You instantiate it by the mere act of typing its name and type into your source file.
Second, you already know all there is to know about a variable by looking at it in your source code. The variable ceases to exist once your program is compiled. After that, it's all just bits.
A pointer only has a type at compile time. At run time, everything that can be done to that address has already been determined. The compiler checks for that, as you already noted. Checking the type of a variable at run time is only useful in languages where a variable's type could change, as in dynamic languages. The closest Delphi comes to that is with its
Variant
type. The type of the variable is alwaysVariant
, but you can store many types of values in it. To find out what it holds, you can use theVarType
function.Any time you could want to use
TypeInfo
to get the type information of the type associated with a variable, you can also directly name the type you're interested in; if the variable is in scope, then you can go find its declaration and use the declared type in your call toTypeInfo
.If you want to pass an arbitrary address to a function and have that function discover the type information for itself, you're out of luck. You will instead need to pass the
PTypeInfo
value as an additional parameter. That's what all the built-in Delphi functions do. For example, when you callNew
on a pointer variable, the compiler inserts an additional parameter that holds thePTypeInfo
value for the type you're allocating. When you callSetLength
on a dynamic array, the compiler inserts aPTypeInfo
value for the array type.The answer that you gave suggests that you're looking for something other than what you asked for. Given your question, I thought you were looking for a hypothetical function that could satisfy this code:
var S: string; Instance: IObjectType; Obj: TDBGrid; Info: PTypeInfo; begin Info:= GetVariableTypeInfo(@S); Assert(Info = TypeInfo(string)); Info:= GetVariableTypeInfo(@Instance); Assert(Info = TypeInfo(IObjectType)); Info:= GetVariableTypeInfo(@Obj); Assert(Info = TypeInfo(TDBGrid)); end;
Let's use the
IsClass
andIsObject
functions from the JCL to build that function:function GetVariableTypeInfo(pvar: Pointer): PTypeInfo; begin if not Assigned(pvar) then Result := nil else if IsClass(PPointer(pvar)^) then Result := PClass(pvar).ClassInfo else if IsObject(PPointer(pvar)^) then Result := PObject(pvar).ClassInfo else raise EUnknownResult.Create; end;
It obviously won't work for
S
orInstance
above, but let's see what happens withObj
:Info := GetVariableTypeInfo(@Obj);
That should give an access violation.
Obj
has no value, soIsClass
andIsObject
both will be reading an unspecified memory address, probably not one that belongs to your process. You asked for a routine that would use a variable's address as its input, but the mere address isn't enough.Now let's take a closer look at how
IsClass
andIsObject
really behave. Those functions take an arbitrary value and check whether the value looks like it might be a value of the given kind, either object (instance) or class. Use it like this:// This code will yield no assertion failures. var p: Pointer; o: TObject; a: array of Integer; begin p := TDBGrid; Assert(IsClass(p)); p := TForm.Create(nil); Assert(IsObject(p)); // So far, so good. Works just as expected. // Now things get interesting: Pointer(a) := p; Assert(IsObject(a)); Pointer(a) := nil; // A dynamic array is an object? Hmm. o := nil; try IsObject(o); Assert(False); except on e: TObject do Assert(e is EAccessViolation); end; // The variable is clearly a TObject, but since it // doesn't hold a reference to an object, IsObject // can't check whether its class field looks like // a valid class reference. end;
Notice that the functions tell you nothing about the variables, only about the values they hold. I wouldn't really consider those functions, then, to answer the question of how to get type information about a variable.
Furthermore, you said that all you know about the variable is its address. The functions you found do not take the address of a variable. They take the value of a variable. Here's a demonstration:
var c: TClass; begin c := TDBGrid; Assert(IsClass(c)); Assert(not IsClass(@c)); // Address of variable Assert(IsObject(@c)); // Address of variable is an object? end;
You might object to how I'm abusing these functions by passing what's obviously garbage into them. But I think that's the only way it makes sense to talk about this topic. If you know you'll never have garbage values, then you don't need the function you're asking for anyway because you already know enough about your program to use real types for your variables.
Overall, you're asking the wrong question. Instead of asking how you determine the type of a variable or the type of a value in memory, you should be asking how you got yourself into the position where you don't already know the types of your variables and your data.
mghie : The answer was already good, but your edit made it even better. +1. -
I found this code in JclSysUtils unit.
function IsClass(Address: Pointer): Boolean; assembler; asm CMP Address, Address.vmtSelfPtr JNZ @False MOV Result, True JMP @Exit @False: MOV Result, False @Exit: end; function IsObject(Address: Pointer): Boolean; assembler; asm // or IsClass(Pointer(Address^)); MOV EAX, [Address] CMP EAX, EAX.vmtSelfPtr JNZ @False MOV Result, True JMP @Exit @False: MOV Result, False @Exit: end;
And if I know if it is a class or object, I can know what is the class. I think I can do something like that to strings too, not sure about others primitive types.
Rob Kennedy : I don't think those will really do what you asked for. I've updated my answer in response. I think you're trying to solve the wrong problem.
0 comments:
Post a Comment