Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Public Member Functions | Protected Attributes | Related Functions | List of all members
Teuchos::OpaqueWrapper< Opaque > Class Template Reference

Base class for wrapped opaque objects. More...

#include <Teuchos_OpaqueWrapper.hpp>

Inheritance diagram for Teuchos::OpaqueWrapper< Opaque >:
Teuchos::OpaqueWrapperWithFree< Opaque, OpaqueFree >

Public Member Functions

 OpaqueWrapper (Opaque opaque)
 Constructor that accepts and wraps a raw handle. More...
 
 operator Opaque () const
 Implicit type conversion from wrapper to raw handle. More...
 
Opaque operator() () const
 Explicit type conversion from wrapper to raw handle. More...
 

Protected Attributes

Opaque opaque_
 The actual handle. More...
 

Related Functions

(Note that these are not member functions.)

template<class Opaque >
RCP< OpaqueWrapper< Opaque > > opaqueWrapper (Opaque opaque)
 Create a new OpaqueWrapper object without a free function. More...
 
template<class Opaque , class OpaqueFree >
RCP< OpaqueWrapper< Opaque > > opaqueWrapper (Opaque opaque, OpaqueFree opaqueFree)
 Create a new OpaqueWrapper object with a free function. More...
 

Detailed Description

template<class Opaque>
class Teuchos::OpaqueWrapper< Opaque >

Base class for wrapped opaque objects.

Template Parameters
OpaqueType of the opaque object (a.k.a. handle).

Summary

If you want to create an RCP to an opaque handle (an instance of a type like MPI_Comm), use the opaqueWrapper() nonmember template function. If that opaque handle needs to be freed after all references to it go away, then supply a "free" function to opaqueWrapper(). The type returned by opaqueWrapper() is RCP<OpaqueWrapper<T> >, an RCP (reference-counted "smart" pointer) to a wrapper of the opaque handle type T. Users are not allowed to construct an OpaqueWrapper object explicitly. You must use the opaqueWrapper() nonmember function to do so.

Prerequisites

In order to understand this documentation, you must first have learned how to use RCP (Teuchos' reference-counted "smart" pointer class) to manage dynamically allocated memory and other resources. It also helps to be familiar with MPI (the Message Passing Interface for distributed-memory parallel programming), but this is not required.

What are opaque handles?

Many different software libraries use the opaque handle (a.k.a. opaque object) idiom to hide the internals of a data structure from users. This standard technique allows users to treat an instance of a data structure as a handle. Users may pass the handle around as if it were a simple value type (like int), and must call nonmember functions in order to create, operate on, use, or destroy instances of the data structure. The MPI (Message Passing Interface) standard is full of examples of the opaque handle idiom, including MPI_Comm (for communicators), MPI_Datatype (for standard and custom data types), and MPI_Op (for standard and custom reduction operations).

In general, opaque handles (corresponding to the Opaque template parameter) must be assignable. This means that copy construction and assignment (operator=) must be syntactically correct for instances of Opaque. This is certainly true of MPI's opaque handle types.

Opaque handles are a useful technique, but they interfere with correct use of reference-counted "smart" pointer types such as Teuchos' RCP or std::shared_ptr. We will explain below why this is the case. The OpaqueWrapper base class allows opaque handles to be wrapped by a real object, whose address you can take. This is needed in order to wrap an opaque object in a RCP, for example.

Why do opaque handles need special treatment?

The OpaqueWrapper class was motivated by MPI's common use of the opaque handle idiom. For MPI, passing MPI_Comm, MPI_Datatype, and MPI_Op objects around by handles hides implementation details from the user. Handles also make it easier to access MPI functionality from Fortran, so that C, C++, and Fortran can all share the same handle mechanism. In fact, some MPI implementations (such as MPICH, at least historically if not currently) simply implement these handles all as integers. (As the MPI standard's advice to implementers suggests, such an implementation would likely maintain a table for each MPI process that maps the integer value to a pointer to the corresponding object.) For example, MPI_Comm might be a typedef to int, and MPI_COMM_WORLD might be a C preprocessor macro for a literal integer value:

typedef int MPI_Comm;
#define MPI_COMM_WORLD 42

In this case, the expression rcp(&MPI_COMM_WORLD) would not even compile, since one cannot take the address of an integer literal such as 42. (Remember that preprocessor macros get replaced with their values before the C++ compiler does its work.) To make this expression compile, one might try the following:

// THIS FUNCTION IS WRONG. IT MAY SEGFAULT.
Teuchos::RCP<MPI_Comm> getMpiCommPtr()
{
MPI_Comm comm = MPI_COMM_WORLD;
// WRONG!!! comm is a stack variable!
return Teuchos::rcp (&comm, false);
}

Using the returned communicator would result in undefined behavior, which in practice might be a segfault, memory corruption, or MPI getting severely confused. This is because the stack variable comm, which may be just an integer, disappears at the end of the function. Its address would no longer point to valid memory after the function returns.

The following code is syntactically correct, but may leak memory:

// THIS CODE LEAKS MEMORY FOR GENERAL MPI_Comm OBJECTS.
Teuchos::RCP<MPI_Comm> getMpiCommPtr (MPI_Comm comm)
{
MPI_Comm *pComm = new MPI_Comm (comm);
// Works for comm==MPI_COMM_WORLD or MPI_COMM_SELF;
// leaks memory for user-created MPI_Comm objects.
return Teuchos::rcp (pComm);
}

