Can't get simple property editor to be used - delphi

A have a set of custom components that we use to encapsulate some functionality and I'm trying to add a custom property editor and can't seem to work out how to get it to apply. Our registration unit has the following code in it
type
THexWordProperty = class(TIntegerProperty)
public
function GetValue: string; override;
end;
{ THexProperty }
function THexWordProperty.GetValue: string;
begin
Result := '$'+IntToHex(GetOrdValue, 4);
end;
followed by the following call
RegisterPropertyEditor(TypeInfo(TPeripheralMask),nil,'',THexWordProperty);
in the registration procedure.
Despite rebuilding the package, uninstalling, reinstalling and restarting Delphi I can't get any of my TPeripheralMask properties to display any different. As far as I can see this is the simplest possible property editor as I'm simply trying to get what is a simple ordinal property to display in a different (but still valid for input) form.
The actual property type is defined simply as type TPeripheralMask = Word;, is there something I should be doing to support additional RTTI for this type?

type
TPeripheralMask = Word;
This is a type alias. That means that TPeripheralMask and Word refer to the same type. You need to make a new type.
type
TPeripheralMask = type Word;
The relevant documentation says the following:
When you declare a type that is identical to an existing type, the
compiler treats the new type identifier as an alias for the old one.
Thus, given the declarations:
type TValue = Real;
var
X: Real;
Y: TValue;
X and Y are of the same type; at run time, there is no way to
distinguish TValue from Real. This is usually of little consequence,
but if your purpose in defining a new type is to utilize runtime type
information, for example, to associate a property editor with
properties of a particular type - the distinction between 'different
name' and 'different type' becomes important. In this case, use the
syntax:
type newTypeName = type KnownType

Related

What does `type` keyword actually means when used in a type definition? [duplicate]

This question already has an answer here:
What does type after the = do in a type declaration
(1 answer)
Closed 5 years ago.
I've recently read some lines of the VCL source code and found the definition of the TCaption type:
TCaption = type string;
I've always thought it was just another name for string type and I was thinking that it was defined as follows:
TCaption = string;
So, I've looked for documentation about the type keyword and I've found this:
type Name = Existing type
Refers to an existing type, such as string by a new Name.
type Name = type Existing type
This has the same effect as above, but ensures that at run time, variables of this type are identified by
their new type name, rather than the existing type name.
After reading it, I'm still confused and I don't understand what "...ensures that at run time, variables of this type are identified by their new type name..." actually means.
Could someone shed some light on this?
Consider the following code, and note that the procedure Check() has a var parameter:
type
Ta = string; // type alias
Tb = type string; // compatible but distinct new type
procedure Check(var s: string);
begin
ShowMessage(s);
end;
procedure TMain.Button2Click(Sender: TObject);
var
a: Ta;
b: Tb;
begin
a := 'string of type Ta,';
b := 'string of type Tb.';
Check(a);
Check(b);
end;
Check(b) results in a compiler error:
E2033 Types of actual and formal var parameters must be identical
In the above, type Tb is compatible with string in that you can f. ex. assign a := b, but it is distinct in that the type identifier (under the hood) has a different value, and therefore not accepted as argument for Check(var s: string).
Type declaration like
TCaption = type string;
creates new type with different RTTI information. Also it cannot be used as var parameter of function if string type needed.
New RTTI information "...ensures that at run time, variables of this type are identified by their new type name...". So if you try to get type name for an instance of TCaptionSame = string;, you'll get string, while for TCaption type variable you'll get TCaption
To get more exact information, it would better to refer to official help

How to get property of the 'record' type using TypInfo unit

