[fpc-devel] "Default" discussion for SmartPointers etc

Maciej Izak hnb.code at gmail.com
Wed Jul 27 10:12:38 CEST 2016

2016-07-26 21:59 GMT+02:00 Sven Barth <pascaldragon at googlemail.com>:

> First of let me tell you that in essence and in general I'm in support
> of your "default" idea (and the resulting idea to use this for smart
> pointers), though I have a few points, but I hope that we'll be able to
> solve these. :)

Note: Generally I am working on boost-spirit like solution for Pascal so
one single bad move will destroy my further plans...

> First of it might be better not to name the tests "tdefaultX.pp" as that
> is already used for the Default() intrinsic... :/

No problem with that - we can change this ;)

> Second I don't know whether it's a good idea to use this with fields
> (though kudos for omitting the ";" between type and "default" ;) ).
> Maybe it would be better - to stay with the source of your idea - to
> only allow this for properties. This would allow to keep the field
> itself private for example and control its access through the setter
> (and ordinary as well as management operators), something that one
> couldn't do if one would need to use operators as the field would need
> to be public to be really useful outside of the record instance.

Property was considered for "default" as main option but has many weakness.
Sure, default "non indexed" property can be added as addition but definitely
not as main idea, because:

1. It breaks tdefault17 :

(yes I know we can use property for this but you need to declare additional
getter/setter which is not much elegant in that case)

2. "default" for field is unique for [Nil|Null]able types (fastest possible
implementation, and very correct for Pascal language. Nilable type works
exactly like pointers with nice additions):

=== code begin ===
  // record constraint is designed for nullable types :P see #24073
  // #24073 Is already fixed in my implementation. record constraint accepts
  // all not nilable types (including sets and strings - empty string as
logical entity is not nil at all)
  TNullable<T: record> = record
  public type
    PT = ^T;
  strict private
    Instance: ^T default;
    function GetValue: T;
    property Value: T read GetValue;

    function HasValue: Boolean; inline;
    function GetValueOrDefault: T;

    class operator Implicit(A: T): TNullable<T>;
    class operator Implicit(A: TNullable<T>): T;
    class operator Implicit(A: PT): TNullable<T>;

    class operator Equal(A: TNullable<T>; B: PT): Boolean;

    class operator Initialize(var A: TNullable<T>);
    class operator Finalize(var A: TNullable<T>);
    class operator Clone(constref Src: TNullable<T>; var Dest:
    class operator Copy(var Rec: TNullable<T>);
=== code end ===

3. "default" need to be transparent and usable with existing code base,
some compiler magic is part of my further compiler development (I mean here
"Storage Modifiers" and ARC objects in DelphiNextgen mode). With current
approach is possible to pass record with default field as var/out/constref
parameter. With property that is impossible.

4. for property for records there exist well know problems for "with"

5. "default" is designed as specialized helpers for TArray<T> and for
Interfaces, property will block passing "default field" as var/out/constref
parameter (similar to point 3).

> Third I don't really agree with the notion that the record methods,
> fields, etc. take precedence to the default field. See further down for
> my suggestion to solve this (though that idea isn't without its flaws
> either).
> Question: can "default" only be used in "record" or also in "object" and
> "class"?

Current implementation allows "default" only for "records".

> >
> > Tests (very good way to see how it works. NOTE: I need to add few other
> > tests for functions with var/const/out, "for in do" loop and for arrays
> > and indexed properties - help with additional tests is welcome):
> >
> > https://github.com/maciej-izak/PascalSmartPointers/tree/master/tests
> Note: tests for visibility.

Right, but note that "default" is implicitly used even when is declared as
strict private (which is correct and by design - for example see TNullable
above). For "default" non indexed properties that rule might be incorrect.

> > The way how to obtain pointer can be a little confusing for most of
> > Pascal programmers. Anyway nothing new for Pascal language. In Pascal we
> > have little known @@ operator to get pointer to variable which handle
> > pointer to procedure/function. For records with "default field" @ means
> > "get pointer to default field" and @@ means "get pointer to record".
> While I have to admit that I haven't known the @@-operator I don't
> necessarily agree with its use. Take tdefault7 and tdefault8 for example
> where you let the left hand side of the assignment determine which
> pointer is used. In tdefault7 it's the pointer to "a" while in tdefault8
> it's the pointer to "a.DefaultValue".

@@ operator is very simple way to determine where you point. In any other
case we have casting hell. See below (and more below). @@ exist rather as
fulfillment to pa := @a; form tdefault7.pp (anyway is necessary for untyped
pointers). You might not like @@ operator but @@ is part of long tradition.
In practice works like a charm and is very clear.

> Since normally in Pascal the
> result type of an expression is *not* determined by the left hand side
> of an assignment that's rather confusing (yes, there are exceptions, but
> that doesn't mean that one needs to add a new one).

That is also by design. That is because you can't declare as "default
field" nor "normal field" the field of owner type, so as logical
consequence the syntax needs to be allowed (I mean here pa := @a; form
tdefault7.pp). In daily usage it works very well and any other compiler
behavior is irrational. Thank this feature most of programmers don't need
to use @@ syntax (btw. the idea of introducing this was not forcing others
to use @@). Additionally is necessary to get ride of untyped/typed pointers
so we have rules presented in example tdefault8. Anyway for complex
solution @@ is needed.

> My idea to solve this coincides with my idea to solve the problem to
> access the record instead of the default field: typecasts.
> ...
> Essentially a typecast would disable the default field for the type it
> had been casted to.

That was my first implementation (!!!). Which is definitely bad. :\ It
breaks a lot of other important rules. For example is impossible to get
ride of Implicit/Explicit operators when as "default field" is declared
record which has Implicit/Explicit operators. Really, really bad idea,
end-user code looks terrible and is unclear.

> Of course this idea with the default field taking precedence becomes
> tainted a bit if one considers the management operators or the
> assignment operators as these would be part of the record and not the
> default field, but would still need to work...

Remember that "default" is designed not "only" for SmartPtr/Obj and for
Nilable types but also for "specialized" helpers for any type (for example
for TArray<T>, for Interfaces etc). We can't do exception only for
management operators and for assignment operators - unclear rule.

> Note: don't forget to test with global operator overloads ;)

I will try to remember ;)

Best regards,
Maciej Izak
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20160727/af09d988/attachment.html>

More information about the fpc-devel mailing list