Channels ▼
RSS

Using Standard C++ in the OMG C++ Mapping


April 2001 C++ Experts Forum/Object Interconnections


Introduction

In our previous columns [1 2], we explored some ideas for hypothetical alternative mappings of OMG IDL to C++ that use Standard C++ features. In particular, we covered two major areas of the OMG C++ mapping [3]:

  1. IDL data-type mapping: which defines how IDL data types, such as structs and sequences, are mapped into C++, including how they are passed between client and target object in operation invocations.
  2. Client-side interface mapping: which defines how IDL interfaces are mapped into C++ to support client applications invoking distributed objects.

Our primary design criteria for these alternative mappings are flexibility, ease of use, and performance. We showed how some IDL data types, such as string and array, map cleanly into Standard C++ types. However, we also showed that mapping other IDL types, such as sequence, into Standard C++ is not quite as straightforward due to trade-offs that must be made between performance and flexibility. Because a sequence can be mapped into multiple Standard C++ container types depending on how the application uses it, mapping interfaces that use sequence types require advanced Standard C++ features, such as member templates to maximize flexibility.

We finish our exploration of this topic in this column by covering the two remaining major areas of the OMG C++ mapping:

  1. Server-side interface mapping: which defines how IDL interfaces are mapped into C++ to support server applications implementing distributed objects.
  2. Pseudo-object mapping: CORBA includes interfaces for aspects of the ORB implementation, including the ORB interface itself, Request, Object, and named value lists (NVList). The interfaces of these pseudo-objects are special in that they sometimes are not defined entirely in IDL (e.g., some are defined in a C-like language). These pseudo-object interfaces must also be mapped into C++ to make them available for both client and server applications.

Below we explore hypothetical mappings using features of Standard C++ for these areas of CORBA. But first, let's also repeat our caveat from our previous column: we must stress the word "hypothetical," since there are currently no efforts within the OMG to define any new C++ mappings, nor do we necessarily condone the creation of any official efforts to do so.

Server-Side Interface Mapping

Before exploring issues related to using Standard C++ for the server side of the C++ mapping, we'll first describe the operation of the server side along with common development approaches for CORBA C++ server applications. A server is typically implemented in an OS process that hosts one or more object adapters, each of which hosts one or more servants [4]. The role of the servant is to incarnate, or provide an implementation for, one or more CORBA objects. The object adapter connects these servants to the ORB runtime and ensures that requests made by clients on target objects are dispatched to the appropriate servant. Both the ORB runtime and the standard CORBA object adapter, the POA (Portable Object Adapter), are provided as part of the development kit provided by your ORB supplier. Servants, on the other hand, are what you develop to implement your application's "business logic."

You can implement a servant in one of three ways:

  1. By deriving it from a server skeleton class. This is the most common method of implementing POA-based servants in C++. IDL compilers generate interface-specific server skeleton classes as abstract base classes. To implement a servant, you derive a class from a skeleton class and override the pure virtual methods representing the operations and attributes declared in the IDL interface. Skeletons also typically contain other ORB-specific operations that assist with method dispatch to these virtual functions.
  2. By tying it into a servant tie instance. A "tie" is an interface-specific servant template class whose method implementations merely delegate their invocations to another class instance. A "tie" derives from the regular interface-specific skeleton class described above. The "tied to" instance then carries out the request. Note that the "tied to" class need not have any inheritance relationship with any CORBA-related classes. This approach was popular with older versions of CORBA (before the POA was introduced in version 2.2), but because the POA supplies all the benefits of this approach through servant managers and default servants [4], the use of ties in C++ is less common.
  3. By using the DSI (Dynamic Skeleton Interface). The DSI allows you to write "generic servants" that can respond to requests made on any object regardless of its interface type. Rather than being derived from or tied into an interface-specific skeleton, it derives from a special skeleton designed specifically for dynamic dispatch. A DSI-based approach, while not very common, is essential for applications such as bridges or routers that must handle any request regardless of whether or not the interface type was known at application compile time.

To explore the server side, let's use the same interface that we used for our explanation of client-side mapping issues in our previous column:

interface A {
    typedef sequence<string> StrSeq;
    StrSeq match (in StrSeq values, in string pattern);
};

Following the rules of the OMG C++ Mapping, but using std::string for the IDL string type rather than char*, the generated server skeleton for this interface would be something similar to this:

class POA_A :
    public virtual PortableServer::ServantBase {
  public:
    virtual A::StrSeq match (const A::StrSeq &values,
                             const string &pat) = 0;
};

As we explained in our previous column, the best mapping for the StrSeq type shown here depends on how the application intends to use it. If the application intends to insert or remove elements from the middle of a StrSeq, it would be best to map it to std::list, otherwise std::vector will suffice. The way we provided for this type of flexibility on the client side was by using a template member function in the client-side proxy class, like this:

class A {
  public:
    typedef vector<string> StrSeq;

    template<typename Return, typename SeqArg>
    Return match (const SeqArg &values, const string &pat);
};

Applying the same approach to the server skeleton yields:

class POA_A :
    public virtual PortableServer::ServantBase {
  public:
    template<typename Return, typename SeqArg>
    virtual Return match (const SeqArg &values,
                            const string &pat) = 0;
};

Unfortunately, this is illegal in C++ because template member functions cannot be virtual. A less flexible approach is to instead make the skeleton class a template:

template<typename Return = A::StrSeq, typename SeqArg = A::StrSeq>
class POA_A :
    public virtual PortableServer::ServantBase {
  public:
    virtual Return match (const SeqArg &values,
                            const string &pat) = 0;
};

An application developer can then parameterize the skeleton base class when defining the servant class, like this:

class MyAServant : public virtual POA_A<std::list>
{
  public:
    std::list match (const A::StrSeq &values,
                            const string &pat) = 0;
};

Note also that this approach allows us to once again use default arguments for the template parameters. Using defaulted template parameters, which isn't allowed with the member template approach, helps make this approach easy to use.

Unfortunately, using a parameterized skeleton base class becomes hard if the IDL interface it represents depends on a number of different sequence types as operation arguments. This difficulty arises because each separate sequence parameter and return type requires a separate template parameter, resulting in a long and unwieldy template parameter list for the skeleton class. Using defaults for the template parameters doesn't always help, especially if the one you want to supply yourself is at the end of the list. Fortunately, such IDL interfaces are rare.

Still, there are other ways to address even those interfaces that make heavy use of multiple sequence types. One way is to have the IDL compiler generate a separate template class for each operation and attribute in each interface and then parameterize the template skeleton base class not with sequence types but with a type for each separate operation and attribute. For example, given interface A as shown above, the IDL compiler could generate the following support code:

namespace POA_A {
    // template class for the <B>match</B> operation
    template<typename Return, typename SeqArg>
    class Amatch {
      public:
        Return operator()(const SeqArg & values,
                                const string &pat) = 0;
    };

    // template class for the skeleton for the <B>A</B> interface
    template<typename match>
    class A : public virtual PortableServer::ServantBase {
      public:
        // ...

      private:
        match* m_match;
    };
}

To avoid name collisions, note that we define these templates within a namespace named POA_A. In the official OMG C++ mapping, POA_A is the name of the skeleton base class for interface A. We've decided to use this name for a different purpose in this hypothetical mapping. If you're a long-time user of the OMG C++ mapping, don't let the reuse of this name confuse you.

For each operation and attribute defined in IDL interface A, the POA_A::A template class holds a pointer data member to a function object instance that represents that operation or attribute. One way to initialize these data members is to make the POA_A::A constructor responsible for creating all the appropriate function object instances, but this would allow only default construction of these function objects. Another solution is to allow the application to construct all the function objects and pass them to the POA_A::A constructor, which is very flexible, but introduces memory management issues regarding who owns the function objects.

An interesting feature of this approach is that you, the application developer, need not derive any servant class from the skeleton class to create a concrete servant class. Instead, the skeleton classes themselves are concrete (just like the tie classes that are part of the official OMG C++ mapping) assuming you supply all the necessary operation and attribute template parameters.

While the approach outlined above is flexible, it suffers from at least two drawbacks:

  1. It's complicated. As shown here, the developer has to define a class for each separate operation and attribute in each interface they want to support. However, having the IDL compiler also generate default class types for each operation and attribute, and supplying those types as default template parameters on the skeleton class, could reduce some of this complexity.
  2. It makes the skeleton-private dispatching code tricky (but not impossible). Skeleton-private dispatching code, which is specific to each ORB supplier's implementation, assists the ORB and the POA in turning the CORBA request coming into the server into a C++ function invocation. This trickiness is caused by the fact that the actual type of the sequence parameters to each function object is unknown at compile-time, meaning that the unmarshaling and dispatching code can make very few assumptions regarding the actual type of the sequence parameter. Luckily, though, this would be a problem that your ORB supplier would have to solve, not you.
  3. Note, however, that this approach could negatively impact the performance of skeleton dispatching code, which in itself might be reason enough to avoid this approach altogether.

