Recent

Author Topic: Quick Modern Object Pascal Introduction, for Programmers  (Read 46204 times)

jwdietrich

  • Hero Member
  • *****
  • Posts: 1232
    • formatio reticularis
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #45 on: July 02, 2016, 01:10:30 pm »
This is a very good introduction.

Could you add (perhaps in the "About" section at the end) a notice about the version of the document and the date of last change? This would make life easier for readers, by providing a hint if it is worthwhile to download a new version.
There is a "Last updated" date at the very bottom of the HTML version, see http://michalis.ii.uni.wroc.pl/~michalis/modern_pascal_introduction/modern_pascal_introduction.html . And the exact history of the document may be seen in the GitHub repository on https://github.com/michaliskambi/modern-pascal-introduction . The commits on https://github.com/michaliskambi/modern-pascal-introduction/commits/master document precisely what and when changed:) I update the HTML/PDF output after every commit.

This is great. Version information should also be added to the PDF edition, however.
function GetRandomNumber: integer; // xkcd.com
begin
  GetRandomNumber := 4; // chosen by fair dice roll. Guaranteed to be random.
end;

http://www.formatio-reticularis.de

Lazarus 2.2.6 | FPC 3.2.2 | PPC, Intel, ARM | macOS, Windows, Linux

guest58172

  • Guest
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #46 on: July 02, 2016, 03:35:44 pm »
  • "GOOD" and "UGLY" interfaces is really not serious in a technical writing. You're not the guy who decides that. Some people have to work with COM as a constraint, your opinion does not matter, they have to do it. dot.
  • You could put a paragraph about class var, class properties, class constructor and class destructors. They allow a better encapsulation, e.g some stuff that would have been defined as private global variables in the past can now be wrapped as class var.

Otherwise I find this doc quite usefull but here you preach to converted people. What you should do is post this on r/programming where ignorant ppl still see Pascal as it was in 1988, when they were at school and when they've made a "cutting-edge" mastermind program...

Also on stack overflow there are often guys who land with some homework and you clearly see that the teacher still uses the same book since 1990...so annoying.


michalis

  • Full Member
  • ***
  • Posts: 137
    • Castle Game Engine
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #47 on: July 02, 2016, 06:35:02 pm »
Quote from: Thaddy
The COM part is still not correct as I explained in my email to you, because it reflects the only way you should not ever never use COM.

I will do a follow up (as per email) this weekend. For starters: there are no memory leaks....

COM should be used through interface instances, not class instances as in your book. And you should respect the reference counting. TComponent is a really bad example...

As for memory leaks: In my example, there are no memory leaks, indeed. In the example you send me through email, there were memory leaks. Simply checking with "-gl -gh" shows this. You probably wanted to use there TInterfacedObject instead of TComponent.

As for the TComponent, with it's _AddRef implementation that disables reference-counting: it is not my invention. It's part of a standard Pascal library, and a basis for countless classes. Showing how TComponent interacts with COM interfaces seems justified.

I agree that, if you just never use class instances to interact with things that are also accessed through COM interfaces (and always descend from TInterfacedObject, with proper _AddRef implementations), then you're safe. If you *want* the reference-counting of COM interfaces, then COM interfaces are a good deal. I'm open to improvements, my example and wording could probably be improved.

My current example is a way to show how to make something equivalent to previous CORBA example using COM -- it's referred to in the next section (about typecasting of interfaces too). It could be moved to a separate section ("Hacky usage of COM interfaces without reference-counting"), and the basic COM section could describe the "proper" COM usage.

Quote from: BBasile
"GOOD" and "UGLY" interfaces is really not serious in a technical writing. You're not the guy who decides that. Some people have to work with COM as a constraint, your opinion does not matter, they have to do it. dot.

I know that my opinion doesn't change the language -- that's why there is a description of both COM and CORBA interfaces in the article.

But, for some weird reason, I still do have some opinion:) The reasons why I feel that COM interfaces are "ugly" is clearly explained in the article.

I also know how interfaces look like in C# and Java. As it happens, C#, Java and CORBA interfaces are the same. The COM interfaces are different, and I heard a "WTF?" reaction when telling programmers that in Pascal we have interfaces entangled with reference-counting ("WTF -- these are two completely different language features, with different use-cases, should be orthogonal"). So it seems that I'm not alone in my opinion. And to explain interfaces to programmers coming from other languages, CORBA interfaces are more natural. (Regardless whether CORBA or COM are better.)

Quote from: BBasile
You could put a paragraph about class var, class properties, class constructor and class destructor.

Good idea! And thanks for good comments -- you're welcome to post about the article everywhere:)

michalis

  • Full Member
  • ***
  • Posts: 137
    • Castle Game Engine
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #48 on: July 02, 2016, 08:12:18 pm »
If you *want* the reference-counting of COM interfaces, then COM interfaces are a good deal. I'm open to improvements, my example and wording could probably be improved.

