Archived

This forum has been archived. Please start a new discussion on GitHub.

What is the "proxy" Slice syntax for?

Hi,

I'm finding it difficult to determine exactly what the new "proxy" type syntax in Slice means exactly.

Take my burgeoning Pengo example:
module Pengo
{
    struct Position
    {
	short x;
	long y;
    };

    interface GameElement
    {
	Position getPos();
    };

    interface Movable extends GameElement
    {
	void moveUp();
	void moveDown();
	void moveLeft();
	void moveRight();
    };

    interface Smashable extends GameElement
    {
	void smash();
    };

    interface Gettable extends GameElement
    {
	void get();
    };

    interface Block extends GameElement
    {
    };

    interface WoodenBlock extends Block
    {
    };

    interface BlockOfIce extends Block, Movable, Smashable, Gettable
    {
    };

    interface PlayerCharacter extends GameElement
    {
	void pickUp(Gettable* element);
    };

    interface Penguin extends Movable, PlayerCharacter
    {
    };
};

The important interface here is "PlayerCharacter" near the bottom. I obviously want to pass a reference (or proxy) to the "pickUp" operation, but why do I need the "*" syntax? What wrong with the IDL syntax ie just "void pickUp(Gettable element);"?

Now, I can appreciate that you may just want the syntax to reflect the nature of the argument passing more clearly, to avoid confusion with "pass-by-value" semantics.

Unfortunately, "void pickUp(Gettable element)" also compiles in Slice! And, according to "diff", different C++ code is produced for this. The documentation is completely silent on what this "plain" syntax means for interfaces. Perhaps this is not supposed to be allowed at all? If it *is*, what does it mean?

Comments

  • Re: What is the "proxy" Slice syntax for?
    Originally posted by dthomson
    Hi,

    I'm finding it difficult to determine exactly what the new "proxy" type syntax in Slice means exactly.

    Take my burgeoning Pengo example:

    module Pengo
    {
        // ...
    
        interface GameElement
        {
    	Position getPos();
        };
    
        // ...
    
        interface Gettable extends GameElement
        {
    	void get();
        };
    
        // ...
    
        interface PlayerCharacter extends GameElement
        {
    	void pickUp(Gettable* element);
        };
    
        // ...
      };
    

    The important interface here is "PlayerCharacter" near the bottom. I obviously want to pass a reference (or proxy) to the "pickUp" operation, but why do I need the "*" syntax? What wrong with the IDL syntax ie just "void pickUp(Gettable element);"?

    Now, I can appreciate that you may just want the syntax to reflect the nature of the argument passing more clearly, to avoid confusion with "pass-by-value" semantics.

    Unfortunately, "void pickUp(Gettable element)" also compiles in Slice! And, according to "diff", different C++ code is produced for this. The documentation is completely silent on what this "plain" syntax means for interfaces. Perhaps this is not supposed to be allowed at all? If it *is*, what does it mean?

    Consider the following (extending your example):
    class Thing implements Pengo::gettable {
        // State members here...
    };
    

    The class implements a gettable. So, when we pass a gettable, we have to decide whether we want to pass it by value or by reference because the run-time type of the argument may be something that is derived from gettable, and the derived thing could be a class instead of an interface.

    The difference in the generated code (in C++) is that a gettable* is passed as a gettablePrx, but a gettable is passed as a gettablePtr, giving you pass-by-reference and pass-by-value semantics, respectively.

    Now, let's assume that we have no derivation of anything (no derived classes or interfaces), and simply write something such as following:
    interface foo { /* ... */ };
    
    interface bar {
        void op(foo p);    // Note: foo, not foo*
    };
    

    In the client code, I get a foo proxy from somewhere and then try to pass that proxy as the parameter to op. It turns out that you cannot do that because a fooPrx is not type compatible with a fooPtr. The signature of op is:
    void op(const fooPtr &, const Ice::Context & = Ice::Context());
    

    When you look at the generated code, you will find that fooPrx is actually a typedef:
    typedef ::IceInternal::ProxyHandle< ::IceProxy::foo> fooPrx;
    

    So, we have a generated class called ::IceProxy::foo, which is the actual proxy instance that contains the code to invoke operations on the remote object, and we have fooPrx, which is a smart pointer to the underlying proxy instance. This also explains why you never have to call things such as duplicate() or release() in the C++ mapping. (The Prx handles are loosely analogous to CORBA _var references.)

    On the other hand, we have fooPtr, which is also a typedef:
    typedef ::IceInternal::Handle< ::foo> fooPtr;
    

    So, for pass-by-value, the formal parameter type is IceInternal::Handle<::foo>, but for pass-by-reference, the formal parameter type is IceInternal::ProxyHandle< ::IceProxy::foo>. As I mentioned previously, IceProxy::foo is the actual proxy class, whereas ::foo is the skeleton class from which you derive class and interface implementations. The two type hierarchies are in no way related, so there is no way to pass a proxy to an operation that uses pass-by-value semantics.

    The upshot of all of this is that, if you define a Slice parameter as foo, you *must* pass a class instance when invoking the operation, whereas, if you define Slice parameter as foo*, you *must* pass a proxy.


    Cheers,

    Michi.
  • Re: Re: What is the "proxy" Slice syntax for?
    Originally posted by michi

    [snip]

    The upshot of all of this is that, if you define a Slice parameter as foo, you *must* pass a class instance when invoking the operation, whereas, if you define Slice parameter as foo*, you *must* pass a proxy.

    Thanks for the explanation. I suspected that was it after I read the section on Slice classes. However, when you're reading the documentation in order, you can't know that there is that other possibility yet, and so the "*" syntax is confusing.

    Some kind of forward reference is definitely needed. Perhaps something briefly outlining that there are also classes that can also implement interfaces, hence the conflict, and that "we'll get to that later in the class section".

    Then I think the readers brain will just push the question of what the "*" is disambiguating onto their mental stack until later ...

    Regards,
    Derek.