Sign in Join

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

Mailing lists / fpc-pascal / 2016-09

corba interfaces - is/as not working properly

22 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

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

David Emerson

Hi all,

I'm testing out CORBA interfaces before changing all my code to use them (I would prefer not to have managed types for interfaces).

However I am running into a glitch. Testing "if (my_object is i_some_interface)" is always returning true, even when the object does not implement the interface. Worse, following the false positive, (my_object as i_some_interface) seems to be returning a *different* interface. Proceeding to use that interface runs functions from the wrongly returned interface, rather than the requested function.

Code below followed by sample output.

program corba_bug;
{$mode objfpc}{$H+}
{$interfaces corba}

uses
classes,

   sysutils;
type
   i_hello = interface
     procedure hello;
     end;
   i_goodbye = interface
     procedure goodbye;
     end;
   i_nonsense = interface
     procedure nonsense;
     end;
   t_fellow = class (TObject, i_hello, i_goodbye) // no nonsense
     procedure hello;
     procedure goodbye;
     end;
procedure t_fellow.hello;
   begin
     writeln ('hello');
   end;
procedure t_fellow.goodbye;
   begin
     writeln ('goodbye');
   end;
var
   fellow : t_fellow;
   obj : TObject;
   h : i_hello;
   g : i_goodbye;
begin
   fellow := t_fellow.Create;
   obj := fellow;
   writeln ('expect 4x hello:');
   fellow.hello;
   h := fellow;
   h.hello;
   (fellow as i_hello).hello;
   if (obj is i_hello) then begin
     h := obj as i_hello;
     h.hello;
     end;
   writeln;
   writeln ('expect 4x goodbye:');
   fellow.goodbye;
   g := fellow;
   g.goodbye;
   // BROKEN - the two below are giving 'hello' rather than 'goodbye'
   (fellow as i_goodbye).goodbye;
   if (obj is i_goodbye) then begin
     g := obj as i_goodbye;
     g.goodbye;
     end;
   writeln;
   if (fellow is i_nonsense) then begin
     // BROKEN - execution is entering here though it shouldn't
     writeln ('Problem: (fellow is i_nonsense) returned true');
     (fellow as i_nonsense).nonsense; // printing 'hello'
     end;
   fellow.free;
end.

Free Pascal Compiler version 3.0.0 [2015/12/05] for x86_64 Copyright (c) 1993-2015 by Florian Klaempfl and others Target OS: Linux for x86-64

Sample output:

expect 4x hello:
hello
hello
hello
hello

expect 4x goodbye:
goodbye
goodbye
hello
hello

Problem: (fellow is i_nonsense) returned true
hello

Graeme Geldenhuys
to David Emerson
David Emerson wrote
I'm testing out CORBA interfaces before changing all my code to use them (I would prefer not to have managed types for interfaces).

I've been using CORBA style interfaces for years, and it was always worked well for me. I must add, I never use the is/as syntax though (for COM or CORBA style interfaces). I always use Supports(..) as in:

 var
   h: i_hello;
 begin
   // fellow is the class instance variable.
   if Supports(fellow, i_hello, h) then
      h.hello;

The Supports(..) syntax just seems more logical to me than the is/as syntax. The latter assumes the object "is" an instance of one of the interfaces and simply typecasts it to a interface variable. The Supports(..) syntax queries the object instance and asks if it supports a specific interface.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Lars
to Graeme Geldenhuys
Graeme Geldenhuys wrote 7 lines
On 2016-09-28 08:38, David Emerson wrote: > I'm testing out CORBA interfaces before changing all my code to use > them (I would prefer not to have managed types for interfaces). > I've been using CORBA style interfaces for years,

Curious, where do corba interfaces come in handy ? When can you use them that object oriented code won't offer the same features? I'm wondering if maybe GoLang interfaces are so popular because of similar reasons to the heavily underused and infamous interfaces available in delphi/fpc for years.

i.e. what's the basic point of using an interface compared to objects and inheritance and procedures? Where do they really shine?

