Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2011-03

Read-only global references

12 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Mark Morgan Lloyd

Where a unit exports an instance of an object, what's best practice for making this read-only? I'm referring to the object reference itself here, not properties of the object.

Where I've done this in the past I've used a function:

interface
function InitText: TFormattedString;
implementation
var	xInitText: TFormattedString= nil;
function InitText: TFormattedString;
begin
   result := xInitText
end { InitText } ;

Is there a better way using e.g. global properties that doesn't necessitate both a property and an explicit function?

property InitText: TFormattedString read xInitText;

The obvious problem here is that xInitText is a forward reference to an unexported variable.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]
Marcos Douglas
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 27 lines
Where a unit exports an instance of an object, what's best practice for making this read-only? I'm referring to the object reference itself here, not properties of the object. Where I've done this in the past I've used a function: interface function InitText: TFormattedString; implementation var     xInitText: TFormattedString= nil; function InitText: TFormattedString; begin  result := xInitText end { InitText } ; Is there a better way using e.g. global properties that doesn't necessitate both a property and an explicit function? property InitText: TFormattedString read xInitText; The obvious problem here is that xInitText is a forward reference to an unexported variable.
Make a function to return a global variable does not make it read only.
If I use your function like this:
o := InitText;
o := nil;  //this is the same xInitText = nil

Marcos Douglas

Mark Morgan Lloyd
to Marcos Douglas
Marcos Douglas wrote 34 lines
On Sat, Mar 5, 2011 at 8:24 AM, Mark Morgan Lloyd wrote: > Where a unit exports an instance of an object, what's best practice for > making this read-only? I'm referring to the object reference itself here, > not properties of the object. > > Where I've done this in the past I've used a function: > > interface > > function InitText: TFormattedString; > > implementation > > var xInitText: TFormattedString= nil; > > function InitText: TFormattedString; > > begin > result := xInitText > end { InitText } ; > > Is there a better way using e.g. global properties that doesn't necessitate > both a property and an explicit function? > > property InitText: TFormattedString read xInitText; > > The obvious problem here is that xInitText is a forward reference to an > unexported variable. Make a function to return a global variable does not make it read only. If I use your function like this: o := InitText; o := nil; //this is the same xInitText = nil

Disagree. You are changing the value of o, not of xInitText. xInitText is initialised once somewhere in the unit exporting it, subsequently its properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...).

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]
Marcos Douglas
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 42 lines
Marcos Douglas wrote: > > On Sat, Mar 5, 2011 at 8:24 AM, Mark Morgan Lloyd > wrote: >> >> Where a unit exports an instance of an object, what's best practice for >> making this read-only? I'm referring to the object reference itself here, >> not properties of the object. >> >> Where I've done this in the past I've used a function: >> >> interface >> >> function InitText: TFormattedString; >> >> implementation >> >> var     xInitText: TFormattedString= nil; >> >> function InitText: TFormattedString; >> >> begin >>  result := xInitText >> end { InitText } ; >> >> Is there a better way using e.g. global properties that doesn't >> necessitate >> both a property and an explicit function? >> >> property InitText: TFormattedString read xInitText; >> >> The obvious problem here is that xInitText is a forward reference to an >> unexported variable. > > Make a function to return a global variable does not make it read only. > If I use your function like this: > o := InitText; > o := nil;  //this is the same xInitText = nil Disagree. You are changing the value of o, not of xInitText. xInitText is initialised once somewhere in the unit exporting it, subsequently its properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...).

You right... but not TOTALY.
I made a little program to explain that, but now I'm confusing, see:

program ref;
{$mode objfpc}{$H+}
uses
  sysutils;
type
  TObj = class
  public
    i: integer;
  end;
var
  o1: TObj = nil;
  o2: TObj = nil;
function getobj: TObj;
begin
  if not Assigned(o1) then
    o1 := TObj.Create;
  o1.i := 1;
  result := o1;
