Generic type conflicts

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Generic type conflicts

Free Pascal - General mailing list
On Fri, Nov 8, 2019 at 11:04 AM Jonas Maebe <[hidden email]> wrote:
You can't. It's the main difference between C++ templates, which is a
Turing-complete programming language, and generics, which is simply a
parametrising mechanic.

I know what you mean, and I'm aware, but you actually kind of can to the fairly straightforward extent that I'm concerned about with Ryan's patch (i.e. simply passing constant results as constraints rather than function parameters.) 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Generic type conflicts

Jonas Maebe-3
On 08/11/2019 17:18, Ben Grasset via fpc-pascal wrote:

> On Fri, Nov 8, 2019 at 11:04 AM Jonas Maebe <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     You can't. It's the main difference between C++ templates, which is a
>     Turing-complete programming language, and generics, which is simply a
>     parametrising mechanic.
>
> I know what you mean, and I'm aware, but you actually kind of can to the
> fairly straightforward extent that I'm concerned about with Ryan's patch
> (i.e. simply passing constant results as constraints rather than
> function parameters.) 

It does explain why templates can contain invalid C++: it's because the
invalid C++ gets discarded before it is (semantically) presented to the
C++ parser. This does not work with generics, because generics are
Pascal themselves.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Generic type conflicts

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list
On Fri, Nov 8, 2019 at 11:18 AM Ben Grasset <[hidden email]> wrote:
I know what you mean, and I'm aware, but you actually kind of can to the fairly straightforward extent that I'm concerned about with Ryan's patch (i.e. simply passing constant results as constraints rather than function parameters.) 

Also, if you were referring specifically to the "making the choice part", looking at the PDQSort C++ code again, all it was actually doing is evaluating the *types* involved, not the comparison function itself. Which would be fairly straightforward to replicate in FPC using something like a constant TTypeKind constraint.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Generic type conflicts

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list
Am 02.11.2019 um 15:55 schrieb Ryan Joseph via fpc-pascal:

> I've wanted to make a generic version of a vector for a while but I always give up because it seems not very possible. It's probably not even a great idea because many methods don't translate between float and integer but I wanted to prevent other code duplication if possible.
>
> Here's an example of how things break down. Are there any solutions for this currently? I feel like generics need to support some compiler directives so different blocks of code can specialize different depending on the type.
>
> {$mode objfpc}
> {$modeswitch advancedrecords}
>
> program generic_vector_2;
> uses
>    Math;
>
> type
>    generic TVec2<TScalar> = record
>      x, y: TScalar;
>      function Normalize: TVec2;
>    end;
>    TVec2f = specialize TVec2<Float>;
>    TVec2i = specialize TVec2<Integer>;
>
> function TVec2.Normalize: TVec2;
> var
>    fac: TScalar;
> begin
>    // Can't determine which overloaded function to call
>    // Incompatible types: got "Extended" expected "LongInt"
>    fac:=Sqrt(Sqr(x) + Sqr(y));
>    if fac<>0.0 then begin
>      // Incompatible types: got "Single" expected "LongInt"
>      fac:=1.0/fac;
>      result.x:=x*fac;
>      result.y:=y*fac;
>    end else begin
>      result.x:=0;
>      result.y:=0;
>    end;
> end;
>
> begin
> end.

First of Sqrt always returns a ValReal (aka the best precision floating
point), thus you should declare fac as ValReal. Thus you should cast x
and y to ValReal before passing them to Sqr to avoid overload troubles.

Then you only need to ensure that the adjusted vector components are
passed correctly to the result which will lead to code like this:

=== code begin ===

program tgenvec;

{$mode objfpc}
{$modeswitch advancedrecords}

uses
   Math;

type
   generic TVec2<TScalar> = record
   private type
     PScalar = ^TScalar;
   public
     x, y: TScalar;
     function Normalize: TVec2;
   end;

function TVec2.Normalize: TVec2;
var
   fac, tmpx, tmpy: ValReal;
begin
   fac := Sqrt(Sqr(ValReal(x)) + Sqr(ValReal(y)));
   if fac <> 0.0 then begin
     fac := 1.0 / fac;
     tmpx := x * fac;
     tmpy := y * fac;
     if GetTypeKind(TScalar) in [tkInteger, tkInt64, tkQWord] then begin
       Result.x := Round(tmpx);
       Result.y := Round(tmpy);
     end else if GetTypeKind(TScalar) = tkFloat then begin
       Result.x := PScalar(@tmpx)^;
       Result.y := PScalar(@tmpy)^;
     end;
   end else
     Result := Default(TVec2);
end;

type
   TVec2f = specialize TVec2<Double>;
   TVec2i = specialize TVec2<LongInt>;

var
   vf: TVec2f;
   vi: TVec2i;
begin
   vf.x := 2.5;
   vf.y := 3.5;
   vf := vf.Normalize;
   Writeln(vf.x, ' ', vf.y);
   // on Win64 this prints 5.8123819371909646E-001 8.1373347120673500E-001
   vi.x := 2;
   vi.y := 4;
   vi := vi.Normalize;
   Writeln(vi.x, ' ', vi.y);
   // on Win64 this prints 0 1
end.

=== code end ===

The path not taken for the GetTypeKind inside the TVec<>.Normalize will
be optimized away. The pointer conversion is needed, because a floating
point type can not be assigned to an integer type. Sadly the compiler
does not realize that it does not really need to take the address there,
so that will stay even in O4 for the non-floating point case. But hey,
you've got a generic Normalize then...

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Generic type conflicts

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list


> On Nov 7, 2019, at 12:28 PM, Ben Grasset via fpc-pascal <[hidden email]> wrote:
>
>   {$IF GetTypeKind(T) in [tkInteger, tkInt64, tkQWord]}
>     Result := A div B
>   {$ELSEIF GetTypeKind(T) = tkFloat}
>     Result := A / B
>   {$ELSE}
>     Result := Default(T);
>   {$ENDIF}

This is what I hinted in my post.  Has anything like this been considered before? It seems necessary to me but it looks like Sven had some fancy work around using pointers. Either way the compile time directive would be faster and avoid the conditional statements.
Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Generic type conflicts

Free Pascal - General mailing list
Ryan Joseph via fpc-pascal <[hidden email]> schrieb am So., 10. Nov. 2019, 02:11:


> On Nov 7, 2019, at 12:28 PM, Ben Grasset via fpc-pascal <[hidden email]> wrote:
>
>   {$IF GetTypeKind(T) in [tkInteger, tkInt64, tkQWord]}
>     Result := A div B
>   {$ELSEIF GetTypeKind(T) = tkFloat}
>     Result := A / B
>   {$ELSE}
>     Result := Default(T);
>   {$ENDIF}

This is what I hinted in my post.  Has anything like this been considered before? It seems necessary to me but it looks like Sven had some fancy work around using pointers. Either way the compile time directive would be faster and avoid the conditional statements.

The token recorder does not support preprocessor conditions. Not to mention that this mixes preprocessor and parser stuff. Generics are part of the parser. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
12