In goLang (don't mean to make this off topic) interfaces are very similar to VarArgs or Variants but not as evil. I mean to keep this on topic by asking specifically what corba interfaces should be used for? Also do they work across DLL's or DSO's sort of like microsoft COM?

Michael Van Canneyt
to Lars
Lars wrote 23 lines
On Wed, September 28, 2016 2:24 am, Graeme Geldenhuys wrote: > On 2016-09-28 08:38, David Emerson wrote: > >> I'm testing out CORBA interfaces before changing all my code to use >> them (I would prefer not to have managed types for interfaces). >> > > I've been using CORBA style interfaces for years, Curious, where do corba interfaces come in handy ? When can you use them that object oriented code won't offer the same features? I'm wondering if maybe GoLang interfaces are so popular because of similar reasons to the heavily underused and infamous interfaces available in delphi/fpc for years. i.e. what's the basic point of using an interface compared to objects and inheritance and procedures? Where do they really shine? In goLang (don't mean to make this off topic) interfaces are very similar to VarArgs or Variants but not as evil. I mean to keep this on topic by asking specifically what corba interfaces should be used for? Also do they work across DLL's or DSO's sort of like microsoft COM?

They do not work with COM. Basically, you should use CORBA interfaces if you want to use interfaces, but are not interested in the reference counting mechanism that comes implicitly with the COM interfaces.

The classes unit has an example of CORBA interfaces: the observer pattern is implemented using CORBA interfaces.

Michael.

Graeme Geldenhuys
to Lars: Curious, where do corba interfaces come in handy ? When can you use them that object…
Lars wrote
i.e. what's the basic point of using an interface compared to objects and inheritance and procedures? Where do they really shine?

Not to repeat tons of information already available on the Internet - Google is your friend.

Objects, Inheritance and Interfaces all have their place, and for different needs. I like the fact that I can explicitly make available an API to use Objects, even if the objects are not directly related. I also like the fact that with interfaces, I can make the implementation private.

I also like to use Interfaces to help with unit testing. Without Interfaces I can only unit test public methods. I have to create a "friend class" (aka a class hack) to unit test protected methods. With Interfaces I don't need any "hacks" and can still unit test functionality of a class even if that implementation is in the private section of a class. I like to unit test code, but hate being forced to make things Public just to unit test them.

Others like the fact that COM Interfaces are reference counted - they sometimes come in handy, but do tend to complicate matters much more.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
David Emerson
to Lars: Curious, where do corba interfaces come in handy ? When can you use them that object…
Lars wrote
Curious, where do corba interfaces come in handy ? When can you use them that object oriented code won't offer the same features?

For multiple inheritence, since fpc does not support multiple inheritence without interfaces. Interfaces are an addition to object oriented code, not separate from it.

In my particular case, in a project of mine in which I am drawing lattices, I have a concept of a node. Sometimes I want to draw that node myself using AGGPAS, and sometimes I want that node to itself be a Window/Widget, descending from fpGUI classes. If I'm using AGGPAS to draw the node, then I don't want the node to descend from fpGUI -- I use my own lighter-weight class hierarchy. So in order to be able to treat these two things the same, I define "enclosing node" as an interface. Now I can have classes descending from two (or more) different hierarchies, which both implement the enclosing node interface. I can write functions to do things with enclosing nodes, and get the benefits of polymorphism with multiple inheritence.

All that said, interfaces have their quirks and I wish we had more native support for multiple inheritence (also templates but that's another story) but anyway I'll take what I can get :-)

~David.

David Emerson
to Graeme Geldenhuys: I've been using CORBA style interfaces for years, and it was always worked well for me. I…
Graeme Geldenhuys wrote 21 lines
On 2016-09-28 08:38, David Emerson wrote: > I'm testing out CORBA interfaces before changing all my code to use them > (I would prefer not to have managed types for interfaces). I've been using CORBA style interfaces for years, and it was always worked well for me. I must add, I never use the is/as syntax though (for COM or CORBA style interfaces). I always use Supports(..) as in: var h: i_hello; begin // fellow is the class instance variable. if Supports(fellow, i_hello, h) then h.hello; The Supports(..) syntax just seems more logical to me than the is/as syntax. The latter assumes the object "is" an instance of one of the interfaces and simply typecasts it to a interface variable. The Supports(..) syntax queries the object instance and asks if it supports a specific interface.