end;
begin
  o1 := getobj;
  o2 := o1;
  writeln('o1.i: ', o1.i);
  writeln('o2.i: ', o2.i);
  writeln('o2.i change to 2');
  o2.i := 2;
  writeln('o1.i = 2 too?": ', o1.i = 2);
  writeln('Destroy o2');
  o2.Free;
  writeln('o1.i still exists?: ', Assigned(o1), ' so, write o1.i: ', o1.i);
  writeln('o1.i change to 3');
  o1.i := 3;
  writeln('o1.i: ', o1.i);
  try
    o1.Free;
  except
    on e: Exception do
      writeln(e.Message);
  end;
  readln;
end.

The o1 variable still exists, even after o2 was destroed! But o2 point to o1... The variable o1 exists, but I can't destroy it! Why??

I test on Lazarus and Delphi 7.
The only diference is in this line:

  writeln('o1.i still exists?: ', Assigned(o1), ' so, write o1.i: ', o1.i);

In Lazarus, the result is:
TRUE | 0

In Delphi, the result is:
TRUE | 2

Again... Why??

Marcos Douglas

Marcos Douglas
to Marcos Douglas
Marcos Douglas wrote 110 lines
On Sat, Mar 5, 2011 at 7:05 PM, Mark Morgan Lloyd wrote: > Marcos Douglas wrote: >> >> On Sat, Mar 5, 2011 at 8:24 AM, Mark Morgan Lloyd >> wrote: >>> >>> Where a unit exports an instance of an object, what's best practice for >>> making this read-only? I'm referring to the object reference itself here, >>> not properties of the object. >>> >>> Where I've done this in the past I've used a function: >>> >>> interface >>> >>> function InitText: TFormattedString; >>> >>> implementation >>> >>> var     xInitText: TFormattedString= nil; >>> >>> function InitText: TFormattedString; >>> >>> begin >>>  result := xInitText >>> end { InitText } ; >>> >>> Is there a better way using e.g. global properties that doesn't >>> necessitate >>> both a property and an explicit function? >>> >>> property InitText: TFormattedString read xInitText; >>> >>> The obvious problem here is that xInitText is a forward reference to an >>> unexported variable. >> >> Make a function to return a global variable does not make it read only. >> If I use your function like this: >> o := InitText; >> o := nil;  //this is the same xInitText = nil > > Disagree. You are changing the value of o, not of xInitText. xInitText is > initialised once somewhere in the unit exporting it, subsequently its > properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...). You right... but not TOTALY. I made a little program to explain that, but now I'm confusing, see: program ref; {$mode objfpc}{$H+} uses  sysutils; type  TObj = class  public    i: integer;  end; var  o1: TObj = nil;  o2: TObj = nil; function getobj: TObj; begin  if not Assigned(o1) then    o1 := TObj.Create;  o1.i := 1;  result := o1; end; begin  o1 := getobj;  o2 := o1;  writeln('o1.i: ', o1.i);  writeln('o2.i: ', o2.i);  writeln('o2.i change to 2');  o2.i := 2;  writeln('o1.i = 2 too?": ', o1.i = 2);  writeln('Destroy o2');  o2.Free;  writeln('o1.i still exists?: ', Assigned(o1), ' so, write o1.i: ', o1.i);  writeln('o1.i change to 3');  o1.i := 3;  writeln('o1.i: ', o1.i);  try    o1.Free;  except    on e: Exception do      writeln(e.Message);  end;  readln; end. The o1 variable still exists, even after o2 was destroed! But o2 point to o1... The variable o1 exists, but I can't destroy it! Why?? I test on Lazarus and Delphi 7. The only diference is in this line:  writeln('o1.i still exists?: ', Assigned(o1), ' so, write o1.i: ', o1.i); In Lazarus, the result is: TRUE | 0 In Delphi, the result is: TRUE | 2 Again... Why??

This occurs because I use the "pointer of memory" of property "i" and the compiler ignore the instance of object?
I don't understand... if o1==o2 and o1 was released (Free and Nil), why I can continue using o2 if it points to the same memory, i.e., o1? And why I can't destroy it?

I think the property "i" continue existing and, because that, I can continue using "i" property through o2 variable. Is that right?

Marcos Douglas