Following my own advice, I reworked the part about COM interfaces a bit. At the beginning I show the "non-hacky" usage of them, using TInterfacedObject, and relying on COM reference-counting to deallocate their memory. This was based on the example modifications send by Thaddy - thanks!

There's a section lower that shows "Using COM interfaces with reference-counting disabled", clearly warning of dangers of this approach.

I still consider CORBA interfaces as more suitable for my purposes -- they give me the functionality I need (which is casting a class to any interface it supports), without any unnecessary feature (reference-counting, magic GUIDs and the need to implement _AddRef). As mentioned, CORBA interfaces are also consistent with Java and C# interfaces, which I see as a confirmation that this approach to interfaces makes sense. But I also do understand that COM interfaces are useful when you want reference-counting. Both uses should be now addressed more fairly in the article.

Paulo França Lacerda

  • New Member
  • *
  • Posts: 44
    • BlaisePoint Informática
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #49 on: September 02, 2016, 03:22:05 pm »
Great stuff, even for experienced programmers!
Lazarus 1.6.0.4 on FreePascal 3.0
Windows 7 32-bit

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #50 on: September 02, 2016, 03:25:56 pm »

Perhaps it is time to add a third kind of interfaces to FPC

Reference counted like COM, but otherwise like CORBA, without any of the COM overhead

Perhaps it is better to study what there is first :-)

The COM overhead part is in its implementation (iow TInterfacedObject and/or descendants) not the interface itself.

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #51 on: September 02, 2016, 03:44:08 pm »
You still have no clue about interfaces. It's a piece of shit that part. Do I make myself clear.
Dont ever mix up class instantiation with interface instantiation. It is still the same mistake I already reported. Is that SO difficult to understand?

It really is time to get rough and ... rude..
« Last Edit: September 02, 2016, 03:46:16 pm by Thaddy »
Specialize a type, not a var.

BeniBela

  • Hero Member
  • *****
  • Posts: 905
    • homepage
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #52 on: September 02, 2016, 05:16:46 pm »

Perhaps it is better to study what there is first :-)

The COM overhead part is in its implementation (iow TInterfacedObject and/or descendants) not the interface itself.


There is quite some overhead, because the value of the interface instance is not the same value as self of the class instance.

So on every usage it needs to calculate self from the interface variable. The same calculation again and again.
Or the inverse calculation when the interface is created.

michalis

  • Full Member
  • ***
  • Posts: 137
    • Castle Game Engine
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #53 on: September 02, 2016, 08:03:33 pm »
Dont ever mix up class instantiation with interface instantiation. It is still the same mistake I already reported. Is that SO difficult to understand?

Being rude doesn't help you convince me (or anyone, I suppose). So please let's discuss it in a civilized manner.

The idea to use COM interfaces with hacked _AddRef methods is not my invention. It is part of the standard TComponent implementation. Every time you instantiate TComponent, or any descendant of it, you're creating a class instance of something that is also a COM interface. The language does allow instantiating a class that is also a COM interface, and people use it (goodle "delphi interfaces no reference counting" if you want), and it's mentioned in various books. People in 2016 are still inventing solutions to this, like here: http://blog.marcocantu.com/blog/2016-april-weak-unsafe-interface-references.html .

That is why my article documents this.

As for what is "proper", I even agree with you in some way: it's a bad idea to instantiate a class that is also a COM interface. It opens a can of worms. My suggestion to such usecase is to use a CORBA interface, that was my point all along. It is explicitly written in the article http://michalis.ii.uni.wroc.pl/~michalis/modern_pascal_introduction/modern_pascal_introduction.html#_using_com_interfaces_with_reference_counting_disabled : "You need to be careful in this case to not free the class instance when some interface variable may refer to it. .... To avoid this mess, it’s usually better to use CORBA interfaces, if you don’t want reference-counting with your interfaces."

So, I'm not sure what do you expect of me. If you want to abolish creating object instances when the class has a COM interface, persuade language designers to do so (and you better have some good idea how to change TComponent then, without breaking compatibility). What I can do, is to clearly document the dangers / disadvantages of some constructions, and I feel I have already done so.

Zoran

  • Hero Member
  • *****
  • Posts: 1829
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #54 on: September 05, 2016, 10:52:35 am »
You still have no clue about interfaces. It's a piece of shit that part. Do I make myself clear.
Dont ever mix up class instantiation with interface instantiation. It is still the same mistake I already reported. Is that SO difficult to understand?

It really is time to get rough and ... rude..

