A better way?

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

Re: A better way?

Tony Whyman
Ryan,

If you want to get rid of (ugly) typecasts then maybe you should
investigate the "absolute" keyword. You get a lot of examples in the
LCL. For example, here's one I chose at random:

function TGtk2WidgetSet.RawImage_CreateBitmaps(const ARawImage:
TRawImage; out
   ABitmap, AMask: HBitmap; ASkipMask: boolean): boolean;
var
   GdiObject: PGDIObject absolute ABitmap;
   GdiMaskObject: PGDIObject absolute AMask;
   Desc: TRawImageDescription absolute ARawImage.Description;
....


You could describe it as typecast done in the var clause of a method.
The right hand identifier is not restricted to function parameters.

Regards

Tony Whyman
MWA

On 15/04/16 07:15, Ryan Joseph wrote:
> I’ve cleaned up some code by declaring uses in the implementation and using generic untyped variables like TObject for parameters. This compiles but I’m left with lots of ugly type casting in the implementation because the parameters for methods are untyped. Yes it works but I feel like the compiler could be helping more.
>
>
>

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

Re: A better way?

Sven Barth-2

Am 15.04.2016 10:46 schrieb "Tony Whyman" <[hidden email]>:
>
> Ryan,
>
> If you want to get rid of (ugly) typecasts then maybe you should investigate the "absolute" keyword. You get a lot of examples in the LCL. For example, here's one I chose at random:
>
> function TGtk2WidgetSet.RawImage_CreateBitmaps(const ARawImage: TRawImage; out
>   ABitmap, AMask: HBitmap; ASkipMask: boolean): boolean;
> var
>   GdiObject: PGDIObject absolute ABitmap;
>   GdiMaskObject: PGDIObject absolute AMask;
>   Desc: TRawImageDescription absolute ARawImage.Description;
> ....
>
>
> You could describe it as typecast done in the var clause of a method. The right hand identifier is not restricted to function parameters.

While it works using "absolute" for a public API is rather unsafe (I'd only use that in private methods). In those cases the "as" operator should he used (or at least "is" plus returning an error).

Regards,
Sven


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

Re: A better way?

Sven Barth-2
In reply to this post by Ryan Joseph-3

Am 15.04.2016 08:46 schrieb "Ryan Joseph" <[hidden email]>:
> Does this look like more work than using $includes? Maybe I need to seriously considering reorganizing my projects to work like this but it just feels wrong for some reason.
>
> =====================
>
> unit Globals;
> interface
>
> type
>         HClassB = 'TClassB';    // declare a hint to the class name
>
> implementation
> end.
>
> =====================
>
> unit ClassB;
> interface
> uses
>         ClassA;
>
> type
>         TClassB = class
>                 procedure DoSomething (sender: TClassA);
>         end;
>
> implementation
>
> procedure TClassB.DoSomething (sender: TClassA);
> begin
>         // full access to TClassA
> end;
>
> end.
>
> =====================
>
> unit ClassA;
> interface
> uses
>         Globals;
>
> type
>         TClassA = class
>                 // The interface section can't know about TClassB so we declare a
>                 // class name hint from Globals.pas (HClassB) and assign the paramter with it
>                 // using the "hint" syntax
>                 procedure SetValue (value: TObject[HClassB]);
>         end;
>
> implementation
>
> // use ClassB now so hints evaluate
> uses
>         ClassB;
>
> procedure TClassA.SetValue (value: TObject[HClassB]);
> begin
>         // behind the scenes the compiler (or preparser if FPC has one) replaces the text "TObject[HClassB]" to “TClassB" or throws an error if the identifier TClassB is not found
>         value.DoSomething(self);
> end;
>
> end.

*shudders* Before we introduce such syntax I'd prefer to get the formal class types that were added for Objective Pascal working with Object Pascal as well (which would be exactly what you want just with a more sane syntax).

But again: you're currently fighting against the language. Use interfaces and abstract classes and overthink your dependencies and design.

Regards,
Sven


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

Re: A better way?

Ryan Joseph-3
In reply to this post by Tony Whyman


> On Apr 15, 2016, at 3:46 PM, Tony Whyman <[hidden email]> wrote:
>
> You could describe it as typecast done in the var clause of a method. The right hand identifier is not restricted to function parameters.

I’m not understanding how this would work. I’ve read that the keyword (I’ve never heard about until now) makes a variable share memory so it’s like an alias I guess.

So, a common scenario is a member variable being declared as TObject then having to typecast it every time I need to access one of it’s methods. Declaring another variable in each function instead of typecasting is perhaps more work but maybe I’m not getting it.

Thanks.

Regards,
        Ryan Joseph

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

Re: A better way?

Ryan Joseph-3
In reply to this post by Sven Barth-2

> On Apr 15, 2016, at 3:56 PM, Sven Barth <[hidden email]> wrote:
>
> *shudders* Before we introduce such syntax I'd prefer to get the formal class types that were added for Objective Pascal working with Object Pascal as well (which would be exactly what you want just with a more sane syntax).

I agree 100%. Putting a class definition into the namespace of another unit and importing like Objective Pascal(c) does is preferable. There doesn’t seem to be much interest in the problem though.

>
> But again: you're currently fighting against the language. Use interfaces and abstract classes and overthink your dependencies and design.
>

Thanks to better usage of “uses” in the implementation I’ve learned now how to replace the abstract classes with weakly typed parameters and typecasting which is perhaps a small improvement because I don’t need to worry about extra classes and overriding etc… I’m happy with that but I’m still doing something the compiler could be helping with so I thought I’d mention it in the spirit of efficiency. :)

