Client-Side C++ Mapping for Interfaces
Proxy Classes
On the client side, a Slice interface maps to a C++ proxy class with member functions that correspond to the operations on that interface. Consider the following Slice interface:
module M
{
interface Simple
{
void op();
}
}
The Slice compiler generates the following definitions for use by the client:
class SimplePrx : public Ice::Proxy<SimplePrx, Ice::ObjectPrx>
{
public:
// Constructors
SimplePrx(
const Ice::CommunicatorPtr& communicator,
std::string_view proxyString);
SimplePrx(const SimplePrx& other) noexcept;
SimplePrx(SimplePrx&& other) noexcept;
// Assignment operators.
SimplePrx& operator=(const SimplePrx& rhs) noexcept;
SimplePrx& operator=(SimplePrx&& rhs) noexcept;
// Member functions mapped from Slice operation op.
void op(const Ice::Context& = Ice::noExplicitContext) const;
[[nodiscard]] std::future<void> opAsync(
const Ice::Context& context = Ice::noExplicitContext) const;
std::function<void()> opAsync(
std::function<void()> response,
std::function<void(std::exception_ptr)> exception = nullptr,
std::function<void(bool)> sent = nullptr,
const Ice::Context& context = Ice::noExplicitContext) const;
};
Your client code interacts directly with the proxy class, M::SimplePrx in the example above. More generally, the generated proxy class for an interface in module M is the C++ proxy class M::<interface-name>Prx.
In the client's address space, an instance of the proxy class is the local ambassador for a remote instance of an Ice object that implements Simple and is known as a proxy class instance, or simply proxy. All the details about the server-side object, such as its address, what transport to use, and its object identity are encapsulated in that instance.
Notice that all proxy member functions are const – proxy instances are immutable.
The Ice::Proxy template is a mix-in class that adds functionality to the proxy class via inheritance. It derives from the provided base proxy classes (here, only Ice::ObjectPrx):
template<typename Prx, typename... Bases>
class Proxy : public virtual Bases...
{
// Helper functions for Prx
}
It’s an instance of the Curiously Recurring Template Pattern (CRTP).
Creating a Proxy
Use the constructor of the generated class to create a proxy from a communicator and a “stringified” proxy. For example:
M::SimplePrx simple{communicator, "simple:tcp -h localhost -p 4061"};
A proxy is a stack-allocated C++ object.
The proxy’s constructor does not allow you to create a “null” proxy. A nullable proxy - and by extension a null proxy - is a proxy held in a std::optional. For example:
// A nullable Simple proxy, default-initialized to std::nullopt.
std::optional<M::SimplePrx> simple;
Inheritance from Ice::ObjectPrx
All generated proxy classes inherit indirectly from the Ice::ObjectPrx class, reflecting the fact that all Slice interfaces implicitly inherit from Object.
Interface Inheritance
Inheritance relationships among Slice interfaces are maintained in the generated C++ classes. For example:
module M
{
interface A { ... }
interface B { ... }
interface C extends A, B { ... }
}
The generated code for CPrx reflects the inheritance hierarchy:
namespace M
{
class CPrx : public Ice::Proxy<CPrx, APrx, BPrx>
{
...
};
}
Given a proxy for C, a client can invoke any operation defined for interface C, as well as any operation inherited from C's base interfaces.
Proxy Factory Methods
The base proxy class Ice::ObjectPrx supports a variety of methods for customizing a proxy. Since proxies are immutable, each of these "factory methods" returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second invocation timeout as shown below:
GreeterPrx greeter{communicator, "greeter:tcp -h localhost -p 4061"};
// Create a new GreeterPrx and assign it to greeter.
greeter = greeter.ice_invocationTimeout(10000);
The factory methods usually return a proxy of the same type as the current proxy, as in the example above.
The only exceptions are the factory methods ice_facet and ice_identity. Calls to either of these functions may produce a proxy for an object of an unrelated type, and you need to supply the desired proxy type when you call them. For example:
GreeterPrx greeter{communicator, "greeter:tcp -h localhost -p 4061"};
GreeterAdminPrx greeterAdmin = greeter.ice_facet<GreeterAdminPrx>("admin");