Delphi RTTI makes it very easy to enumerate and extract information about an object's properties, fields and methods at runtime.
However, is it possible to add a property and field to an object at runtime?
I realize that this would involve changes to the VMT if it depends on a getter / setter, which is why i mentioned fields, so the injected property could be bound to said field and thus avoid appending nodes to the VMT tree.
This might seem like a silly question, but I am working on a layout designer. The objects you can move around and position represents web elements, that all have different properties.
It would save me a lot of time if I somehow could populate these proxy design objects with the actual properties from the web elements. Then I could use a normal object inspector directly on them, rather than having to manually populate the inspector rows.
Serialization would also be easier since I could use the standard Delphi way, as opposed to hand writing IO code.
I doubt this is possible without some form of hack, but it's always best to ask.
Delphi is a statically typed language in its root. However, you can use variants - custom variants to be precise to create dynamic properties.
type
TMyCustomObject= class(TInvokeableVariantType)
public
procedure Clear(var V: TVarData); override;
procedure Copy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean); override;
function GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean; override;
function SetProperty(const V: TVarData; const Name: string; const Value: TVarData): Boolean; override;
end;
You can implement few virtual methods in the above code and get the desired results.
Here is a tutorial:
http://www.uweraabe.de/Blog/2010/08/07/a-magical-gathering-part-2/
An alternative approche would be to not rely on straight RTTI, but rather on an intermediate description layer.
You could initialize the intermediate layer from the RTTI for what's in the RTTI, and add extras for your dynamic properties.
This would also allow you to shield your layout designer from the RTTI quirks. Most of what the RTTI expose is very low level, and a lot of it is "unsafe" in that it can trigger unsafe behaviors in objects (and thus crash your designer).
This is especially relevant if you dot not have 100% control over the objects to fix their issues, which I guess you do not, as otherwise you would not need dynamic properties.
During interactive design invalid property values are the norm: partial inputs, typing errors, incoherent settings, etc. all these will require some kind of context-specific sanitizing, which an intermediate layer can provide.
In the Delphi IDE, the design-time sanitizing is basically up to each individual component's implementation and the csDesigning component state. While it is simple, there is very limited feedback a component can provide on incoherent or invalid properties.
Related
Is there any way to get the names of the parameters of a given method of a class/object in an array/delimited string in Delphi 7? Somehow like this:
var
s : string;
aSL : TStringList;
begin
...
// using rtti calls in TMethodUtility.collectParamNames
s := TMethodUtility.collectParamNames( TStringList.addObject );
// or
TMethodUtility.collectParamNames( TStringList.addObject, aSL );
...
end;
Thanks in advance!
It is technically possible (otherwise the IDE's Form Designer could not generate code for component event handlers at design-time), however, there are important caveats that will hinder your goal (and make the particular example in your question impossible to resolve):
the necessary RTTI is only generated for properties that are declared as published (such as component events). RTTI is not generated for methods themselves (even published ones), or for properties that are not published.
TObject has a public MethodAddress() method for getting the memory address of a published method (the DFM streaming system uses this when hooking up event handlers), however you cannot get the necessary RTTI from a method pointer alone.
IF you can match a given method pointer to the value of a published event, then you can extract the parameter names from the event's RTTI. Obtain a TypInfo.PPropInfo pointer for the event using the TypInfo.GetPropInfo() function, then pass its PropType field value to the TypInfo.GetTypeData() function to get a TypInfo.PTypeData pointer, and then you can iterate through its ParamList field (which is an array of records containing ParamName and TypeName fields).
See the following blog article on this topic for more details:
Getting the parameters of published methods.
For what you are attempting, a general-purpose solution would require Extended RTTI that was introduced in Delphi 2010 and thus is not available in Delphi 7. Extended RTTI is not limited to published items, and is much more detailed than what the old-style RTTI provides.
I'm trying to find out if there is a way to do things similar to Delphi's enhanced RTTI features.
As far as I know FPC doesn't provide RTTI features which appeared in Delphi since Delphi 2010. But I'd like to find some way to do a few tricks during runtime.
Using typinfo unit in FPC I can do such things as:
get Object published property list - via getPropList from typinfo unit;
get/set value of the Object's published property - via GetPropValue(...): Variant/SetPropValue(...Value: Variant);
get published method - via MethodAddres;
But I haven't found a way to do things like:
call methods;
call constructors, or create Objects;
Update: the problem with constructors is much like methods one - I want to have a way to pass different params in it:
// concept of code
type
TClass = class of TObject;
TMyClass1 = class
public
constructor Create(Param1: Integer; Param2: string); override;
end;
TMyClass2 = class
public
constructor Create(ObjectParam: Object); override;
end;
TParams = array of Variant;
var
Classes: array of TClass
Instances: array of Object;
ParamArray: array of TParams;
...
For I := 0 to Count-1 do
begin
LocalConstructor := #(Classes[I].Create);
Instances[I] := CallConstructor(LocalConstructor, ParamArray[I]);
end;
So I need to call constructor without knowing its signature.
So my problem is to call an Object's method and pass some parameters to it. It could look like function CallMethod(Instance: Object; MethodName: String; Params: array of Variant): Variant;
If I'm not mistaken it could be solved via Delphi's 2010+ RTTI. But before using enhanced Delphi's RTTI I'd like to understand is it possible in FPC.
In other words my current problem is pass arguments to a routine.
I know it can be done using this scheme:
type
TmyProc = procedure CallMe(x: byte);
...
var proc: TmyProc;
...
proc := pointerToFunc^;
proc(0);
But I need to implement it without knowing count and types of parameters (during compile time).
There are a few links related to the topic:
Delphi: Call a function whose name is stored in a string
http://www.swissdelphicenter.ch/torry/showcode.php?id=1745
The second article (http://www.swissdelphicenter.ch/torry/showcode.php?id=1745) describes a way to pass arguments to a routine imported from DLL by name. Which is almost that I need I suppose. But I'm not sure that way is reliable.
Maybe there's any library, which implements these things using "old" typinfo unit (without RTTI unit)?
Also I'm interested in a way to create some kind of universal event handlers - procedures which can be assigned to different events (with different sets of parameters) e.g.:
procedure myEventHandler(params: array of variant);
...
Button.OnClick := myEventHandler;
Button.OnMouseMove := myEventHandler;
is this possible? Or at least something similar to it?
You can call methods, even not published, using MethodAddress, but it's up to you to ensure correct argument list.
You can call constructors using metaclasses (class references), example of it could be seen in TCollection: you pass class of your collection item at runtime and then it can be created when needed. By defining abstract class with virtual (and probably abstract) constructor, you can come up with argument list you wish, some example here.
AFAIK there is no way to determine argument list at runtime, but if you design both the methods to call and caller itself, there are many ways you can implement similar behavior.
For example, you pass variant open array (Array of const), as it's done in Format(), so number of arguments and its type may vary. But even having one and only pointer as the argument, you sure can pass as many as you want, all you need to do is to come up with some class to which it will lead.
This Embarcadero article discussing memory issues for the XE7 IDE contains the following:
Be aware of the “Growth by Generics”
Another scenario that might depend on your application code and cause an increase of the memory used by the compiler and the debugger relates with the way generic data types are used. The way the Object Pascal compiler works can cause the generation of many different types based on the same generic definition, at times even totally identical types that are compiled in different modules. While we won’t certainly suggest removing generics, quite the contrary, there are a few options to consider:
Try to avoid circular unit references for units defining core generic types
Define and use the same concrete type definitions when possible
If possible, refactor generics to share code in base classes, from which a generic class inherits
The last item I understand. The first two I am less clear on.
Do these issues affect only IDE performance, or is there an impact on the size of the compiled code?
For instance, considering the second item, if I declare TList<Integer> in two separate units, will I get two separate chunks of code in each of those units in my executable? I certainly hope not!
Point 2. This refers to instantiating same generic type where possible. For instance using TList<Integer> in all places instead of having two generic types TList<Integer> and TList<SmallInt>.
Declaring and using TList<Integer> in several units will only include single copy of TList<Integer> in exe file. Also, declaring TIntegerList = TList<Integer> will result with same.
Generic bloat people are referring to relates to having complete TList<T> copy for each specific type you use even though underlying generated code is the same.
For instance: TList<TObject> and TList<TPersistent> will include two separate copies of TList<T> even though generated code could be folded to single one.
That moves us to Point 3. where using base class for common class code and then using generic classes on top of that to get type safety, can save you memory both during compilation and in exe file.
For example, building generic class on top of non generic TObjectList will only include thin generic layer for each specific type instead of complete TObjectList functionality. Reported as QC 108966
TXObjectList<T: class, constructor> = class(TObjectList)
protected
function GetItem(index: Integer): T;
procedure SetItem(index: Integer; const Value: T);
public
function Add: T;
property Items[index: Integer]: T read GetItem write SetItem; default;
end;
function TXObjectList<T>.GetItem(index: Integer): T;
begin
Result := T( inherited GetItem(index));
end;
procedure TXObjectList<T>.SetItem(index: Integer; const Value: T);
begin
inherited SetItem(index, Value);
end;
function TXObjectList<T>.Add: T;
begin
Result := T.Create;
inherited Add(Result);
end;
The code bloat they are talking about in the article (as it is about the out of memory issue in the IDE) is related to the generated DCUs and all the meta information that is held in the IDE. Every DCU contains all the used generics. Only when compiling your binary the linker will remove duplicates.
That means if you have Unit1.pas and Unit2.pas and both are using TList<Integer> both Unit1.dcu and Unit2.dcu have the binary code for TList<Integer> compiled in.
If you declare TIntegerList = TList<Integer> in Unit3 and use that in Unit1 and Unit2 you might think this would only include the compiled TList<Integer> in Unit3.dcu but not in the other two. But unfortunately that is not the case.
I have a business object that I would like to "connect" to my UI better. I've seen some partial solutions for making objects data-aware, but they all involved significant changes to my business object, including an extra layer of abstraction.
I've been looking into the improved RTTI in new versions of Delphi, and it looks very interesting and useful. I'm wondering if I could use it to programmatically inject new write methods for all properties.
The way this would work is that my TEdit descendant would by given a reference to an object property when the form is built. The TEdit would then insert a reference to itself in an attribute for that property (and of course remove itself on destructor or being given another reference). The TEdit would also ensure that the write method for the property is replaced by one that notifies the TEdit of changes after calling the original write method.
Is this feasible? The big show stopper would be that injecting a new write method isn't possible, hence the title for this question.
There are also potential problems with derived properties, but it should be possible to find a solution for that.
Your question already puts you ahead of me with programming skill so I'll just add how I might approach this:
If I were to try to write something like that I'd probably start with a TList for each field in your TBusinessObject. That list would be used to indicate what needed to be updated when you needed to push out a change.
So when the TEdit is created it would add itself to a list which was associated with a piece of data in your TBusinessObject. When the TBusinessObject updated that piece of data it would then run through the list of attached objects. It would see the TEdit and, knowing it was a TEdit, would run code to update the .Text. If I attached a TCaption then the code would update the .Caption.
The TEdit, as you indicated, would need to tell the TBusinessObject when it's value was updated. I guess this is the tricky spot - you could create a new TEdit and add in a TList to maintain who it should inform when it changes. If you used the .Tag to indicate a field number in the TBusinessObject then the OnChange (or whatever event) could then call something like TBusinessObject.FieldUpdate[TEdit.Tag, NewValue] which then triggers your business logic. That, in turn, might make the TBusinessObject update other fields, which may have their own TLists to fields to update.
Preventing circular updates would require that you have a way of updating a control without triggering events. For one program I wrote I had two methods to update the control: SetValue and ChangeValue. SetValue disabled any events (OnChange, OnValidate), updated the control's value and then reenabled the events. ChangeValue simply changed the value and allowed any of the control's events to fire as required.
There are probably slicker ways to do this but hopefully this gives you food for thought.
Possible to change property write methods programmatically using RTTI for creating object-aware controls?
No, it's not possible. RTTI gives you information, it doesn't give the ability to alter types at runtime.
The big show stopper would be that injecting a new write method isn't possible, hence the title for this question
In order for you to change this at runtime there should be something similar to an event handler that you can set. It's an easy concept, but it has some runtime overhead, both in call time (it would be an indirection where a direct call would normally suffice) and in terms of required memory (each property would require an extra TEvent style field). This is easy for you to implement if you need it, but it would be harmful if the compiler automatically generated such code for all classes "just in case".
If you're thinking of somehow patching the code in memory at runtime, that's not going to work and it would be, at best, unreliable.
In this post entitled Inducing The Great Divide, Cobus Kruger talked about business objects.
The solution he cooked is essentially compliant to your requirements:
Make use of advanced RTTI features introduced in recent Delphi version.
Separation of the business logic from presentation logic.
Any PODO (Plain Old Delphi Object) will do as business object !
The magic lays in the TObjectBinding class which ties any TWinControl with any business object.
Excerpt:
TObjectBinding = class
private
fCtx: TRttiContext;
fControlType: TRttiType;
fObjType: TRttiType;
fPropFieldMapping: TDictionary<TRttiProperty, TRttiField>; // Dictionary of object Properties & corresponding Fields
fControl: TWinControl; // The control (normally form)
fObj: TObject; // Object it represents.
procedure CreateMappings;
function FindField(Prop: TRttiProperty; out Field: TRttiField): Boolean;
function FieldClass(Field: TRttiField): TClass;
// Modify these to change the rules about what should be matched.
function IsValidField(Field: TRttiField): Boolean;
function IsValidProp(Prop: TRttiProperty): Boolean;
// Modify these to change the mappings of property type to VCL control class.
procedure AssignField(Prop: TRttiProperty; Field: TRttiField);
procedure AssignProp(Prop: TRttiProperty; Field: TRttiField);
// Used from AssignField/AssignProp. Extend these to support a wider range of properties.
function GetPropText(Prop: TRttiProperty): string;
procedure SetPropText(Prop: TRttiProperty; const Text: string);
public
constructor Create(Control: TWinControl; Obj: TObject);
destructor Destroy; override;
//
procedure Load;
procedure Save;
end;
I hope that this will be a good starting point for you.
I have some business objects written in Delphi with a custom scheme of database persistence which is finally working for my needs. Ok, great. Now it's time for GUI implementations. And here begins the problems.
How to bind my object to the GUI properly?
I cannot use Data Aware controls since I isolated all data access components into the ORM layer, so I start to write some "Object Aware" controls using the RTTI unit (I'm working with Delphi 2010), but I have the feeling I'm going the wrong way...
Some ideas on how to resolve this using only the VCL controls?
You have several patterns for linking ORM with User Interface.
See for instance the Model GUI Mediator pattern. In short, you write an observer which will reflect the ORM content into the UI components, and vice versa. This has been implemented for instance in the tiOpf framework for Delphi (this link has videos).
Another approach is to map your data at runtime: you design your form just like usual, then you fill the content in the OnShow event, then the "Save" or "OK" button will validate then save the content into the ORM record. This is what is done in the main Sample application of our framework. Easy to code in this simple sample, but could lead into spaghetti code if you have a lot of fields and validation to operate.
The last approach is to let your ORM creates the form.
In our framework, you can define some UI properties about each table, in a dedicated structure. Then a single unit will create a form with all editable fields of your ORM object. Links to other records will be displayed as a combo box, booleans as checkboxes, sets as radioboxes, and so on. Then the filtering (e.g. trim a text field from spaces on left or right side) and the validation (e.g. ensure that a field value is unique or a valid IP address) is handled not in the UI part, but in the business logic itself, i.e. the ORM.
IMHO it's mandatory to keep a true multi-tier architecture. That is, the UI has to rely mostly on the business logic. For instance, data validation must be part of the ORM, not of the UI. For instance, if you decide to add a web client to your Delphi client application, you won't have to code the validation another time: it will be common to both clients, separated from the UI implementation details.
What you could do (though I have no code samples) is use a combination of
class helpers or interceptor classes
binding interfaces for single domain objects and/or domain object lists
Class helpers have the disadvantage that they are not officially supported and you cannot add any fields to the class you are helping.
Interceptor classes are simply descendant classes with the same name as their ancestor:
uses
stdctrls;
type
TButton = class(stdctrls.TButton)
end;
You can put interceptor classes in their own unit and use that whereever you want. Just make sure these units are included AFTER the standard unit, so your descendant is used at run time.
Benefit of interceptor classes:
You can continue to design your UI using standard VCL or third party controls.
You get all the advantages of descendants.
You do not need to create or install your own controls.
No need for special mapper classes or use of RTTI.
Easily (well, relatively easily) integrated into a (DUnit-) testable user interface along the lines of Julian Bucknall's article on this in the (distinct) Delphi Magazine as referred to in this question/answer: Unit-testing mouse event handlers
Pseudo sample of interceptor control with binding interface / command interface:
uses
stdctrls;
type
ICommandAction = interface(IInterface)
function IsEnabled: Boolean;
procedure Execute;
procedure Update;
end;
IBindSingle = interface(IInterface)
function GetValueFromControl: string;
procedure LoadValueIntoControl(const aValue: string);
end;
TButton = class(stdctrls.TButton, ICommandAction)
protected
function IsEnabled: Boolean;
procedure Execute;
procedure Update;
end;
TEdit = class(stdctrls.TEdit, IBindSingle)
function GetValueFromControl: string;
procedure LoadValueIntoControl(const aValue: string);
end;
implementation could be along the lines of:
function TButton.IsEnabled: Boolean;
begin
Result := Self.Enabled;
end;
procedure TButton.Execute;
begin
Self.Action.Execute;
end;
procedure TButton.Update;
begin
Self.Action.Update;
end;
function TEdit.GetValueFromControl: string;
begin
Result := Self.Text;
end;
procedure LoadValueIntoControl(const aValue: string);
begin
Self.Text := aValue;
end;
My current customer have made their own "mapper" classes in the past (before I came).
Their data objects have fields (which are objects), and you can map these fields to a control. I extended the framework by using a MVC-like approach:
edtTarraCode: TAdvEdit;
procedure TframTarraTab.InitMapping;
begin
...
Mapper.AddMapping(edtTarraCode, Controller.DataModel.tarID);
...
end;
Per control a simple "mapping" class is created:
TMappingAdvEdit = class(TBaseEditMapping)
protected
procedure InitControl; override;
procedure AppData2Control; override;
procedure Control2AppData; override;
end;
No rocket sience, and maybe better solutions are available in the mean time (this worked in D6 and lower :-) ) but it works good enough for the customer.
Btw: also a data object generator is used. So if a field changes in the database (for example tarra.tarid is changed into tareID) we get a compiler error because "tarid" does not longer exist. This works much better than "fixed string" mapping (runtime errors).
There's currently no way to do this using only VCL controls. I know that Lazarus has a set of RTTI-based data-aware controls; you might want to look at them for some basic ideas. But it's more difficult than you might think at first. For example, unlike a dataset, an object has no built-in signaling mechanism when its members' values change. Which means that unless your data-binding controls own the object completely and nothing else has access to it, it's possible for some other code to change some value and then that change doesn't get reflected in the UI.
I've heard various things from the Delphi team in the last few years about extending the object model or the RTTI model to allow for better data binding, but whatever that's about is still a few years out.
Take a look at EverClassy Dataset at http://www.inovativa.com.br. It may meet your needs. EverClassy Dataset is a Delphi dataset designed to be populated by objects instead records from a database system.
With this component you will have the chance to interoperate your domain objects with dataware componentes, what will give you great power to build your GUI.