So lots of languages have a pattern where you do something like this:
object = Create_Object().Set(1).Set(2).Set(3);
(I believe this originated in Smalltalk.) This works because the Set() method returns a reference to its receiver.
Can I do anything like this in Ada?
Approaches I've tried which don't work include:
-- doesn't work because the returned object is a read-only copy of the receiver
function Set(self: in out Object) return Object;
-- doesn't work because I can't return an access to a local variable
function Set(self: in out Object) return access Object;
-- looks like it works until I realise that it's not a method, and isn't doing
-- dynamic dispatch [*]
function Set(self: access Object) return access Object;
Can this be done?
I think a function with this profile will do the job for you:
function Set (Target : in Instance;
New_Item : in Integer) return Instance;
Having written a full package around that function declaration, I can write:
Object := Set (1).Set (2).Set (3);
Object.Show;
and get the output:
{1, 2, 3}
I've pushed the full sources to http://repositories.jacob-sparre.dk/miscellaneous-ada-2005-examples/.
Your third proposal is the correct approach, and you are in fact defining a primitive operation when you type "access" for the parameter. Here is an example, which uses two tagged types to show where dispatching occurs, and uses chaining of method calls. I have provided a procedure for convenience, as well, since otherwise you would have to use a temporary variable. I have used Create_Object, but an explicit call to "new", but that's of course the same thing. In addition, I have shown an example where Ada will not do dynamic dispatching when it knows statically the types involved. This is actually a nice features (performance-wise), even if indeed it requires care (and even experts are bitten every now and then :=)
with Utils; use Utils;
procedure Chain is
O : access Object := new Object;
C : access Child := new Child;
begin
O.Add (1).Add (2);
C.Add (3).Add (4);
end Chain;
package Utils is
type Object is tagged null record;
function Add (Self : access Object; Val : Integer) return access Object;
procedure Add (Self : access Object; Val : Integer); -- for convenience
type Child is new Object with null record;
overriding function Add (Self : access Child; Val : Integer) return access Child;
overriding procedure Add (Self : access Child; Val : Integer); -- for convenience
end Utils;
with Ada.Text_IO; use Ada.Text_IO;
package body Utils is
function Add (Self : access Object; Val : Integer) return access Object is
begin
Put_Line ("func Object.Add" & Val'Img);
Self.Add (Val); -- static call, not dynamic dispatch
return Self;
end Add;
procedure Add (Self : access Object; Val : Integer) is
begin
Put_Line ("proc Object.add" & Val'Img);
end Add;
overriding function Add (Self : access Child; Val : Integer) return access Child is
begin
Put_Line ("Child.Add" & Val'Img);
Self.Add (Val); -- static call, not dynamic dispatch
return Self;
end Add;
overriding procedure Add (Self : access Child; Val : Integer) is
begin
Put_Line ("proc Child.Add" & Val'Img);
end Add;
end Utils;
The output of this program is:
func Object.Add 1
proc Object.add 1
proc Object.add 2
Child.Add 3
proc Child.Add 3
proc Child.Add 4
Edit: As discussed in the comments, it is recommended to always use overriding to make sure the procedure is indeed overriding, not using a different profile. An access parameter does create a primitive operation.
From paragraph 18, function [formal] parameters can only use mode 'in' (read-only). So the input object does not get modified.
18 {parameter mode} The parameter mode of a formal parameter conveys the direction of information transfer with the actual parameter: in, in out, or out. Mode in is the default, and is the mode of a parameter defined by an access_definition. The formal parameters of a function, if any, shall have the mode in. [Ada 2005]
So I do not think that the language supports it as shown in other languages.
However, if you don't mind copies, we can save the result back in the object, but that's not the same as those other languages that you reference.
obj := obj.set("this").set(123).set(some_other_type);
You set "this" on a copy of 'obj' and return that copy. From that copy, we create a new copy and set 123 in that copy, which is in turn returned. Finally, that last result is copied and we set 'some_other_type' to it. That last copy is returned and we can save it in obj. Note that a well optimized implementation can avoid most if not all of those copies if the set() functions are small enough and inlined, but I would not could on it.
In retrospect, using procedures:
obj.set("this");
obj.set(123);
obj.set(some_other_type);
will do no copies at all.
Related
I think DELPHI language offers a very clear and beautiful implementation of data encapsulation
via the property methods and clear getter and setter functions.
I want to access a class string list strlst via a getter and setter functions.
Option #1 in the code sample works fine as expected , but option #2 calling the
Stringlist.Commatext function does actually not work.
Is this a DELPHI bug or did I miss something with respect to class design?
Target : fill class stringlist without creating an external Tstringlist class, just pass commatext.
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, classes;
type
TSimpleClass = class
FStrlst: TStringList;
private
procedure SetStrList(const Value: TStringList);
public
constructor Create;
property strlst: TStringList read FStrlst write SetStrList;
end;
{ TSimpleClass }
constructor TSimpleClass.Create;
begin
inherited;
FStrlst := TStringList.Create;
end;
procedure TSimpleClass.SetStrList(const Value: TStringList);
var
i: Integer;
begin
Writeln('class getter / setter function is called ');
for i := 0 to Value.Count - 1 do
begin
FStrlst.Add(Value[i]);
end;
writeln ('content of internal strlist : ' + FStrlst.CommaText )
end;
begin
var
aSimpleClass: TSimpleClass;
var
testLst: TStringList;
try
aSimpleClass := TSimpleClass.Create;
try
// option #1 is working
testLst:=TStringList.Create;
testLst.CommaText := 'a,b,c';
aSimpleClass.strlst := testLst;
// option #2 not working ... this code should also call the
// setter function
aSimpleClass.strlst.commatext := 'd,e,f';
finally
aSimpleClass.Free;
end;
writeln ('done sample code !');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
cmd line window sample output
output of this sample code
Is this a DELPHI bug or did I miss something with respect to class design?
No, it is not a Delphi bug, but a misunderstanding on your part. You say "property, getter and setter function not called", and I understand you refer to getter and setter for aSimpleClass.strlst.
You have defined direct read access (of FStrLst) for TSimpleClass.strlst, thus there is no getter to be called when reading (as in // option #2), and aSimpleClass.strlst refers directly to FStrLst.
The CommaText property of TStringList has its own setter and that is called in // option #2
Edit
Or, if your intention is to not expose the TStringList at all, but only its CommaText property, that is OK as follows:
Make sure the TStringList is private, as well as a getter and setter for CommaText, and add the property CommaTxt
TSimpleClass
private
FStrlst: TStringList;
...
function GetCommaText: string;
procedure SetCommaText(s: string);
public
property CommaTxt: string read GetCommaText write SetCommaText;
...
end;
Implement the getter and setter:
function TSimpleClass.GetCommaText: string;
begin
result := FStrlst.CommaText;
end;
procedure TSimpleClass.SetCommaText(s: string);
begin
FStrLst.CommaText := s;
end;
Now you can access the CommaTxt property without exposing the TStringList.
The reason why your option #2 doesn't fire the setter mthod is because in your option #2 you are not assigning any value to your strlst but insteadyou are interacting with the object that is being returned by the said property.
You see when you call aSimpleClass.strlst.commatext := 'd,e,f'; what happens is next:
Getter method of the strlst property is being called in order to return the reference to the TStringList object (the object thype that is defined by property)
Then the string value of d,e,f is assigned to the ComaText property that belongs to the String List whose reference was returned by your property getter method.
Now if at this time the TStringList that is part of your class would still not have been created an Access Violation would have been raised since you would be trying to access property of a non existent object.
How to pass a method function to another Object? Is such a thing possible in Delphi?
For example, I am trying to create a PopupMenu in a DataModule, since this PopupMenu will be used in multiple places in the application.
The PopupMenu will use a particular function for example: GetCustomer() which returns TCustomer, on which various actions can be performed.
Each Form/Frame that is creating and using the DataModule, will want to pass it a custom implementation of GetCustomer().
I was wanting to write something like this:
TGetCustomerFunc = function: TCustomer of object;
and then create a field/property on the DataModule:
TPopupDataModule = class(TDataModule)
public
{ Public declarations }
GetCustomer: TGetCustomerFunc;
end;
GetCustomer would be assigned a method by each frame that is creating the PopupDataModule, with its own particular implementation.
However, function: TCustomer of object; is not valid syntax.
Any suggestions on how to go about this?
Your initial question: How to pass a method function to another Object?
You can declare a type for your function or procedure prototype and then pass that around as you would any other variable. Look at the use of TNotifyEvent all over the RTL, VCL and FMX for examples of how to do that.
Is such a thing possible? Yes.
It's not clear to me exactly what your use case is, and so I'm not sure of the best example to give you. You say that you want to use a TDataModule to create a TPopupMenu, which seems clear enough, but you then state that the TCustomer needs to be provided by TFrame and TForm objects that call it.
Why not just pass it the TCustomer object? It seems a bit convoluted to pass a function to get an object instead.
If I have understood what you are looking for you will want something like this:
interface
type
TCustomer = class(TObject);
TGetCustomerFunc = function(): TCustomer of Object;
TPopupDataModule = class(TDataModule)
public
function BuildPopupMenu(fnGetCustomer: TGetCustomerFunc): TPopupMenu;
...
end;
implementation
function TPopupDataModule.BuildPopupMenu(fnGetCustomer: TGetCustomerFunc): TPopupMenu;
var
pCustomer: TCustomer;
begin
if(Assigned(fnGetCustomer)) then
begin
pCustomer:=fnGetCustomer();
...
end;
end;
To call it you can then pass the address of a function in the object that's calling it. So if you define in your form:
protected
function GetTheCustomer(): TCustomer;
You can pass this routine to the TPopupDataModule like this:
{ pPopupDM:=TPopupDataModule.Create(Application); // called previously }
pPopupDM.BuildPopupMenu(Self.GetTheCustomer); // in a Form method Self is the form
Highlight
This constructor:
constructor TCoords.Create(const AX, AY: Integer);
begin
TCoords.Create(Point(AX, AY));
end;
has been confirmed malfunctioning in both Linux Lazarus 2 and Windows Delphi XE6.
Could this be a bug?
I am new to OOP in Lazarus / Delphi, excuse possible newbie error. Thank you.
I can't seem to understand why the following sequence of Lazarus / (Delphi-like) custom, very basic, an object will not work. I'm trying to debug this for hours already, since then I found:
What works:
Calling the one constructor without an argument, and calling the one with TPoint argument directly.
What doesn't:
Calling this one:
constructor Create(const AX, AY: Integer);
However, I found this would work - But only if if called without its class name inside the constructor. Why is that causing trouble?
Declarations
// WORKS - this creates instance of TCoords initialized to PointOutOfReach
constructor Create; reintroduce;
// WORKS - this creates instance of TCoords initialized to user coordinates
constructor Create(const ACoords: TPoint);
// DOES NOT WORK, strangely returns Point(0, 0), if called with class name
// WORKS only if called without class name - confusing or error on my side?
constructor Create(const AX, AY: Integer);
Calls
// OK - WORKING
NewCoords := TCoords.Create;
NewCoords.X:=12;
NewCoords.Y:=120;
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
// OK - WORKING
NewCoords := TCoords.Create(Point(12, 120));
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
// NOT WORKING as expected
NewCoords := TCoords.Create(12, 120);
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
Coords unit with TCoords object definition
unit Coords;
{$mode objfpc}{$H+}
interface
uses
Classes;
type
// Flexible X,Y coordinates object.
TCoords = class(TObject)
// these declarations are accessible within this unit only
private
// this is the variable we are working with
FCoords: TPoint;
// property for this function is unnecessary, but I like it as it is
function IsInitialized: Boolean;
// these declarations are accessible to all
public
// this creates instance of TCoords initialized to PointOutOfReach
constructor Create; reintroduce;
// this creates instance of TCoords initialized to user coordinates
constructor Create(const ACoords: TPoint);
// THIS ONE DOES NOT WORK, strangely returns Point(0, 0)
constructor Create(const AX, AY: Integer);
// this indicates if instance was initialized or not by the user
property Initialized: Boolean read IsInitialized;
// this works directly with private FCoords variable storing coordinates
property P: TPoint read FCoords write FCoords;
// these two are shortcuts for X,Y coordinates' direct access
property X: Integer read FCoords.X write FCoords.X;
property Y: Integer read FCoords.Y write FCoords.Y;
end;
implementation
var
// this gets initialized when loading this unit
PointOutOfReach: TPoint;
constructor TCoords.Create;
begin
// this is the same as `inherited`, but I like to be explicit
inherited Create;
// since called without argument, we have to ensure, there is some nonsense
FCoords := PointOutOfReach;
end;
constructor TCoords.Create(const ACoords: TPoint);
begin
// this is the same as `Create`, but I like to be explicit
TCoords.Create;
// in the previous mandatory call we have initialized FCoords already
// but to PointOutOfReach; here we overwrite it with user coordinates
FCoords := ACoords;
end;
constructor TCoords.Create(const AX, AY: Integer);
begin
// this is the same as `Create(TPoint)`, but I like to be explicit
// TCoords.Create(Point(AX, AY));
// Why can't I call the above, shouldn't it be the very same?
Create(Point(AX, AY));
end;
function TCoords.IsInitialized: Boolean;
begin
// this returns True in case FCoords has been initialized
// initialized means here for the FCoords point to be different from PointOutOfReach
// achieved either by calling `Create(APoint)`, or later overwriting PointOutOfReach
Result := FCoords <> PointOutOfReach;
end;
initialization
// initialize PointOutOfReach to "impossible" coordinates when loading unit
PointOutOfReach := Point(MAXINT, MAXINT);
end.
Thank you in advance, I myself can't seem to see the difference between those two.
Major error corrected - no change
Missing overload; in constructors' declarations has been corrected. Still getting 0,0 coords from the last constructor, sadly.
David Heffernan stated the reason for my approach not to work in a comment, let me quote:
TCoords.Create(Point(AX, AY)) creates a new instance. When you do this inside a constructor you now have two instances. Replace it with Create(Point(AX, AY)).
Thank you kindly for the explanation!
If even solved, I think a better approach will be not chaining those constructors.
Applying this rule works, working snippet with unchained constructors:
constructor TCoords.Create;
begin
inherited Create;
// since called without argument, we have to ensure,
// there are some corner-case coordinates, so that we can
// differentiate between a [0:0] and uninitialized state
FCoords := PointOutOfReach;
end;
constructor TCoords.Create(const ACoords: TPoint);
begin
inherited Create;
FCoords := ACoords;
end;
constructor TCoords.Create(const AX, AY: Integer);
begin
inherited Create;
FCoords := Point(AX, AY);
end;
PS: For the code to work, so far I see no need to apply "setters" as in Nasreddine Galfout's answer.
It is the way you are declaring the properties X and Y change them to this
property X: Integer read FCoords.X write SetCoordX;
property Y: Integer read FCoords.Y write SetCoordY;
procedure TCoords.SetCoordX(const Value: Integer);
begin
FCoords.X := Value;
end;
procedure TCoords.SetCoordY(const Value: Integer);
begin
FCoords.Y := Value;
end;
This has to do with how Delphi does assignment of properties. In your case you are assigning to an implicit variable added by the compiler when retrieving the value of X.
I can't seem to remember where I read about this I will find it and edit my answer
How can I change (If possible) a value in a class, whilst inside another class procedure. Example:
Class1 : Class
Private
Random : Integer;
public
end;
Class2 : Class
Private
Public
Procedure DoSomething();
end;
Procedure Class2.DoSomething();
Begin
//Do Something
If ... then
Change Random to ...
end;
It depends. You first have to think of the reason why the visibility is separated. private methods are intended for use within a class, while public methods are intended for use outside of a class.
In your particular example, you are not referencing a class. Instead, I will use your example to demonstrate the following:
type
TMyObject = class(TObject)
private
FNumber: Integer;
function GetNumber: Integer;
procedure SetNumber(Input: Integer)
public
property Number: Integer read GetNumber write SetNumber;
end;
function TMyObject.GetNumber: Integer;
begin
Result := FNumber;
end;
procedure TMyObject.SetNumber(Input: Integer);
begin
FNumber := Input;
end;
As you can see, I've added a property called Number which uses GetNumber to obtain the value and SetNumber to assign it. Use of such methods is almost always coupled with the use of a property in this manner.
Coming back to your original question, as I said, it depends. Your property setter might perform some additional code than just assigning the value, for example. In such a case, from within your class, you may need to set this value through its private FNumber field instead, so that the additional setter code isn't called.
So in short, the answer is no. It is not absolutely necessary to access private members through getters and setters from within the class. However, from outside the class, then yes, it will be necessary.
On the other hand, you could also declare the member under strict private, which would force the class member to only be visible from within the class itself, and not from anywhere else in the same unit.
Read more here about visibility of class members.
strict private and strict protected
declarationsection were introduced in D2007. Members declared in a
strict private section are not accessible from other classes even in the
same unit, as this example shows:
type
TMyClass = class
private
MyPrivateField : Integer;
strict private
MyStrictPrivateField : Integer;
public
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
MyClass : TMyClass;
begin
MyClass := TMyClass.Create;
MyClass.MyPrivateField := 0; // compiles ok
MyClass.MyStrictPrivateField := 1; // does not compile
// fails with E2361, "Cannot access private symbol TMyClass.MyStrictPrivateField
end;
Note that not only does this fail to compile, but in Seattle at least,
MyStrictPrivateField does not show up in the autocomplete list for
TMyClass (with the insertion point in FormCreate), unlike MyPrivateField,
which does.
So if I have an Object and needs to call one method from another within that Object, how do I construct that call?
TestObject = {}
TestObject.__index = TestObject
function TestObject.new()
local self = setmetatable({}, TestObject)
self.value = init
-- a count [integer] of something. Not important what
self.counter = 99
return self
end
function TestObject:getCount()
return self.counter
end
function TestObject:getCountPlus(add_value)
-- HERE HOW SHOULD THIS BE FORMATED??
return self.getCount() + add_value
end
And using this Object would be something like this:
local testObject = TestObject.new()
testObject:getCountPlus(1)
which should result in 100.
The getCount() needs to know what instance it is in. When you write
function TestObject:getCount()
it is same as writing
function TestObject.getCount(self)
(note the colon changed to dot). So calling self.getCount() is like calling getCount with self=nil. Do self:getCount(), which is same as self.getCount(self). This may seem odd: why does interpreter not provide self automatically? It's just the way the language was designed: it only provides self automatically with the : notation.