I really don't understand who are you addressing here? Because Michalis is absolutely clear in his article about what he thinks about this.
Just use CORBA interfaces unless you use interfaces for real COM support.
With CORBA interfaces, you can get real advantage without reference counting overhead (as explained in java tutorial, see http://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html, and good example of usage in their collections library is here: http://docs.oracle.com/javase/tutorial/collections/interfaces/index.html).

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #55 on: September 05, 2016, 11:28:56 am »
Zoran, He - and by proxy, you too - has no clue about both types of interfaces. The text is simply nonsensical and opiniated. And because he has no clue the examples are wrong. That should be corrected.
I already gave him decent example code some time ago. And a sketch for an adapted text. Reference counted interfaces are becoming more and more important, not just for COM, but also for  Smartpointers and ARC support in trunk that is being written by Maciej Izak. Now those are "modern object pascal" features! Smartpointers already work in trunk with the patch in bug report 0030534 applied.

To show you how this is important:
Code: Pascal  [Select][+][-]
  1. program smartpointers;
  2. {$ifdef fpc}{$mode delphi}{$endif}
  3. uses
  4.   classes,smartptrs;
  5. var
  6.   L:TStringlist;
  7.   PL:TSmartPointer<TStringList>;
  8. begin
  9.   // Auto create
  10.   L:= PL.Value;
  11.   L.Add('Testme');
  12.   writeln(L.Count);
  13.   // Nothing
  14.   L:=PL.Value;
  15.   L.Add('foo');
  16.   L.Add('foo2');
  17.   L.Add('Foobar');
  18.   Writeln(L.Count);  
  19.   writeln(L.Text);
  20.   // Auto cleanup
  21. end.

This is no pseudocode, this is working code.

Here's one possible implementation of a smartpointer, based on Marco Cantu's book, it relies on reference counted interfaces:
Code: Pascal  [Select][+][-]
  1. unit smartptrs;
  2. {$ifdef fpc}{$mode delphi}{$endif}
  3. interface
  4. type
  5.   TSmartPointer<T:class, constructor> = record
  6.   strict private
  7.     FValue:T;
  8.     FFreeTheValue:IInterface;
  9.     function GetValue:T;
  10.     type
  11.       TFreeTheValue = class(TInterfacedObject)
  12.       private
  13.         fObjectToFree:TObject;
  14.       public
  15.         constructor Create(anObjectToFree: TObject);
  16.         destructor Destroy;override;
  17.       end;
  18.    public
  19.      constructor Create(AValue: T);overload;
  20.      procedure Create;overload;
  21.      class operator Implicit(AValue: T):TSmartPointer<T>;
  22.      class operator Implicit(smart: TSmartPointer<T>):T;
  23.     property value: T read GetValue;
  24.    end;
  25.  
  26. implementation
  27.    
  28.    constructor TSmartPointer<T>.TFreeTheValue.Create(anObjectToFree:TObject);
  29.    begin
  30.      self.fObjectToFree := anObjectToFree;
  31.    end;
  32.    
  33.    destructor TSmartPointer<T>.TFreeTheValue.Destroy;
  34.    begin
  35.      fObjectToFree.Free;
  36.      inherited;
  37.    end;
  38.    
  39.    constructor TSmartPointer<T>.Create(AValue:T);
  40.    begin
  41.      FValue := AValue;
  42.      FFreeTheValue := TFreeTheValue.Create(FValue);
  43.    end;
  44.    
  45.    procedure TSmartPointer<T>.Create;
  46.    begin
  47.      TSmartPointer<T>.Create(T.Create);
  48.    end;
  49.    
  50.    class operator TSmartPointer<t>.Implicit(AValue:T):TSmartPointer<T>;
  51.    begin
  52.      Result := TSmartPointer<T>.Create(AValue);
  53.    end;
  54.    
  55.    class operator TSmartPointer<T>.Implicit(smart: TSmartPointer<T>):T;
  56.    begin
  57.      Result := Smart.Value;
  58.    end;
  59.  
  60.    function TSmartPointer<T>.GetValue:T;
  61.    begin
  62.      if not Assigned(FFreeTheValue) then
  63.        Self := TSmartPointer<T>.Create(T.Create);
  64.      Result := FValue;
  65.    end;
  66. end.


« Last Edit: September 05, 2016, 01:19:30 pm by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #56 on: September 05, 2016, 12:52:00 pm »
There is quite some overhead, because the value of the interface instance is not the same value as self of the class instance.
So on every usage it needs to calculate self from the interface variable. The same calculation again and again.
Or the inverse calculation when the interface is created.

In the working case I presented above this is nonsense as well. The overhead is in creation/release and that is all. You could have known that, you are not a nobody. (Although you are trolling "a bit" lately IMO :) )
Specialize a type, not a var.