Thanks, Graeme. I tried this but I'm getting a compilation error: "Interface type i_hello has no valid GUID"

I assume this is the Supports function(s) in sysutils

Is it possible to use Supports / QueryInterface / GetInterface / something else, without having a GUID?

Alternately, if I give a GUID does an interface not automatically become COM + managed?

Thanks,
David

Lars
to Graeme Geldenhuys: Not to repeat tons of information already available on the Internet - Google is your…
Graeme Geldenhuys wrote
Others like the fact that COM Interfaces are reference counted - they sometimes come in handy, but do tend to complicate matters much more.

And Corba interfaces.. you have to implement that yourself?

I found this
http://www.lenholgate.com/blog/2001/02/corba---reference-counting.html

As for googling the info, sometimes I just find it easier to talk to a real programmer with experience than sifting through millions of pages of misinformation from google :-)

Graeme Geldenhuys
to David Emerson: Thanks, Graeme. I tried this but I'm getting a compilation error: "Interface type i_hello…
David Emerson wrote
Is it possible to use Supports / QueryInterface / GetInterface / something else, without having a GUID?

I'm not sure. I just had a look at my code. It seems all my [Corba] Interface have GUID's defined too. I never noticed - probably down to habit from my COM Interface days with Delphi 4-7.

Ctrl+Shift+G in Lazarus IDE should generate a GUID for you.

Alternately, if I give a GUID does an interface not automatically become COM + managed?

No definitely not! The GUID just helps the compiler or RTL identify an Interface.

Regards,
Graeme

David Emerson
to Graeme Geldenhuys
Graeme Geldenhuys wrote 6 lines
On 2016-09-28 22:59, David Emerson wrote: > Alternately, if I give a GUID does an interface not automatically become > COM + managed? No definitely not! The GUID just helps the compiler or RTL identify an Interface.

Oh fabulous! Thanks much. I definitely got a wrong impression on that one.

Ctrl+Shift+G in Lazarus IDE should generate a GUID for you.

thanks, very handy :)

~David.

Martin Schreiber
to Lars: Curious, where do corba interfaces come in handy ? When can you use them that object…
Lars wrote 8 lines
On Wed, September 28, 2016 2:24 am, Graeme Geldenhuys wrote: > On 2016-09-28 08:38, David Emerson wrote: >> I'm testing out CORBA interfaces before changing all my code to use >> them (I would prefer not to have managed types for interfaces). > > I've been using CORBA style interfaces for years, Curious, where do corba interfaces come in handy ?

Assume there is a support-function or -class, for example TStatFiler from MSEgui, which stores and reads values to/from streams. It must call the appropriate read and write functions in the client classes. It is possible to define the functions in a common ancestor of the clients as "virtual abstract" which enforces a single client-class hierarchy which often is impossible to achieve.
The solution are interfaces but not the default COM-interfaces with their reference counting nightmare but the not reference counted CORBA-interfaces - which is a wrong name BTW because they have nothing to do with CORBA.

Clients of TStatReader/TStatWriter must implement the interface:
"
 istatfile = interface(iobjectlink)[miid_istatfile]
  procedure dostatread(const reader: tstatreader);
  procedure dostatwrite(const writer: tstatwriter);
  procedure statreading;
  procedure statread;
  function getstatvarname: msestring;
  function getstatpriority: integer;
 end;