The only question now is if using $ifdef’s and $includes to make large single units is preferable to typecasting ugliness.

Thanks.

Regards,
        Ryan Joseph

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

Re: A better way?

Dennis
In reply to this post by Sven Barth-2

> > You could describe it as typecast done in the var clause of a
> method. The right hand identifier is not restricted to function
> parameters.
>
> While it works using "absolute" for a public API is rather unsafe (I'd
> only use that in private methods). In those cases the "as" operator
> should he used (or at least "is" plus returning an error).
>
> Regards,
> Sven
>
>
May I know why it is unsafe to use absolute on public method's parameters?
Also, will there be any compiler options in which the parameters are
passed through registers?  In that case, will the absolute of private
method's parameters still work?

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

Re: A better way?

Tony Whyman
In reply to this post by Ryan Joseph-3


On 15/04/16 10:00, Ryan Joseph wrote:
> So, a common scenario is a member variable being declared as TObject then having to typecast it every time I need to access one of it’s methods. Declaring another variable in each function instead of typecasting is perhaps more work but maybe I’m not getting it.
>
> Thanks.
Here's an update of my previous example. In this example, "absolute" is
just a way of tidying up the code when you need to make multiple
references to a variable that always has to have its type coerced.

In the end though this is just a "what's in a name? A rose by any other
name would smell as sweet" debate. No matter how you approach the
problem, you have to have an identifier for the other class when you
reference its methods and properties. If you don't want to declare two
mutually referential classes in the same unit, then one of them (but
only one) has to use some technique to avoid circular references. Type
casting is a simple technique and you either have to coerce each use
(think of TClassB(FObject) as just being a longer name for the same
thing) or declare an alias of the correct type - a "With" statement can
also be your friend here.

It's not that big an overhead and when it comes to type casts a 'C'
programmer would wonder what all the fuss was about.

unit unitA;

interface

type

class TClassA
private
   FClassBObject: TObject;
public
   procedure SomeProc;
end;

implementation

uses unitB;

procedure TClassA.SomeProc;
var aClassBObject: TClassB absolute FClassBObject;
begin
   aClassBObject.OtherProc;
end;

end.

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

Re: A better way?

Tony Whyman
Just remembered, FPC also supports macros - see
http://www.freepascal.org/docs-html/prog/progse5.html

I haven't tested it, but you should be able to do

{$MACRO On}

{$DEFINE aClassBObject:=TClassB(FClassBObject) }

and then use aClassBObject instead of the coercion.

On 15/04/16 11:13, Tony Whyman wrote:

> In the end though this is just a "what's in a name? A rose by any
> other name would smell as sweet" debate. No matter how you approach
> the problem, you have to have an identifier for the other class when
> you reference its methods and properties. If you don't want to declare
> two mutually referential classes in the same unit, then one of them
> (but only one) has to use some technique to avoid circular references.
> Type casting is a simple technique and you either have to coerce each
> use (think of TClassB(FObject) as just being a longer name for the
> same thing) or declare an alias of the correct type - a "With"
> statement can also be your friend here.

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

Re: A better way?

Ryan Joseph-3

> On Apr 15, 2016, at 5:34 PM, Tony Whyman <[hidden email]> wrote:
>
> Just remembered, FPC also supports macros - see http://www.freepascal.org/docs-html/prog/progse5.html
>
> I haven't tested it, but you should be able to do
>
> {$MACRO On}
>
> {$DEFINE aClassBObject:=TClassB(FClassBObject) }
>
> and then use aClassBObject instead of the coercion.

I remember trying to doing pre-parser type things using macros but the think the conclusion is they don’t work by just replace text like in C.

I’m trying an example like this but getting errors and even crashing the compiler (seg faults).

Regards,
        Ryan Joseph

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

Re: A better way?

Ryan Joseph-3
In reply to this post by Tony Whyman