I have this record type
TDoublePoint = record
X : Double;
Y : Double;
end;
then I have object with this property
uses ..TypInfo;
TCell = class(TPersistent)
private
FZoom : TDoublePoint
published
property Zoom : TDoublePoint read FZoom write FZoom;
end;
But when I want to get PropInfo of this property with this function:
function GetKind(AObject:TObject; Propertyname :shortstring):TTypeKind;
var p :ppropinfo;
begin
p:=GetPropInfo(AObject, Propertyname); // <p = nil
Result:= p^.proptype^.Kind;
end;
..
..
c := TCell.Create;
GetKind(c, 'Zoom'); // <- error
c.Free;
I get error, because variable p is nil in the function.
But Why?
There is tkRecord in the TTypeKind, so I expected no problems to read/write the property of record type, but it seems, it is not possible (?)
Google search did not tell much.
Delphi 7 does not generate RTTI for a record type by default, and so a published property that uses a record type will not have RTTI, either (you can use TypInfo.GetPropList() to confirm that).
At one point, this was a documented limitation:
Published properties are restricted to certain data types. Ordinal, string, class, interface, variant, and method-pointer types can be published.
However, there is a workaround. IF a record type contains any compiler-managed data types (long strings, interfaces, dynamic arrays, etc), then RTTI will be generated for that record type, as will any published property that uses that record type, and thus GetPropInfo() can find such properties (I have confirmed that this does work in Delphi 7).
Your TDoublePoint record does not contain any compiler-managed data types, so that is why GetPropInfo() is returning nil for your TCell.Zoom property.
That RTTI issue was fixed in a later version (not sure which one. I'm guessing maybe in Delphi 2010, when Extended RTTI was first introduced). For instance, the code you have shown works for me as-is in XE. GetPropInfo() can find the Zoom property as expected, without having to introduce any compiler-managed types into the TDoublePoint record type.

Discrepancy of types

Delphi.
Why
type
myInt = Integer;
myNewInt = -2147483648..2147483647;
var
a: myint;
b: myNewInt;
begin
a := b;
end;
It is compiled normally though types formally different - one is declared here, another undertakes from other module. And if
uses
windows;
type
_FILETIMEA = record // Copy from windows.pas
dwLowDateTime: DWORD;
dwHighDateTime: DWORD;
end;
var
x: _FILETIMEA;
y: windows._FILETIME;
begin
x := y;
end;
Will cause a compilation error (in line x:=y; [DCC Error] ... E2010 Incompatible types: 'windows._FILETIME' and '_FILETIMEA'), though type _FILETIMEA = Windows._FILETIME ?
Delphi doesn't support duck typing. You have 2 different records. They just look a like, but they are 2 different types for the compiler. If you want to assign the two variables you have to type cast them what is possible because they have the same size.
x := _FILETIMEA(y);
Because you're not doing the same thing. :) Delphi is strongly typed (there aren't many exceptions - almost everything has a specific type).
type
myInt = Integer;
myNewInt = -2147483648..2147483647;
There is nothing incompatible about these types. They're both the same type (integer), and support the same range of values. There's no conflict about allowing the assignment of one to the other.
// Your unit name
unit GuTypes;
Type
_FILETIMEA = record // Copy from windows.pas
dwLowDateTime: DWORD;
dwHighDateTime: DWORD;
end;
This is a new type, defined by your code (even though it's identical to the one in Windows.pas, it's not the same). It's GuTypes._FileTimeA, which is not the same as Windows._FileTimeA.
This is actually a good thing, because it allows different units to use the same typenames without conflict; you can prefix with the unit name as a "namespace" to clarify which one you mean to use. For instance, if you have yours defined in GuTypes, and Windows has one declared there, in a third unit MyAppCode, you can do this safely:
var
GFileTime: GuTypes._FILETIMEA;
WFileTime: Windows._FILETIMEA;
Even if one of the types changes, your code is safe from side effects, because the two types can't accidentally be interchanged.
You can typecast to force the assignment to work, but that's usually a bad idea; it defeats the whole purpose of using a strongly typed language. Typecasting tells the compiler "I'm smarter than you, and I know this is wrong but I want you to ignore it and do it anyway."
A better solution would be either to declare your variable as you did the y in your example (Windows._FILETYPEA), or to declare a compatible type (type TMyFileTimeA = Windows._FILTETIMEA;).
See the XE2 Wiki Pages on Type Compatibility and Identity. Look specifically at the Assignment Compatibility section.

Delphi - records with variant parts