"
iobjectlink provides basic object communication functionality:
"
 iobjectlink = interface(inullinterface)
  procedure link(const source,dest: iobjectlink; valuepo: pointer = nil;
                        ainterfacetype: pointer = nil; once: boolean = false);
  procedure unlink(const source,dest: iobjectlink; valuepo: pointer = nil);
               //source = 1 -> dest destroyed
  procedure objevent(const sender: iobjectlink; const event: objecteventty);
  function getinstance: tobject;
 end;
"
inullinterface is an empty CORBA-style interface:
"
{$interfaces corba}
 inullinterface = interface
  //no referencecount
 end;
"
If a client registers at TStatFiler it provides its "istatfile" pointer which
will be stored in a list in TStatFiler. TStatfiler will use this list in
order to call the necessary functions and procedures of the clients if
reading/writing has been triggered.

MSEgui extensively uses CORBA-interfaces.

Martin

Martin Schreiber
to David Emerson: Oh fabulous! Thanks much. I definitely got a wrong impression on that one. thanks, very…
David Emerson wrote 14 lines
On 09/28/2016 03:31 PM, Graeme Geldenhuys wrote: > On 2016-09-28 22:59, David Emerson wrote: >> Alternately, if I give a GUID does an interface not automatically become >> COM + managed? > > No definitely not! The GUID just helps the compiler or RTL identify an > Interface. Oh fabulous! Thanks much. I definitely got a wrong impression on that one. > Ctrl+Shift+G in Lazarus IDE should generate a GUID for you. thanks, very handy :)

Corba-style interfaces can use any string as ID so for local interfaces it is possible to use shorter ID-strings for better performance. MSEide constructs such ID's by RightClick-'Insert UID'. Example: "['jA']{49}". If the interface is listed in header of the queried class it is also possible to get the interface by compile time type conversion: "
type
{$interfaces corba}

 iabc = interface
  procedure a();
  procedure b();
  procedure c();
 end;
 tabc = class(tobject,iabc)
  protected
   procedure a();
   procedure b();
   procedure c();
 end;
var
 abc: tabc;
 abcintf: iabc;

[...]
abcintf:= iabc(abc);
"
which has the best performace.

Martin

Graeme Geldenhuys
to Martin Schreiber
Martin Schreiber wrote
abcintf:= iabc(abc); " which has the best performace.

But you must still remember to check if abcintf has been successfully assigned.

[personal opinion]
Hence I still prefer the Supports(..) call as it can be used directly in a boolean expression, the name itself makes it very clear what you are asking from the code, and it just reads better.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Martin Schreiber
to Graeme Geldenhuys
Graeme Geldenhuys wrote 8 lines
On 2016-09-29 06:42, Martin Schreiber wrote: > abcintf:= iabc(abc); > " > which has the best performance. But you must still remember to check if abcintf has been successfully assigned.

There will be a compiler error if "abc" does not implement "iabc".

Show quoted text 5 lines
[personal opinion] Hence I still prefer the Supports(..) call as it can be used directly in a boolean expression, the name itself makes it very clear what you are asking from the code, and it just reads better.

Step through the code with F7 and you will see what overhead it means. AFAIK the compiler does not optimise for compiletime known case.

Martin

Sven Barth
to Martin Schreiber
"Martin Schreiber" wrote 11 lines
On Thursday 29 September 2016 10:49:03 Graeme Geldenhuys wrote: > On 2016-09-29 06:42, Martin Schreiber wrote: > > abcintf:= iabc(abc); > > " > > which has the best performance. > > But you must still remember to check if abcintf has been successfully > assigned. > There will be a compiler error if "abc" does not implement "iabc".

Graeme meant that abc can still be Nil.

Regards,
Sven

David Emerson
to Sven Barth: Graeme meant that abc can still be Nil. Sven
Sven Barth wrote 15 lines
Am 29.09.2016 11:34 schrieb "Martin Schreiber" < >: > > On Thursday 29 September 2016 10:49:03 Graeme Geldenhuys wrote: > > On 2016-09-29 06:42, Martin Schreiber wrote: > > > abcintf:= iabc(abc); > > > " > > > which has the best performance. > > > > But you must still remember to check if abcintf has been successfully > > assigned. > > > There will be a compiler error if "abc" does not implement "iabc". Graeme meant that abc can still be Nil.