> On Apr 15, 2016, at 5:13 PM, Tony Whyman <[hidden email]> wrote:
>
> unit unitA;
>
> interface
>
> type
>
> class TClassA
> private
>  FClassBObject: TObject;
> public
>  procedure SomeProc;
> end;
>
> implementation
>
> uses unitB;
>
> procedure TClassA.SomeProc;
> var aClassBObject: TClassB absolute FClassBObject;
> begin
>  aClassBObject.OtherProc;
> end;
>
> end.

I see now. That’s possibly preferable so I’ll keep it in mind but like you said just sucking it up and type casting is not so bad. :) Sure enough but it would even better if the compiler was more friendly here. If there was a good pre parser in FPC I’d just make some custom syntax  but that’s not an option either as far as I know.

Regards,
        Ryan Joseph

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

Re: A better way?

Ryan Joseph-3
In reply to this post by Ryan Joseph-3

> On Apr 15, 2016, at 6:26 PM, Ryan Joseph <[hidden email]> wrote:
>
> I remember trying to doing pre-parser type things using macros but the think the conclusion is they don’t work by just replace text like in C.
>
> I’m trying an example like this but getting errors and even crashing the compiler (seg faults).

Never mind! I was getting recursion causing the problems. It looks like they do work like the c preprocessor. Thanks, I’ll see if I can do something with this.

Regards,
        Ryan Joseph

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

Re: A better way?

Sven Barth-2
In reply to this post by Dennis

Am 15.04.2016 11:22 schrieb "Dennis" <[hidden email]>:
>
>
>> > You could describe it as typecast done in the var clause of a method. The right hand identifier is not restricted to function parameters.
>>
>> While it works using "absolute" for a public API is rather unsafe (I'd only use that in private methods). In those cases the "as" operator should he used (or at least "is" plus returning an error).
>>
>> Regards,
>> Sven
>>
>>
> May I know why it is unsafe to use absolute on public method's parameters?

I didn't mean that in the context of the "public" section of classes, but if your API is available to third party users (that can also include other members of the same development team) then you need to make sure that the instance that is passed in is indeed a correct one as otherwise you'd access the wrong fields or methods. For this kind of mistake public and protected methods are of course the most affected ones as are routines that are declared in the interface section of a unit.

> Also, will there be any compiler options in which the parameters are passed through registers?  In that case, will the absolute of private method's parameters still work?

Parameters are already often passed in registers (of course this depends on the platform). However parameters also often have a spilling locations in the stack to store the parameter if register pressure gets too high, so absolute shouldn't be a problem normally. It might also be that the usage of absolute prohibits some optimizations the compiler would otherwise do with that parameter inside the function.

Regards,
Sven


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

Re: A better way?

Sven Barth-2
In reply to this post by Ryan Joseph-3

Am 15.04.2016 13:58 schrieb "Ryan Joseph" <[hidden email]>:
>
>
> > On Apr 15, 2016, at 5:13 PM, Tony Whyman <[hidden email]> wrote:
> >
> > unit unitA;
> >
> > interface
> >
> > type
> >
> > class TClassA
> > private
> >  FClassBObject: TObject;
> > public
> >  procedure SomeProc;
> > end;
> >
> > implementation
> >
> > uses unitB;
> >
> > procedure TClassA.SomeProc;
> > var aClassBObject: TClassB absolute FClassBObject;
> > begin
> >  aClassBObject.OtherProc;
> > end;
> >
> > end.
>
> I see now. That’s possibly preferable so I’ll keep it in mind but like you said just sucking it up and type casting is not so bad. :) Sure enough but it would even better if the compiler was more friendly here. If there was a good pre parser in FPC I’d just make some custom syntax  but that’s not an option either as far as I know.

Again, you're fighting against design principles of the language. It's just as if you'd want to have virtual class methods in C++...

Regards,
Sven


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

Re: A better way?

Ryan Joseph-3

> On Apr 15, 2016, at 7:03 PM, Sven Barth <[hidden email]> wrote:
>
> Again, you're fighting against design principles of the language. It's just as if you'd want to have virtual class methods in C++…

Agreed. Everyone has their own list of things they want their language to do I guess. Maybe I got spoiled with the typeless “id” type from Objective-C that let me have little work arounds for things like this. Not having a dictionary type after using PHP hurts also. ;)

Here’s another option I thought of. If the type casting was of a serious quantity doing this could save time and improve readability of code.

unit unitA;

interface

type

class TClassA
private
 FClassBObject: TObject;
public
 procedure SomeProc;
end;

implementation

uses unitB;

type
 TClassAHelper = class helper for TClassA
  function aClassBObject: TClassB;
 end;

function TClassAHelper.aClassBObject: TClassB;
begin
 result := TClassB(FClassBObject)
end;

procedure TClassA.SomeProc;
begin
 aClassBObject.OtherProc;
end;

end.

Regards,
        Ryan Joseph

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