BeniBela

  • Hero Member
  • *****
  • Posts: 905
    • homepage
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #57 on: September 05, 2016, 03:04:46 pm »
In the working case I presented above this is nonsense as well. The overhead is in creation/release and that is all. You could have known that, you are not a nobody. (Although you are trolling "a bit" lately IMO :) )

Perhaps it changes with smartpointers

Look at how it calls a COM function, e.g. _Addref:

Code: [Select]
0000000000400208 488b00                   mov    (%rax),%rax
000000000040020B ff5008                   callq  *0x8(%rax)

WRPR_$SYSTEM_$$_TINTERFACEDOBJECT_$_IUNKNOWN_$_1_$_SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT
0000000000422C50 4883ef10                 sub    $0x10,%rdi
0000000000422C54 e90706ffff               jmpq   0x413260 <SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT>

That is four times the amount of calling an ordinary method of a class:

Code: [Select]
00000000004001F0 e88b300100               callq  0x413280 <SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT>

And the creation overhead is enormous, too, as it calls:

Code: [Select]
      procedure InitInterfacePointers(objclass: tclass;instance : pointer);

        var
          ovmt: PVmt;
          i: longint;
          intftable: pinterfacetable;
          Res: pinterfaceentry;
        begin
          ovmt := PVmt(objclass);
          while assigned(ovmt) and (ovmt^.vIntfTable <> @emptyintf) do
            begin
              intftable:=ovmt^.vIntfTable;
              if assigned(intftable) then
              begin
                i:=intftable^.EntryCount;
                Res:=@intftable^.Entries[0];
                while i>0 do begin
                  if Res^.IType = etStandard then
                    ppointer(@(pbyte(instance)[Res^.IOffset]))^:=
                      pointer(Res^.VTable);
                  inc(Res);
                  dec(i);
                end;
              end;
              ovmt:=ovmt^.vParent;
            end;
        end;

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #58 on: September 05, 2016, 04:37:59 pm »
That's in any language. Nothing to do with Object Pascal. You know that too ;)
Look at calls through variants that's even worse.
OTOH, otherwise the programmer needs to do a lot of that stuff.
And yes, my smartpointer example is a view into the very near future when that overhead is just on creation and destroy. Just try it.
Any automatic memory maintenance comes with its problems, including garbage collected languages.
Under ARC, of which the smartpointer example is a part of preliminary necessity (before it ends up in system and used through compiler magic) , this is less so than with sweep/release as under Java and .Net.

Also note that e.g. for Lazarus GUI's atm it suffices to have TAppication under smartpointer control since the design governs that ultimately all GUI objects are owned by the application and as such its smartpointer. And release is by Owner, just as it is now. What overhead ;) (Ok, that's trolling by me  8-) O:-) )
« Last Edit: September 05, 2016, 06:15:28 pm by Thaddy »
Specialize a type, not a var.

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Quick Modern Object Pascal Introduction, for Programmers
« Reply #59 on: September 05, 2016, 04:41:13 pm »
In the working case I presented above this is nonsense as well. The overhead is in creation/release and that is all. You could have known that, you are not a nobody. (Although you are trolling "a bit" lately IMO :) )

Perhaps it changes with smartpointers

Look at how it calls a COM function, e.g. _Addref:

Code: [Select]
0000000000400208 488b00                   mov    (%rax),%rax
000000000040020B ff5008                   callq  *0x8(%rax)

WRPR_$SYSTEM_$$_TINTERFACEDOBJECT_$_IUNKNOWN_$_1_$_SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT
0000000000422C50 4883ef10                 sub    $0x10,%rdi
0000000000422C54 e90706ffff               jmpq   0x413260 <SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT>

That is four times the amount of calling an ordinary method of a class:

Code: [Select]
00000000004001F0 e88b300100               callq  0x413280 <SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT>


I only see one call and then a jump to right method.

And the creation overhead is enormous, too, as it calls:

Code: [Select]
      procedure InitInterfacePointers(objclass: tclass;instance : pointer);

        var
          ovmt: PVmt;
          i: longint;
          intftable: pinterfacetable;
          Res: pinterfaceentry;
        begin
          ovmt := PVmt(objclass);
          while assigned(ovmt) and (ovmt^.vIntfTable <> @emptyintf) do
            begin
              intftable:=ovmt^.vIntfTable;
              if assigned(intftable) then
              begin
                i:=intftable^.EntryCount;
                Res:=@intftable^.Entries[0];
                while i>0 do begin
                  if Res^.IType = etStandard then
                    ppointer(@(pbyte(instance)[Res^.IOffset]))^:=
                      pointer(Res^.VTable);
                  inc(Res);
                  dec(i);
                end;
              end;
              ovmt:=ovmt^.vParent;
            end;
        end;

Nowadays CPU's are quite fast so there shouldn't have noticeable slowdowns on (massive) usage of interfaces.

 

TinyPortal © 2005-2018