Or perhaps, var abc : TObject (or some other descendent passed as a variable) -- it may or may not support iabc, but we want to check, and do something with it if it does.

~David

Michalis Kamburelis
to Graeme Geldenhuys

This thread, and my own tests, showed that you need to assign GUIDs to interfaces to have reliable "is", even for CORBA interfaces.

I'm wondering, would it be possible to improve here things on the compiler side (at least only in FPC-specific modes or under some modeswitch), to make things safer?

Propositions:

1. The question "X is IMyInterface", when "IMyInterface" does not have a GUID, could fail to compile.

Just like right now "Supports(X, IMyInterface)" does not compile when "IMyInterface" does not have a GUID. This is better -- the presence of GUID is now, the "X is IMyInterface" seems like a trap, when the interfaces can have no GUIDs. Two interfaces without GUIDs are treated as equal, by the "is" operator. The example code that started this thread shows how bad are the consequences. I made my own little example, attaching.

2. I assume "as" operator has the same problem? So "X as IMyInterface" would benefit from the same safeguard.

3. How about going further, and just making the GUID required at every interface declaration? Since it's necessary to have reliable "is", "as", "Supports"...

4. And how about going even more further, and just generate an internal GUIDs (random, or based on a memory address) when no GUID is explicitly specified? This way "is", "as", "Supports" always just work. And it removes the need to do 1., 2., 3. above, of course.

Is there a drawback to doing this, that I don't see?

Regards,
Michalis

P.S. I have added to
http://michalis.ii.uni.wroc.pl/~michalis/modern_pascal_introduction/modern_pascal_introduction.html a section documenting it.

stdreamer
to Michalis Kamburelis
Michalis Kamburelis wrote 7 lines
4. And how about going even more further, and just generate an internal GUIDs (random, or based on a memory address) when no GUID is explicitly specified? This way "is", "as", "Supports" always just work. And it removes the need to do 1., 2., 3. above, of course. Is there a drawback to doing this, that I don't see?

You assume that the interfaces are
1) user controlled
2) is not used outside from your own environment.

Both valid use cases but invalid assumptions. GUID needs to stay user selectable otherwise you loose the ability to implement interfaces for 3rd party applications (banking, stock exchange, ms office etc). Interfaces are compiler agnostic you might import them to use a 3rd party library (eg. ADO) which has its own GUIDs that you must not change.

Michalis Kamburelis
to stdreamer

2016-09-30 18:19 GMT+02:00 stdreamer :

Show quoted text 20 lines
On 30/09/2016 17:24 μμ, Michalis Kamburelis wrote: > > 4. And how about going even more further, and just generate an > internal GUIDs (random, or based on a memory address) when no GUID is > explicitly specified? This way "is", "as", "Supports" always just > work. And it removes the need to do 1., 2., 3. above, of course. > > Is there a drawback to doing this, that I don't see? > You assume that the interfaces are 1) user controlled 2) is not used outside from your own environment. Both valid use cases but invalid assumptions. GUID needs to stay user selectable otherwise you loose the ability to implement interfaces for 3rd party applications (banking, stock exchange, ms office etc). Interfaces are compiler agnostic you might import them to use a 3rd party library (eg. ADO) which has its own GUIDs that you must not change.

I didn't say I want to remove user-selectable GUIDs from the language. I understand that e.g. when using COM interfaces for inter-process communication, you want to explicitly specify the GUIDs.

My point (4.) was to generate an internal GUID *when no GUID is explicitly specified by the programmer*. This way the interfaces would work more reliably ("is", "as", "Supports" working as expected) when you're fine with using them only internally in your application.

Right now, not specifying the GUID of your interface is a trap. You get something that compiles, without a warning, but "is" and "as" don't work correctly. See the examples in my email, or in the 1st mail in this thread. It would be nice to eliminate this trap:)

Michalis