View Single Post
  #2 (permalink)  
Old 03-05-2003
michi's Avatar
michi michi is offline
ZeroC Staff
 
Name: Michi Henning
Organization: ZeroC
Project: Ice
 
Join Date: Feb 2003
Location: Brisbane, Australia
Posts: 907
Re: What is the "proxy" Slice syntax for?

Quote:
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:

Code:
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):

Code:
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:

Code:
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:

Code:
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:

Code:
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:

Code:
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.
Reply With Quote