Marcos Douglas
to Mark Morgan Lloyd: Disagree. You are changing the value of o, not of xInitText. xInitText is initialised…
Mark Morgan Lloyd wrote 42 lines
Marcos Douglas wrote: > > On Sat, Mar 5, 2011 at 8:24 AM, Mark Morgan Lloyd > wrote: >> >> Where a unit exports an instance of an object, what's best practice for >> making this read-only? I'm referring to the object reference itself here, >> not properties of the object. >> >> Where I've done this in the past I've used a function: >> >> interface >> >> function InitText: TFormattedString; >> >> implementation >> >> var     xInitText: TFormattedString= nil; >> >> function InitText: TFormattedString; >> >> begin >>  result := xInitText >> end { InitText } ; >> >> Is there a better way using e.g. global properties that doesn't >> necessitate >> both a property and an explicit function? >> >> property InitText: TFormattedString read xInitText; >> >> The obvious problem here is that xInitText is a forward reference to an >> unexported variable. > > Make a function to return a global variable does not make it read only. > If I use your function like this: > o := InitText; > o := nil;  //this is the same xInitText = nil Disagree. You are changing the value of o, not of xInitText. xInitText is initialised once somewhere in the unit exporting it, subsequently its properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...).

I was right.
The "o" points to "xInitText". If you release "o" the "xInitText" will be release too.
But if you do:
o:=nil
...isn't the same xInitText:=nil, of course.

I did another test, see:

program test2;
{$mode objfpc}{$H+}
type
  pmyrec = ^myrec;
  myrec = record
    a: integer;
    p: pmyrec;
  end;
var
  r1: pmyrec;
  r2: pmyrec;
begin
  New(r1);
  r1^.a := 1;
  New(r1^.p);
  r1^.p^.a := 44;
  r2 := r1;
  r2^.a := 2;
  writeln(r2^.a);
  writeln(r2^.p^.a);
  Dispose(r1);
  writeln(r2^.a); //ok, because "a" is an simple integer
  writeln(r2^.p^.a); //  error, because "p" was released when "r1" was
relesead too.
  readln;
end.

I can access to primitive variables... but not to pointers.

Marcos Douglas

Aleksa Todorovic
to Marcos Douglas
Marcos Douglas wrote 82 lines
On Sat, Mar 5, 2011 at 7:05 PM, Mark Morgan Lloyd wrote: > Marcos Douglas wrote: >> >> On Sat, Mar 5, 2011 at 8:24 AM, Mark Morgan Lloyd >> wrote: >>> >>> Where a unit exports an instance of an object, what's best practice for >>> making this read-only? I'm referring to the object reference itself here, >>> not properties of the object. >>> >>> Where I've done this in the past I've used a function: >>> >>> interface >>> >>> function InitText: TFormattedString; >>> >>> implementation >>> >>> var     xInitText: TFormattedString= nil; >>> >>> function InitText: TFormattedString; >>> >>> begin >>>  result := xInitText >>> end { InitText } ; >>> >>> Is there a better way using e.g. global properties that doesn't >>> necessitate >>> both a property and an explicit function? >>> >>> property InitText: TFormattedString read xInitText; >>> >>> The obvious problem here is that xInitText is a forward reference to an >>> unexported variable. >> >> Make a function to return a global variable does not make it read only. >> If I use your function like this: >> o := InitText; >> o := nil;  //this is the same xInitText = nil > > Disagree. You are changing the value of o, not of xInitText. xInitText is > initialised once somewhere in the unit exporting it, subsequently its > properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...). I was right. The "o" points to "xInitText". If you release "o" the "xInitText" will be release too. But if you do: o:=nil ...isn't the same xInitText:=nil, of course. I did another test, see: program test2; {$mode objfpc}{$H+} type  pmyrec = ^myrec;  myrec = record    a: integer;    p: pmyrec;  end; var  r1: pmyrec;  r2: pmyrec; begin  New(r1);  r1^.a := 1;  New(r1^.p);  r1^.p^.a := 44;  r2 := r1;  r2^.a := 2;  writeln(r2^.a);  writeln(r2^.p^.a);  Dispose(r1);  writeln(r2^.a); //ok, because "a" is an simple integer