Finally, note that all of the skeleton parameterization approaches shown in this column could result in poor performance. This is especially true for sequences of simple types, such as long and enum, because each element would have to be unmarshaled individually rather than unmarshaling all elements at once, as the official OMG C++ mapping allows. However, it might be possible for your ORB supplier to overcome these performance hurdles by specializing the marshaling code for all sequences of these simple types.

Pseudo-Object Mappings

A CORBA pseudo-object is an object whose implementation is provided by your ORB supplier as part of their overall implementation. For example, the ORB itself is a pseudo-object, as are the Request, NVList, NamedValue, and Context types. These types are called pseudo-objects because they do not always follow the same invocation and life-cycle rules that normal CORBA objects follow.

Some pseudo-objects, such as the ORB, are fat interfaces with many operations and nested types. They often use lots of sequence types. For such pseudo-objects, applying the sequence-related parameterization approaches that we've explored in this column and the previous one is hard due to the sheer number of different sequence types involved. Thus, heavy use of Standard C++ features in such pseudo-objects is unwieldy.

Other pseudo-objects, such as NVList, ExceptionList, and ContextList are simply containers and would just map to the appropriate Standard C++ container. For example, an NVList is simply a container for NamedValue instances, and it could be mapped to a std::vector. A NamedValue, on the other hand, is just a std::pair holding a string and a CORBA::Any. Applying Standard C++ for these types would make them much easier to use in CORBA applications. Note that both the CORBA DII (Dynamic Invocation Interface) and DSI use pseudo-objects extensively, so simplifying these pseudo-objects would have the side effect of also simplifying the use of the DII and DSI.

Concluding Remarks

This column describes several ways to define a CORBA C++ mapping using features of Standard C++ for server-side interfaces and for pseudo-objects. Due to column length restraints, our analysis of many areas of the mapping with respect to Standard C++ is incomplete, but our goal was to explain some of the possibilities, not to supply an exhaustive solution. We hope that our exploration of the issues, however brief, shows that getting maximum use out of Standard C++ for a CORBA C++ mapping is not trivial. Still, we have found this exploration interesting, as it has forced us to "think out of the box" and ignore traditional approaches to the C++ mapping in order to try to take full advantage of Standard C++. We hope you found this interesting as well.

Acknowledgements

Occasional conversations with Michi Henning over the past few years helped shape the contents of this column.

References

[1] D.C. Schmidt and S. Vinoski. "Object Interconnections: Standard C++ and the OMG C++ Mapping," C/C++ Users Journal C++ Experts Forum, January 2001, http://www.cuj.com/experts/1901/vinoski.html.

[2] D.C. Schmidt and S. Vinoski. "The History of OMG C++ Mapping," C/C++ Users Journal C++ Experts Forum, November 2000, http://www.cuj.com/experts/1811/vinoski.html.

[3] Object Management Group. IDL C++ Language Mapping. http://www.omg.org/technology/documents/formal/c++.htm, 1999.

[4] Michi Henning and Steve Vinoski. Advanced CORBA Programming with C++ (Addison Wesley, 1999).

Steve Vinoski is chief architect and vice president of Platform Technologies for IONA Technologies and is also an IONA Fellow. A frequent speaker at technical conferences, he has been giving CORBA tutorials around the globe since 1993. Steve helped put together several important OMG specifications, including CORBA 1.2, 2.0, 2.2, and 2.3; the OMG IDL C++ Language Mapping; the ORB Portability Specification; and the Objects By Value Specification. In 1996, he was a charter member of the OMG Architecture Board. He is currently the chair of the OMG IDL C++ Mapping Revision Task Force. He and Michi Henning are the authors of Advanced CORBA Programming with C++, published in January 1999 by Addison Wesley Longman.

Doug Schmidt is an associate professor member at the University of California, Irvine. His research focuses on patterns, optimization principles, and empirical analyses of object-oriented techniques that facilitate the development of high-performance, real-time distributed object computing middleware on parallel processing platforms running over high-speed networks and embedded system interconnects. He is the lead author of the book Pattern-Oriented Software Architecture: Patterns for Concurrent and Networked Objects, published in 2000 by Wiley and Sons. He can be contacted at schmidt@uci.edu.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.