Teuchos - Trilinos Tools Package
Version of the Day
|
Base class for wrapped opaque objects. More...
#include <Teuchos_OpaqueWrapper.hpp>
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... | |
Base class for wrapped opaque objects.
Opaque | Type of the opaque object (a.k.a. handle). |
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.
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.
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.
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:
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:
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:
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.)
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:
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()):
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
:
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.
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 208 of file Teuchos_OpaqueWrapper.hpp.
|
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 214 of file Teuchos_OpaqueWrapper.hpp.
|
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:
Definition at line 231 of file Teuchos_OpaqueWrapper.hpp.
|
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 238 of file Teuchos_OpaqueWrapper.hpp.
|
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 322 of file Teuchos_OpaqueWrapper.hpp.
|
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 338 of file Teuchos_OpaqueWrapper.hpp.
|
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 247 of file Teuchos_OpaqueWrapper.hpp.