In fact, this is not ok. Here you access memory which has been freed, so the result is completely undefined (hence difference between Delphi and FPC in your example above).

 writeln(r2^.p^.a); //  error, because "p" was released when "r1" was relesead too.

Just to be clear - memory pointed to by p has not been release, it is still out there (you have to New-s and one Dispose), but you lost reference to that memory because memory holding value of p was released.

Show quoted text 11 lines
 readln; end. I can access to primitive variables... but not to pointers. Marcos Douglas _______________________________________________ fpc-pascal maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Mark Morgan Lloyd
to Marcos Douglas: On Sat, Mar 5, 2011 at 7:05 PM, Mark Morgan Lloyd I was right. The "o" points to…
Marcos Douglas wrote 14 lines
>> Make a function to return a global variable does not make it read only. >> If I use your function like this: >> o := InitText; >> o := nil; //this is the same xInitText = nil > Disagree. You are changing the value of o, not of xInitText. xInitText is > initialised once somewhere in the unit exporting it, subsequently its > properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...). I was right. The "o" points to "xInitText". If you release "o" the "xInitText" will be release too. But if you do: o:=nil ...isn't the same xInitText:=nil, of course.

You're right if you're talking about primitive heap operations, in exactly the same way that you'd be right if you used assembler to trample all over flat memory. However sticking to my original question which dealt with exporting an instance of an object, you could free the read-only object by doing o.Free which is identical to InitText.Free, i.e. you'd be using the declared destructor- which presumably could be hidden from calls outside the exporting unit.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]
Marcos Douglas
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 25 lines
Marcos Douglas wrote: >>> Make a function to return a global variable does not make it read only. >>> If I use your function like this: >>> o := InitText; >>> o := nil;  //this is the same xInitText = nil >> >> Disagree. You are changing the value of o, not of xInitText. xInitText is >> initialised once somewhere in the unit exporting it, subsequently its >> properties etc. can be used elsewhere e.g. InitText.AppendFormatted(...). > > I was right. > The "o" points to "xInitText". If you release "o" the "xInitText" will > be release too. > But if you do: > o:=nil > ...isn't the same xInitText:=nil, of course. You're right if you're talking about primitive heap operations, in exactly the same way that you'd be right if you used assembler to trample all over flat memory. However sticking to my original question which dealt with exporting an instance of an object, you could free the read-only object by doing o.Free which is identical to InitText.Free, i.e. you'd be using the declared destructor- which presumably could be hidden from calls outside the exporting unit.

I extrapoled your question... sorry.
But my initial concept is OK: your "read only" object is not read only.

Marcos Douglas

leledumbo
to Marcos Douglas

Using functions is the only way I know (still it's not that "read only", accessing from assembly code still allows modification). For pointer based types, you could give a copy of the instance instead of the original one (if you want a better read only semantics where the function has persistent return value).

View this message in context: http://free-pascal-general.1045716.n5.nabble.com/Read-only-global-references-tp3410604p3411619.html Sent from the Free Pascal - General mailing list .
Mark Morgan Lloyd
to leledumbo
leledumbo wrote 5 lines
Using functions is the only way I know (still it's not that "read only", accessing from assembly code still allows modification). For pointer based types, you could give a copy of the instance instead of the original one (if you want a better read only semantics where the function has persistent return value).

Thanks, I'd reluctantly come to the same conclusion. "Read only" in that it doesn't expose a global variable which can be unintentionally written.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]
Marcos Douglas
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 11 lines
leledumbo wrote: > > Using functions is the only way I know (still it's not that "read only", > accessing from assembly code still allows modification). For pointer based > types, you could give a copy of the instance instead of the original one > (if > you want a better read only semantics where the function has persistent > return value). Thanks, I'd reluctantly come to the same conclusion. "Read only" in that it doesn't expose a global variable which can be unintentionally written.

"...global variable which can be unintentionally written." If the intention is not expose, intentionally, a variable... ok, this is the better way I know (but it can be written without problem).

Marcos Douglas