I want to have a record (structure) with a 'polymorphic' comportment. It will have several fields used in all the cases, and I want to use other fields only when I need them. I know that I can accomplish this by variant parts declared in records. I don't know if it is possible that at design time I can access only the elements I need. To be more specific, look at the example bellow
program consapp;
{$APPTYPE CONSOLE}
uses
ExceptionLog,
SysUtils;
type
a = record
b : integer;
case isEnabled : boolean of
true : (c:Integer);
false : (d:String[50]);
end;
var test:a;
begin
test.b:=1;
test.isEnabled := False;
test.c := 3; //because isenabled is false, I want that the c element to be unavailable to the coder, and to access only the d element.
Writeln(test.c);
readln;
end.
Is this possible?
All variant fields in a variant record are accessible at all times, irrespective of the value of the tag.
In order to achieve the accessibility control you are looking for you would need to use properties and have runtime checks to control accessibility.
type
TMyRecord = record
strict private
FIsEnabled: Boolean;
FInt: Integer;
FStr: string;
// ... declare the property getters and settings here
public
property IsEnabled: Boolean read FIsEnabled write FIsEnabled;
property Int: Integer read GetInt write SetInt;
property Str: string read GetString write SetString;
end;
...
function TMyRecord.GetInt: Integer;
begin
if IsEnabled then
Result := FInt
else
raise EValueNotAvailable.Create('blah blah');
end;
Even if I heard that by original Niklaus Wirth's Pascal definition all should work as you expected, I saw no such behaviour in Delphi, starting from its ancestor, Turbo Pascal 2.0. Quick look at FreePascal showed that its behaviour is the same. As said in Delphi documentation:
You can read or write to any field of any variant at any time; but if you write to a field in one variant and then to a field in another variant, you may be overwriting your own data. The tag, if there is one, functions as an extra field (of type ordinalType) in the non-variant part of the record."
Regarding your intent, as far as I understood it, I would use two different classes, kind of
a = class
b : Integer
end;
aEnabled = class(a)
c: Integer
end;
aDisabled = class(a)
d: String //plus this way you can use long strings
end;
This way you can get some support from IDE's Code Editor even at designtime. More useful, though, is that it will be much more easier to modify and support later.
However, if you need quick switching of record variable values at runtime, #David Heffernan's variant , to use properties and have runtime checks, is more reasonable.
The example given is NOT a variant record, it includes all the fields all the time.
A true variant record has the variants sharing the same memory. You just use the "case discriminator: DiscType of ..... " syntax, no need for a separate field telling you what variant is active.

Arrays as result type and input for functions

in delphi7 i have a function that i need to return a array as a result type b
"function createsbox(key:tkey):array[0..255] of byte;" this is not allowed it is expecting a "identifyer expected but array found" is the error throw up. seems to work fine if i declare a record type of array but it seems pointless to do this for one function.
The issue is that you're not allowed to create a new type in a function declaration. But that's what you're doing when you specify the return type as array[0..255] of Byte. Instead, declare a named type, and then use it for the return type:
type
TSBox = array[0..255] of Byte;
function CreateSBox(const Key: TKey): TSBox;
There is a subtle reason for that, and it is in Pascal two array type declarations are not the same "type" regardless of how identical their declaration are, and thereby not assignment compatible. If you write:
var
A: array[1..10] of Integer;
B: array[1..10] of Integer;
A and B are different types. If you write
A := B;
The code won't compile, A and B are different types.
Thereby, if you write
var
A: array[1..10] of Integer;
...
function Foo(...): array[1..10] of Integer;
You're actually declaring a type for the function result - that type would be pretty useless because you could not assign it to A or any array no matter how it s declaration is, for example:
A := Foo(...);
would not work even if the compiler would let you to declare a function that way.
The only way to have a useful function result type thereby is to use a type already declared. Only open arrays are an exception to this rule, but they can be used only as function parameters, not as the result.
okay type TSBox = array of Byte works fine, but using this new type in 2 or more units, could be tricky. 'Cause u will get an error message "incompatible types".
There's a partial solution for this situation. U can inform the compiler which unit has that type with this notation: unit.type;
Example:
Imagine there I have a unit called Web, where I declare the type TDownload. I could do something like this:
var fileUrl : Web.TDownload;
In this case the compiler will understand that u r using the same type.
However, and thats my question for u all: what if u don't want to put that unit in the uses clausule for whatever - circular reference, poo good practices, etc.. What can I do to avoid this problem?

Resources