Announcement

Collapse
No announcement yet.

passing interfaces - no type checking?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #21
    So why (does PB) dress it up as something it is not - either a specific (and by implication unique) object reference or a specific interface reference.
    Well, that was just a contrived example of mine to see just what the behaviours were of interfaces as function parameters. This is, after all, somewhat silly...

    Code:
    Sub Foo(ifx As I_X)
      Call ifx.Fx1(75)
    End Sub
    ...and is just a wrapper on Fx1(). However, in real apps I can think of it being reasonable to pass an object to various functions. The thing to do though rather than passing a specific interface might be to pass a generic IUnknown pointer which could be used in other procedures to acquire the specific interface required there. I'll provide both a PowerBASIC & C++ version of this using my CA class from my example in Jose's forum mentioned previously. First the C++ version...

    Code:
    //CAClient.cpp 
    #include <windows.h>
    #include <stdio.h>
    const CLSID CLSID_CA={0x20000000,0x0000,0x0000,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4}};
    const IID   IID_I_X ={0x20000000,0x0000,0x0000,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5}};
    const IID   IID_I_Y ={0x20000000,0x0000,0x0000,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6}};
    
    
    interface I_X : IUnknown
    {
     virtual HRESULT __stdcall Fx1(int)=0;
     virtual HRESULT __stdcall Fx2(int)=0;
    };
    
    
    interface I_Y : IUnknown
    {
     virtual HRESULT __stdcall Fy1(int)=0;
     virtual HRESULT __stdcall Fy2(int)=0;
    };
    
    
    void Foo(IUnknown* pIUnk, int ifDesired)
    {
     if(ifDesired==1)  //ifDesired = interface desired
     {
        I_X* pIX=NULL;
        pIUnk->QueryInterface(IID_I_X,(void**)&pIX);
        pIX->Fx1(1);
        pIX->Fx2(1);
        pIX->Release();
     }
     if(ifDesired==2)  //ifDesired = interface desired
     {
        I_Y* pIY=NULL;
        pIUnk->QueryInterface(IID_I_Y,(void**)&pIY);
        pIY->Fy1(2);
        pIY->Fy2(2);
        pIY->Release();
     }
    }
    
    
    int main(int argc, char* argv[])
    {
     IUnknown* pIUnknown=NULL;
     HRESULT hr;
      
     CoInitialize(NULL);
     hr=CoCreateInstance(CLSID_CA,NULL,CLSCTX_SERVER,IID_IUnknown,(void**)&pIUnknown);
     if(SUCCEEDED(hr))
     {
        Foo(pIUnknown,1);
        Foo(pIUnknown,2);
        pIUnknown->Release();
     }
     CoUninitialize();
    
     return 0;
    }
    
    /*
    Called CA::QueryInterface() For IID_IUnknows
    Called CA::AddRef()
    Called CA::AddRef()
    Called CA::Release()
    Called CA::QueryInterface() For IID_IUnknows
    Called CA::AddRef()
    Called CA::Release()
    Called CA::QueryInterface() For IID_I_X
    Called CA::AddRef()
    Called Fx1()  :  iNum = 1
    Called Fx2()  :  iNum = 1
    Called CA::Release()
    Called CA::QueryInterface() For IID_I_Y
    Called CA::AddRef()
    Called Fy1()  :  iNum = 2
    Called Fy2()  :  iNum = 2
    Called CA::Release()
    Called CA::Release()
    */
    And the PowerBASIC version...

    Code:
    #Compile               Exe "CAClient.exe"
    #Dim                   All
    #Include               "Win32Api.inc"
    $CLSID_CA              =Guid$("{20000000-0000-0000-0000-000000000004}")  'Class ID of Class CA, ie., Class A
    $IID_IX                =Guid$("{20000000-0000-0000-0000-000000000005}")  'Interface X
    $IID_IY                =Guid$("{20000000-0000-0000-0000-000000000006}")  'Interface Y
    
    
    Interface I_X $IID_IX : Inherit IUnknown
      Method Fx1(ByVal iNum As Long) As Long
      Method Fx2(ByVal iNum As Long) As Long
    End Interface
    
    
    Interface I_Y $IID_IY : Inherit IUnknown
      Method Fy1(ByVal iNum As Long) As Long
      Method Fy2(ByVal iNum As Long) As Long
    End Interface
    
    
    Sub Foo(unk As IUnknown, ifDesired As Long)
      If ifDesired=1 Then
         Local ix As I_X
         ix=unk
         Call ix.Fx1(1)
         Call ix.Fx2(1)
         Set ix=Nothing
      End If
      If ifDesired=2 Then
         Local iy As I_Y
         iy=unk
         Call iy.Fy1(2)
         Call iy.Fy2(2)
         Set iy=Nothing
      End If
    End Sub
    
    
    Function PBMain() As Long
      Local IUnk As IUnknown
    
      IUnk=NewCom Clsid $CLSID_CA
      Call Foo(IUnk,1)
      Call Foo(IUnk,2)
      Set IUnk=Nothing
      Waitkey$
    
      PBMain=0
    End Function
    
    'Called CA::QueryInterface() For IID_IUnknows
    'Called CA::AddRef()
    'Called CA::AddRef()
    'Called CA::Release()
    'Called CA::QueryInterface() For IID_IUnknows
    'Called CA::AddRef()
    'Called CA::Release()
    'Called CA::QueryInterface() For IID_I_X
    'Called CA::AddRef()
    'Called Fx1()  :  iNum = 1
    'Called Fx2()  :  iNum = 1
    'Called CA::Release()
    'Called CA::QueryInterface() For IID_I_Y
    'Called CA::AddRef()
    'Called Fy1()  :  iNum = 2
    'Called Fy2()  :  iNum = 2
    'Called CA::Release()
    As you can see, Foo() was modified to take a generic IUknown pointer, and an integer arguement to specify which interface was desired. Within the function the IUnknown pointer was cast to the desired specific interface. Pretty neat that the output from both programs is exactly the same.

    Notice in the C++ program that pIUnknown in the CreateInstance() call is an output parameter typed as a (void**)&pIUnknown. That is most definitely just a raw number with no inherent type as far as C++ is concerned, and by typeing it as such it doesn't leave much doubt as to what it is.
    Last edited by Fred Harris; 15 Feb 2009, 09:13 PM.
    Fred
    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

    Comment


    • #22
      Originally posted by Fred Harris View Post
      As you can see, Foo() was modified to take a generic IUknown pointer, and an integer arguement to specify which interface was desired.
      So Foo can test the interface and reject -at run time - an invalid interface. I would rather know that an invalid interface call was rejected in compilation, than risk the future of civilisation when my home-built PowerBasic-powered nanobot goes ape.

      Alternatively, one would have to rely on a convention - such as calling a method to return the interface name - to give a practical level of confidence that the correct interface had been passed. Such conventions rely on human factors and are thus inherently prone to failure. Enjoy civilisation while you can, Fred!.

      Comment


      • #23
        >OOP and COM - though closely related, are not exactly the same

        Good observervation although I'm not so sure they are even that closely related, since you can use the Component Object Model architecture without using Object-Oriented Programming syntax and techniques.

        eg. Jose Roca did a lot of work and has provided a bunch of code to do exactly that... for use when the PB compilers did not support O-O-P.
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #24
          I'd rather know that an invalid interface call was rejected in compilation
          Well, that's easy to achieve with normal function calls. COM isn't for everybody and everything. It was designed in the early 90s to solve some extremely vexing problems; problems actually the significance of which rivaled the complexity of designing GUIs operating systems. And those problems were the dynamic composition (at run-time) of computer systems using binary components, and the safe versioning and upgrading of software. Although that was the end result; not the initial more modest though still difficult goal, i.e., "How in the world do you integrate an Excel spreadsheet into a Word document?"

          Here's another version of the above program that shows a little bit better testing at run time of what is going on. It uses the dll in only slightly modified form downloadable from my tutorial at...

          http://www.jose.it-berater.org/smffo...p?topic=2980.0

          Code:
          'code causing output statements from within dll...
          
          'HRESULT __stdcall CA::QueryInterface(REFIID riid, void** ppv)
          '{
          ' *ppv=0;
          ' if(riid==IID_IUnknown)
          ' {
          '    *ppv=(I_X*)this;
          '    printf("Called CA::QueryInterface() For IID_IUnknows\n");
          ' }
          ' else if(riid==IID_I_X)
          ' {
          '    *ppv=(I_X*)this;
          '    printf("Called CA::QueryInterface() For IID_I_X\n");
          ' }
          ' else if(riid==IID_I_Y)
          ' {
          '    *ppv=(I_Y*)this;
          '    printf("Called CA::QueryInterface() For IID_I_Y\n");
          ' }
          ' if(*ppv)
          ' {
          '    AddRef();
          '    return S_OK;
          ' }
          ' printf("Called CA::QueryInterface()\n");
          '
          ' return(E_NOINTERFACE);
          '}
          '
          #Compile               Exe "CAClient.exe"
          #Dim                   All
          #Include               "Win32Api.inc"
          $CLSID_CA              =Guid$("{20000000-0000-0000-0000-000000000004}")  'Class ID of Class CA, ie., Class A
          $IID_IX                =Guid$("{20000000-0000-0000-0000-000000000005}")  'Interface X
          $IID_IY                =Guid$("{20000000-0000-0000-0000-000000000006}")  'Interface Y
          $IID_IZ                =Guid$("{20000000-0000-0000-0000-000000000007}")  'Interface Z
          $IID_PieInTheSky       =Guid$("{20000000-0000-0000-0000-000000000009}")  'Interface PieInTheSky
          
          
          Interface I_X $IID_IX : Inherit IUnknown
            Method Fx1(ByVal iNum As Long) As Long
            Method Fx2(ByVal iNum As Long) As Long
          End Interface
          
          
          Interface I_Y $IID_IY : Inherit IUnknown
            Method Fy1(ByVal iNum As Long) As Long
            Method Fy2(ByVal iNum As Long) As Long
          End Interface
          
          
          Interface I_Z $IID_IZ : Inherit IUnknown
            Method Fz1(ByVal iNum As Long) As Long
            Method Fz2(ByVal iNum As Long) As Long
          End Interface
          
          
          Sub Bar(IUnknwn As IUnknown, xIID As Guid)
            Select Case xIID
              Case $IID_IX
                Local ix As I_X
                Print "xIID = $IID_IX"
                ix=IUnknwn
                If IsObject(ix) Then
                  Print "ix is a ledgitimate object, and we are now in the I_X VTable!"
                  Call ix.Fx1(1) : Call ix.Fx2(1)
                  Set ix=Nothing
                Else
                  Print "This IUnknown has no knowledge of " GuidTxt$(xIID)
                End If
              Case $IID_IY
                Local iy As I_Y
                Print "xIID = $IID_IY"
                iy=IUnknwn
                If IsObject(iy) Then
                  Print "iy is a ledgitimate object!"
                  Call iy.Fy1(2) : Call iy.Fy2(2)
                  Set iy=Nothing
                Else
                  Print "This IUnknown has no knowledge of " GuidTxt$(xIID)
                End If
              Case $IID_IZ
                Local iz As I_Z
                Print "xIID = $IID_IZ"
                iz=IUnknwn
                If IsObject(iz) Then
                  Print "iz is a ledgitimate object!"
                  Call iz.Fz1(2) : Call iz.Fz2(2)
                  Set iz=Nothing
                Else
                  Print "You Need To Pay Me Some Extra Bucks If You Want "
                  Print "To Use " GuidTxt$(xIID)
                End If
              Case Else
                Print "...And If You Want This You Had Better Really Open Your Wallet!"
            End Select
          End Sub
          
          
          Function PBMain() As Long
            Local IUnk As IUnknown
          
            IUnk=NewCom Clsid $CLSID_CA
            If IsObject(IUnk) Then
               Call Bar(IUnk,$IID_IX)
               Call Bar(IUnk,$IID_IY)
               Call Bar(IUnk,$IID_IZ)
               Call Bar(IUnk,$IID_PieInTheSky)
               Set IUnk=Nothing
            Else
               Print $CLSID_CA "Couldn't Be Instantiated!"
            End If
            Waitkey$
          
            PBMain=0
          End Function
          
          'Called CA::QueryInterface() For IID_IUnknows
          'Called CA::AddRef()
          'Called CA::AddRef()
          'Called CA::Release()
          'Called CA::QueryInterface() For IID_IUnknows
          'Called CA::AddRef()
          'Called CA::Release()
          'xIID = $IID_IX
          'Called CA::QueryInterface() For IID_I_X
          'Called CA::AddRef()
          'ix is a ledgitimate object, and we are now in the I_X VTable!
          'Called Fx1()  :  iNum = 1
          'Called Fx2()  :  iNum = 1
          'Called CA::Release()
          'xIID = $IID_IY
          'Called CA::QueryInterface() For IID_I_Y
          'Called CA::AddRef()
          'iy is a ledgitimate object!
          'Called Fy1()  :  iNum = 2
          'Called Fy2()  :  iNum = 2
          'Called CA::Release()
          'xIID = $IID_IZ
          'Called CA::QueryInterface()
          'You Need To Pay Me Some Extra Bucks If You Want
          'To Use {20000000-0000-0000-0000-000000000007}
          '...And If You Want This You Had Better Really Open Your Wallet!
          'Called CA::Release()
          Fred
          "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

          Comment


          • #25
            A COM interface is simply a related group of functions. As long as it can provide pointers to these functions, you can implement it as you wish. Classes are just a convenient way to do it, with additional advantages such being able, with appropriate compiler support, of calling an interface method through its name, not its position in the array of pointers know as the virtual table.
            Last edited by José Roca; 16 Feb 2009, 10:59 AM.
            Forum: http://www.jose.it-berater.org/smfforum/index.php

            Comment


            • #26
              Originally posted by Fred Harris View Post
              ... the jump from standard object oriented programming to interface based programming ( COM ) is just about as painful as from procedural programming to OOP.
              That's not the kind of thing I was hoping to hear... wish someone had a roadmap for that journey!

              Comment


              • #27
                For years folks have been screeming at PowerBASIC - "We Need OOP!" "How Am I Gonna Compete Without COM?" "In Visual Basic We Did It This Way!"

                Remember the old saying about being careful about what you ask for because you just might get it!
                Fred
                "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                Comment


                • #28
                  Not to mention DirectX. Now that it can be used without wrappers, almost nobody seems interested in it.
                  Forum: http://www.jose.it-berater.org/smfforum/index.php

                  Comment


                  • #29
                    Originally posted by José Roca View Post
                    A COM interface is simply a related group of functions. As long as it can provide pointers to these functions, you can implement it as you wish. Classes are just a convenient way to do it...
                    Well said. If the goal was just Object programming, PowerBasic could have acheived it years ago - it's not rocket science, and has been around for decades. One can only surmise that they did not want to. I would guess that COM was the prize and OOP was the target of opportunity - which is not to slight their implementation if it - it's just COM-flavored, that's all.

                    Comment


                    • #30
                      Originally posted by Michael Mattias View Post
                      Is it my imagination, or is picking up this 'Object Stuff' easier for those of us (e.g. moi) who have NOT previously used a product featuring same than for those who have?
                      That's an interesting observation, and probably not your imagination. The real truth is that objects are fairly simple structures. But, in the past, they've been defined as having magical, mystical properties which destroy all evil on the planet. {smile} Comical, but almost true. I've read some amazing descriptions which sound incredible and are truly not credible. To those who've used objects before, some of these unusual definitions have surely had some impact. From a marketing perspective, there's a certain logic to this. If you can maintain a wall of difficulty or a wall of secrecy, then your programming tool looks like the "perfect and only solution".

                      With PowerBASIC, we've tried hard to puncture that wall. We've given simple and straightforward definitions without all the mystical overtones. It just may be that prior knowledge of objects deflects some of the real, underlying simplicity.

                      What's an object? A chunk of data private to the object, neatly packaged with a group of subroutines which manipulate the data and provide any other functionality you need. It's just that simple.

                      Data private to the object is known as Instance Variables. The definition of the group of subroutines is known as the Interface, and the definition of the entire object is known as the Class. The Class definition is used to create the object -- the object is said to be an instance of the Class.

                      What's a COM object? Everything the same as above, except that's it's possible to have multiple interfaces, as an option. Multiple groups of subroutines -- you get to pick and choose which one you wish to use at any particular time. Plus, the object can be located outside of your code module. That's it. The whole difference.

                      I'd like to recommend that all of our friends check out "The Life and Times of an Object", here at the top of the "Programming with Objects" forum.

                      Comment


                      • #31
                        Bob:

                        Thanks for compiling all those older posts into the 1 sticky. Makes it much easier for me.

                        Originally Posted by Michael Mattias
                        Is it my imagination, or is picking up this 'Object Stuff' easier for those of us (e.g. moi) who have NOT previously used a product featuring same than for those who have?
                        What makes it "easier" Mike is that you are learning it for the first time. Your "mental reflexes" aren't already set up. Someone never really exposed to OOP or COM can essentially treat this as a new feature set. Others are looking for ways to compare the PB version of OOP/COM with what they used before (in my case C++/C++ Builder/Delphi).

                        So my "instincts" were to look for the similar implementation/capabilities expecting just a syntax change "print" vs "printf" kind of thing.

                        While in reality, other than understanding the concept, treating everything about how PB (and other languages) implement OOP as a separate language feature makes it easier to learn. Precisely because the differences are such that there is no direct analog for many things (as different aspects of OOP are implemented).

                        It's like learning and driving for years here in the US where we drive on the right side of the road. And then suddenly going to one of those backwards countries where they drive on the left side of the road When a situation comes up where you have to react on instict (to avoid a collision) your instincts tell you to pull the wheel to the right, when you really have to pull the wheel to the left.

                        JS
                        John,
                        --------------------------------
                        John Strasser
                        Phone: 480 - 273 - 8798

                        Comment

                        Working...
                        X