The above implementation of getMpiCommPtr() is correct only for the standard MPI_Comm objects provided by MPI, like MPI_COMM_WORLD and MPI_COMM_SELF. It is not correct, and in fact may leak memory, for custom MPI_Comm objects that the user creates by calling functions like MPI_Comm_split(). This is because user-created MPI_Comm objects must be freed by MPI_Comm_free(). Other kinds of opaque objects, like MPI_Datatype and MPI_Op, have their own free functions. Thus, even if opaque handles have the type integer, they really behave like pointers or references. Some of them can and should be freed at the end of their useful lives; others must not. (Compare std::ostream; std::cout should never be closed by typical user code, but an output file should be closed.)

How to use OpaqueWrapper

We fix this problem by providing the OpaqueWrapper template base class and the opaqueWrapper() nonmember template function. Use this function to wrap an opaque handle (like an MPI_Comm) in an RCP. This ensures that the RCP does the right thing in case the handle must be freed. For example, to wrap MPI_COMM_WORLD in a RCP, just do this:

RCP<OpaqueWrapper<MPI_Comm> > comm = opaqueWrapper (MPI_COMM_WORLD);

If you instead want to create a custom MPI_Comm using a function like MPI_Comm_split(), then you may wrap it in an RCP as follows (please see discussion later about MPI_Comm_free()):

MPI_Comm rawComm;
// We omit all arguments but the last of MPI_Comm_split, for clarity.
int errCode = MPI_Comm_split (..., &rawComm);
if (errCode != MPI_SUCCESS) {
// ... Handle the error ...
}
RCP<OpaqueWrapper<MPI_Comm> > comm = opaqueWrapper (rawComm, MPI_Comm_free);

The optional second argument to opaqueWrapper() is a "free" function. It has type OpaqueFree which is a template parameter. If the free function is provided, then when the RCP's reference count goes to zero, that function is called to "free" the handle. If opaqueFree is a free function, then the following must be syntactically valid, where opaque has type Opaque:

opaqueFree (&opaque);

The function's return value, if any, is ignored. Furthermore, the OpaqueFree type must be copy constructible. (A function pointer is trivally copy constructible.)

Users are responsible for knowing whether to provide a free function to opaqueWrapper(). In this case, because we created an MPI_Comm dynamically using a communicator "constructor" function, the MPI_Comm must be "freed" after use. RCP will automatically call the "free" function once the reference count of comm reaches zero.

Note
The above example is only correct if the reference count of comm will go to zero before MPI_Finalize is called. This is because it's not valid to call MPI_Comm_free after MPI_Finalize has been called. The details::safeCommFree function checks whether MPI_Finalize has been called (via MPI_Finalized) before calling MPI_Comm_free; you may use this function as the free function if you are concerned about this.

Definition at line 240 of file Teuchos_OpaqueWrapper.hpp.

Constructor & Destructor Documentation

template<class Opaque >
Teuchos::OpaqueWrapper< Opaque >::OpaqueWrapper ( Opaque  opaque)
inline

Constructor that accepts and wraps a raw handle.

Users typically never have to invoke the constructor explicitly. The opaqueWrapper() nonmember template function does this for them.

Definition at line 246 of file Teuchos_OpaqueWrapper.hpp.

Member Function Documentation

template<class Opaque >
Teuchos::OpaqueWrapper< Opaque >::operator Opaque ( ) const
inline

Implicit type conversion from wrapper to raw handle.

Users typically never have to convert directly from an OpaqueWrapper to the raw handle that it wraps. For example, if you have an RCP<OpaqueHandle<T> >, just deferencing the RCP will return the raw handle via this implicit type conversion operator:

// We omit the right-hand side of this assignment, for simplicity.
RCP<OpaqueWrapper<T> > wrapped = ...;
// RCP's operator* returns OpaqueWrapper<T>&.
// In turn, the operator below automatically converts to T.
T raw = *wrapped;

Definition at line 263 of file Teuchos_OpaqueWrapper.hpp.

template<class Opaque >
Opaque Teuchos::OpaqueWrapper< Opaque >::operator() ( ) const
inline

Explicit type conversion from wrapper to raw handle.

Users typically never have to convert directly from an OpaqueWrapper to the raw handle that it wraps. However, in case they do, we provide this operator.

Definition at line 270 of file Teuchos_OpaqueWrapper.hpp.

Friends And Related Function Documentation

template<class Opaque >
RCP< OpaqueWrapper< Opaque > > opaqueWrapper ( Opaque  opaque)
related

Create a new OpaqueWrapper object without a free function.

See the documentation of OpaqueWrapper for a detailed explanation of why and how to use this function.

Definition at line 354 of file Teuchos_OpaqueWrapper.hpp.

template<class Opaque , class OpaqueFree >
RCP< OpaqueWrapper< Opaque > > opaqueWrapper ( Opaque  opaque,
OpaqueFree  opaqueFree 
)
related

Create a new OpaqueWrapper object with a free function.

See the documentation of OpaqueWrapper for a detailed explanation of why and how to use this function.

Definition at line 370 of file Teuchos_OpaqueWrapper.hpp.

Member Data Documentation

template<class Opaque >
Opaque Teuchos::OpaqueWrapper< Opaque >::opaque_
protected

The actual handle.

This is protected and not private so that OpaqueWrapperWithFree can access it. In general, one should avoid using protected data, but it would be silly to add member functions just for this simple use case.

Definition at line 279 of file Teuchos_OpaqueWrapper.hpp.


The documentation for this class was generated from the following file: