27 Creating Client Applications by Using PCM C++

This chapter explains how to create C++ client applications that communicate with a Oracle Communications Billing and Revenue Management (BRM) system by using the Portal Communication Module (PCM) C++ Application Programming Interface (API).

Before using PCM C++, read "Comparison of the PCM C++ and PCM C APIs".

About PCM C++

Use the classes and their member functions in the PCM C++ API to write C++ client applications that communicate with BRM. PCM C++ is a set of wrappers around the PCM C client library. The C++ classes represent the BRM C data structures. These structures, including flists, fields, context, and POIDs, are defined in the pcm.h file.

However, C++ provides several advantages over C: improved runtime memory, type checking, compile time, and reliability. C++ provides hooks for assertions in debugging. If programmed properly, smart pointers can make memory management easier for programmers. In addition, C++ allows object oriented programming, a more powerful approach than is possible in C. PCM C++ allows you to take advantage of these C++ capabilities.

For information on BRM data types, see "Understanding the BRM Data Types".

For information on flists, see "Understanding Flists and Storable Classes".

Skills Required

To use the PCM C++ package, you must have the following skills and experience:

Installation

PCM C++ is installed automatically as part of the BRM SDK. For installation and configuration information, including software requirements, see "About BRM SDK".

Note:

PCM C++ is sometimes be referred to as PCM CPP.

Comparison of the PCM C++ and PCM C APIs

Table 27-1 compares the functionality available in the PCM C++ and PCM C APIs:

Table 27-1 Comparison of C++ and C APIs in PCM

PCM C API PCM C++ API

PCM_CONNECT

PinContextOwner PinContext::create( );

PCM_CONTEXT_OPEN

PinContextOwner PinContext::create(PinFlistBase & inFlist);

PCM_CONTEXT_CLOSE

void PinContext::close();

PCM_OP

NA

PIN_FLIST_CONCAT

NA

PIN_FLIST_COPY

PinFlistOwner PinFlist::clone() const;

PIN_FLIST_COUNT

NA

pin_flist_sort

void PinFlist::sort(PinFlistBase &sortlist, int32 descending=0, int32 sortDefault=0);

pin_flist_recursive_sort

void PinFlist::sortRecursively(PinFlistBase &sortlist, int32 descending=0, int32 sortDefault=0);

PIN_FLIST_FLD_DROP

void PinFlist::drop(const PinXXXTypeField &fld);

// For field types, where XXX is

// Int, Uint, Num, Enum, Tstamp, Str, Binstr, Poid, BigDecimal

PIN_FLIST_FLD_GET

PinXXXObserver

PinFlist::get(const PinXXXTypeField &fld, PinBool optional=false);

//Where XXX is

//Int, Uint, Num, Enum, Tstamp, Str, Binstr, Poid, BigDecimal, Sub

PIN_FLIST_FLD_PUT

void PinFlist::set(const PinXXXTypeField &fld, PinXXXOwner &val);

// For field types, where XXX is

// Int, Uint, Num, Enum, Tstamp, Str, Binstr, Poid, BigDecimal, Sub

PIN_FLIST_FLD_SET

void PinFlist::set(const PinXXXTypeField &fld, PinXXX val);

// Value based setter for simple field types, where XXX is

// Int, Uint, Num, Enum, Tstamp, Str, Binstr

void PinFlist::set(const PinXXXTypeField &fld, PinXXXBase &val);

// For field types, where XXX is

// Int, Uint, Num, Enum, Tstamp, Str, Binstr, Poid, BigDecimal, Sub

PIN_FLIST_FLD_TAKE

PinXXXOwner PinFlist::take(const PinXXXTypeField &fld, PinBool optional=false);

// For field types, where XXX is

// Int, Uint, Num, Enum, Tstamp, Str, Binstr, Poid, BigDecimal, Sub

PIN_FLIST_ELEM_ADD

PinFlistObserver PinFlist::add(const PinArrayTypeField &fld, PinRecId id)

PIN_FLIST_ELEM_COUNT

int PinFlist::count(const PinArrayTypeField& fld);

PIN_FLIST_ELEM_DROP

void PinFlist::drop(const PinArrayTypeField& fld, PinRecId id);

PIN_FLIST_ELEM_GET

PinFlistObserver

PinFlist::get(const PinArrayTypeField& fld, PinRecId id, PinBool optional = PIN_BOOLEAN_FALSE);

PIN_FLIST_ELEM_GET_NEXT

PinElemObservingIterator::next();

PIN_FLIST_ELEM_PUT

void PinFlist::put(const PinArrayTypeField& fld, PinFlistOwner&, PinRecId id);

PIN_FLIST_ELEM_SET

void PinFlist::set(const PinArrayTypeField& fld, PinFlistBase&, PinRecId id);

PIN_FLIST_ELEM_TAKE

PinFlistOwner

PinFlist::take(const PinArrayTypeField &fld, PinRecId id, PinBool optional = PIN_BOOLEAN_FALSE);

PIN_FLIST_ELEM_TAKE_NEXT

PinElemObservingIterator::next();

PIN_FLIST_SUBSTR_ADD

NA

PIN_FLIST_SUBSTR_DROP

NA

PIN_FLIST_SUBSTR_GET

NA

PIN_FLIST_SUBSTR_PUT

NA

PIN_FLIST_SUBSTR_SET

NA

PIN_FLIST_SUBSTR_TAKE

NA

PIN_FLIST_TO_STR

// String toString();

PinBool PinFlist::isNull() const;pin_flist_t*

PinFlist::get();pin_flist_t* PinFlist::release();

PIN_POID_COMPARE

int PinPoid::compare(const PinPoidBase &poid, int checkRev=0) const;

//isEqual() returns a boolean

//unlike compare() which acts like //strcmp();PinBool PinPoid::isEqual(const PinPoidBase &poid, int checkRev=0) const;

PIN_POID_COPY

PinPoidOwner

PinPoid::clone() const;

PIN_POID_CREATE

static PinPoidOwner

PinPoid::create(PinPoidDb db, PinPoidType type, PinPoidId id);

NA

static PinPoidObserver

PinPoid::createAsObserved(poid_t *pdp);

static PinPoidOwner

PinPoid::createAsOwned(poid_t *pdp);

PIN_POID_DESTROY

static void PinPoid::destroy(PinPoid *obj);

PIN_POID_GET_DB

PinPoidDb PinPoid::getDb() const;

PIN_POID_GET_ID

PinPoidId PinPoid::getId() const;

PIN_POID_GET_TYPE

PinPoidType PinPoid::getType() const;

PIN_POID_GET_REV

PinPoidRev PinPoid::getRev() const;

PIN_POID_IS_NULL

PinBool PinPoid::isNull() const;

PIN_POID_IS_TYPE_ONLY

PinBool PinPoid::isTypeOnly() const;

PIN_POID_TO_STR

void PinPoid::toString(char buf[ ], int bufsize, int skiprev=0) const;

pbo_decimal_abs

PinBigDecimal::abs() const;

pbo_decimal_abs_assign

NA

pbo_decimal_add

PinBigDecimal::operator+(const PinBigDecimal& val);

PinBigDecimal::operator+=(const PinBigDecimal& val);

pbo_decimal_add_assign

NA

pbo_decimal_compare

int PinBigDecimal::compare(const PinBigDecimal& val) const;

PinBool PinBigDecimal::isZero() const;PinBool

PinBigDecimal::isLessThanZero() const;PinBool

PinBigDecimal::isGreaterThanZero() const;

pbo_decimal_copy

NA

pbo_decimal_divide

PinBigDecimal::operator/(const PinBigDecimal& val);

PinBigDecimal::operator/=(const PinBigDecimal& val);

//same result:

PinBigDecimal::divide(const PinBigDecimal& val, int decimalPlaces, int mode = DEF_ROUNDING_MODE);

pbo_decimal_divide_assign

NA

pbo_decimal_from_double

PinBigDecimal::setDouble(double val, int decimalPlaces, int mode = DEF_ROUNDING_MODE);

pbo_decimal_from_str

NA

pbo_decimal_is_null

PinBool PinBigDecimal::isNull() const;

pbo_decimal_is_zero

See pbo_decimal_compare

pbo_decimal_multiply

PinBigDecimal::operator*(const PinBigDecimal& val);

PinBigDecimal::operator*=(const PinBigDecimal& val);

//same result:

PinBigDecimal::multiply(const PinBigDecimal& val, int decimalPlaces,int mode = DEF_ROUNDING_MODE);

pbo_decimal_multiply_assign

NA

pbo_decimal_negate

PinBigDecimal::negate() const;

pbo_decimal_negate_assign

NA

pbo_decimal_round

NA

pbo_decimal_round_assign

NA

pbo_decimal_signum

int PinBigDecimal::sigNum()

pbo_decimal_subtract

PinBigDecimal::operator-(const PinBigDecimal& val);

PinBigDecimal::operator-=(const PinBigDecimal& val);

pbo_decimal_subtract_assign

NA

pbo_decimal_to_double

PinBigDecimal::getDouble() const;

pbo_decimal_to_str

char* PinBigDecimal::toString(char* pbuf, int bufSize, int decimalPlaces = -1) const;


Understanding PCM C++ Concepts

While there are several similarities between the PCM C and PCM C++ APIs, there are also some key differences. This section explains these differences in detail and explains how to approach some specific programming functions. Understanding this information allows you to take advantage of PCM C++ capabilities and should make coding easier in areas such as memory management.

Passing Arguments

Using the C++ wrappers, you can minimize the need for dealing with untyped void pointers and explicit casting. In addition, PCM C++ includes methods that accept typed arguments. These two contrasting examples illustrate this point:

Using pointers in C:

time_t *endp = NULL; 
time_t end_time;
  
endp = (time_t *) PIN_FLIST_FLD_GET(inflistp, PIN_FLDT_END_T, 1, ebufp);
end_time = (endp) ? *endp : pin_virtual_time(NULL);
  
PIN_FLIST_FLD_SET(outflistp, PIN_FLD_END_T, &end_time, ebufp);
  

Passing arguments in C++ :

PinTstampObserver endt   = inFlist->get(tsf_PIN_FLD_END_T, PIN_BOOLEAN_TRUE);
  
PinTstamp endTime = endt->isNull() ? pin_virtual_time(NULL):: endt->value();
  
outFlist->set(tsf_PIN_FLD_END_T, endTime);
  

In the C++ example:

  • When retrieving END_T from the flist, no explicit casting is needed to convert from "void*" to "time_t*".

  • When setting the value of END_T in the flist, you pass a typed argument (value) instead of an untyped pointer (pointer to the value).

Using Arrays

To walk through elements of an array in an flist, PCM C exposes a cookie-based interface, where it is the responsibility of the caller to initialize the cookie and pass it to a series of calls to retrieve the members of the array:

//Example in C initializting and passing a cookie
pin_cookie_t cookie = NULL;
while ((elemp = PIN_FLIST_ELEM_GET_NEXT
                (flistp, PIN_FLD_BALANCES, &elemid, 1, &cookie, ebufp)) != NULL) {
                ...
}
  

In contrast, PCM C++ uses iterator objects, which eliminate some common programming mistakes and also introduce commonly used patterns. A sample of iterator objects in C++:

//Example in C++ using iterator objects
PinElemObservingIterator iter;
for (iter = flist->getElements(tsf_PIN_FLD_BALANCES);
    iter.hasMore();) {
  
    PinFlistObserver aResource = iter.next();
    cout << "Resource id: " << iter.getRecId();
    ...
}

Using Smart Pointers to Manage Memory

C++ wrappers use constructs, generically called smart pointers, which are similar to the auto_ptr in the standard C++ library. Smart pointers are objects designed to look and act like built-in pointers, but offer greater functionality: smart pointers overload the operator -> (and sometimes the operator *).

PCM C++ uses smart pointers to eliminate the need for careful and explicit memory management that is required in PCM C. Because the smart pointer objects used to manipulate flists, POIDs, and connections are eliminated when the object is deleted or goes out of scope, the underlying resource, such as the flist, Portal object ID (POID), or connection, is freed.

Note:

Though it is common for smart pointers to be implemented using C++ templates, the PCM C++ implementation of smart pointers does not use them. This ensures consistent behavior on all BRM platforms.

Construction and Destruction

All flist, POID, and connection manipulations in PCM C++ are accomplished by using smart pointers. Smart pointers delete the object they point to when the last smart pointer pointing to that object is destroyed. This nearly eliminates resource leaks.

This example shows how a smart pointer is used to create an input flist:

void
  
function1()
{
      // Create input flist
      PinFlistOwner input = PinFlist::create();
  
      // Note that at the end of this block, the destructor
      // for "input" will get invoked. The destructor will
      // automatically destroy the underlying flist.
}
  

The smart pointer implementations in PCM C++ do not allow multiple smart pointer references to the same underlying object. Some of the reasons are:

  • The underlying C implementation of flist does not allow reference counting, which adds overhead.

  • A typical flist usage pattern requires a single pointer to an flist.

  • Even done correctly, multiple pointers to the same flist can result in errors that are difficult to debug.

Copying and Assignment

In PCM C++, object ownership is transferred when the copy constructor or the assignment operator is invoked on a smart pointer. This is similar to the auto_ptr that is a standard C++ template library object. To copy the underlying data, use clone(). Because object ownership is transferred when the copy constructor is called, passing these smart pointers by value is not recommended.

The correct usage is to pass smart pointers using references, as shown below:

void function1()
{
    // Connect to Portal
    PinContextOwner context = PinContext::create();
  
    // Get the plan list 
    // (Note that the context smart pointer is passed by reference, not by value. 
    // If it were passed by value, the underlying connection would have been destroyed 
    // when function2() finishes and also "context" would be pointing to 
    // a freed connection!!)
    function2(context);
}
  
void function2(
    PinContextBase &context)
{
    ...
}

Using Field Value Ownership

Manipulating flists is done differently in PCM C++ than in PCM C. The PCM C API provides different sets of functions to manipulate flists.

  • PIN_FLIST_FLD_GET(), PIN_FLIST_SUBSTR_GET(), and PIN_FLIST_ELEM_GET() are used to access to the contents of the flist using pointers. This is like peeking inside the flist. The underlying flist retains ownership of the memory.

  • With PIN_FLIST_XXX_TAKE(), the underlying flist relinquishes ownership of the pointed to block of memory and returns a pointer to the caller. The caller is now responsible for freeing the contents.

  • PIN_FLIST_XXX_SET() is used to add new fields into an flist. The flist makes a copy of the passed in memory block and owns the copied block.

  • PIN_FLIST_XXX_PUT() transfers ownership of field values to the flist.

PCM C++ provides overloaded methods in the PinFlist class to deal with these four methods: get(), take(), set(), put(). Correct memory management is enforced by defining two types of smart pointers: observers and owners as shown in Figure 27-1.

  • Observers do not delete the wrapped BRM data structure (object) pointed to when they are destroyed.

  • Owners delete the object pointed to when they are destroyed, unless they are assigned or passed to a copy constructor.

Figure 27-1 Owner and Observer Memory Management

Description of Figure 27-1 follows
Description of ''Figure 27-1 Owner and Observer Memory Management''

The PinFlist::get() method returns an observer. The take() method returns an owner style smart pointer. Similarly, the put() method accepts only an owner. The set() method accepts either an observer or an owner. Relying on method signatures to do clean memory management is preferable to manual and careful programming. The following example shows the proper usage:

PinIntObserver flags = input->get(tsf_PIN_FLD_FLAGS);
// The following put() call will not compile because 
// you cannot transfer ownership of something that you 
// do not own!!
//      output->put(tsf_PIN_FLD_FLAGS, flags);
  
// The set() will work fine...because a copy is made.
output->set(tsf_PIN_FLD_FLAGS, flags);

Using PinBigDecimal

The PCM C++ PinBigDecimal class can be used with many standard C++ programming features. However, there are some differences which are documented in this section.

For a sample program illustrating PinBigDecimal, see sample_PinBD.cpp located in BRM_SDK_Home/source/samples/apps/C++.

Field Value Ownership

The PCM C++ class PinBigDecimal does not formally support the owner/observer model used by other PCM C++ classes. This alternative approach allows you to directly create and manipulate a PinBigDecimal, which makes it much easier to use in arithmetic expressions.

PinBigDecimal creates a base object, which is destroyed automatically when it goes out of scope. This concept is described in "Using Smart Pointers to Manage Memory".

This sample shows how to use PinBigDecimal. As noted above, PCM C++ creates a PinBigDecimal object that is destroyed automatically when it goes out of scope (in this example, when the function is exited).

{
     PinBigDecimal num1( "22.57" );
     ...
}

Using PinBigDecimal with Flists

PCM C+ uses a different approach than PCM C to setting (or getting) a PinBigDecimal value into (or from) an flist. In PCM C, you must carefully program using low-level functions: the PIN_FLIST_FLD_SET macro with the PinBigDecimal.get() method to retrieve the pointer to the actual big decimal number in pin_decimal_t*. This is shown below:

PinBigDecimal sum = num3 + num4;
  
PinErrorBuf  ebuf; 
pin_flist_t* flistp     = PIN_FLIST_CREATE( &ebuf );
PIN_FLIST_FLD_SET( flistp, PIN_FLD_DUE, sum.get(), &ebuf );
::printFlist( "The Flist I just created with C API...", flistp );
PinBigDecimal due( (pin_decimal_t*) PIN_FLIST_FLD_GET( flistp, PIN_FLD_DUE, 1, &ebuf ));
PIN_FLIST_DESTROY( flistp, 0 );
due.toString( buf, sizeof( buf ), due.getNumDecimalPlaces() );
::printf( "The value pulled from the Flist for PIN_FLD_DUE is: %s\n\n", buf );
  

In PCM C++, this low-level approach is unnecessary. Because C++ is object oriented and type safe, you can set() a PinBigDecimal object to the flist, which is shown below:

PinBigDecimal num3( 22.578979, 5 );
PinBigDecimal num4( double_val, 2, ROUND_HALF_DOWN );
PinBigDecimal sum = num3 + num4;
  
PinFlistOwner cpp_flist = PinFlist::create(); 
cpp_flist->set( tsf_PIN_FLD_DUE, sum );
::printFlist( "The Flist I just created with the C++ API...", cpp_flist->get() );
due = cpp_flist->get( tsf_PIN_FLD_DUE )->get();
due.toString( buf, sizeof( buf ), due.getNumDecimalPlaces() );
::printf( "The value pulled from the Flist for PIN_FLD_DUE is: %s\n\n", buf );

Using the toString() Method

To use PinBigDecimal with toString(), you must pass a parameter containing the number of decimal places to be set in the returned string. To do this, call PinBigDecimal::getNumDecimalPlaces().

Note:

In some implementations, this is unnecessary because there is a default value for the number of decimal places. This is not yet implemented in PCM C++.

The following sample shows one method of converting a PinBigDecimal to a string by passing the necessary parameters.

char buf[100];
PinBigDecimal num1( "22.57" );
PinBigDecimal num2( "99"  );
PinBigDecimal sum = num1 + num2;
  
PinErrorBuf  ebuf; 
pin_flist_t* flistp     = PIN_FLIST_CREATE( &ebuf );
PIN_FLIST_FLD_SET( flistp, PIN_FLD_DUE, sum.get(), &ebuf );
::printFlist( "The Flist I just created with C API...", flistp );
  
PinBigDecimal due( (pin_decimal_t*) PIN_FLIST_FLD_GET( flistp, PIN_FLD_DUE, 1, &ebuf ));
PIN_FLIST_DESTROY( flistp, 0 );
due.toString( buf, sizeof( buf ), due.getNumDecimalPlaces() );
::printf( "The value pulled from the Flist for PIN_FLD_DUE is: %s\n\n", buf );

Using the Divide Method

Because the decimal result can be infinite (for example 1/3=.333333...), the divide method of PinBigDecimal requires you to define the number of decimal places in the result. (The number of decimal places is called the scale.) Limiting the scale of the result introduces a rounding question, which is answered by defining the rounding method to be used. Defaults for both the scale and rounding method are defined in the PinBigDecimal.h file: the default scale is 10 [DEF_DIV_DECIMAL_PLACES (10)] and the default rounding method is "half up" [DEF_ROUNDING_MODE (ROUND_HALF_UP)].

This sample shows the divide method of PinBigDecimal using the default scale:

char buf[100];
char buf1[100];
char buf2[100];
  
PinBigDecimal num1 = "18.4328";
PinBigDecimal num2 = "3.4937";
  
PinBigDecimal num3 = num1 / num2;
  
num1.toString( buf, sizeof( buf ), num1.getNumDecimalPlaces() );
num2.toString( buf1, sizeof( buf1 ), num2.getNumDecimalPlaces() );
num3.toString( buf2, sizeof( buf2 ), num3.getNumDecimalPlaces() );
  
     cout << buf << " / " << buf1 << " is: " << buf2 << endl;
  

To programmatically specify the number of decimal places and/or the rounding mode, you must call the divide method, as shown in this example:

// The above statement "num3 = num1 / num2" will NOT be the same as the following call
// to the divide method.
  
PinBigDecimal num4 = num1;
num4.divide( num2, 9, ROUND_HALF_UP );
num4.toString( buf2, sizeof( buf2 ), num4.getNumDecimalPlaces() );
  
     cout << buf << " divided by " << buf1 << " is: " << buf2 << endl;
  

To programmatically specify the number of decimal places and/or the rounding mode, you must call the divide method.

Using a Null Pointer

You can use a special null pointer as a value to indicate that a feature is unused. For example, if a customer chooses not to enroll in an optional stock purchase plan, you can pass an flist with a null pointer for this option to the database to indicate that the customer is not participating.

Performing an arithmetic function on a PinBigDecimal variable containing a null pointer causes the class PinBigDecimal to throw an exception. However, you can use a null pointer successfully with the try and catch commands. This sample code illustrates this:

char* pnull = 0;
PinBigDecimal null_val = PinBigDecimal( pnull );
try
{
      PinBigDecimal bad_idea;  // The default constructor will set the value to zero.
      bad_idea += null_val;
      ::printf( "The program should NEVER get here...\n\n" );
}
catch ( const PinEbufExc& /*cExcptn*/ )
{
      null_val.toString( buf, sizeof( buf ), 2 );
      ::printf( "The string value of a null PinBigDecimal is: %s\n\n", buf );
}

Note:

PinEbufExc is the specific error thrown under these circumstances.

Handling Exceptions

PCM C is macro-based and uses series-style ebuf-based error checking. Since the same structure is used by all the function calls, all calls return immediately without any action after the first error is recorded in the ebuf. This style avoids an explicit error check after every line and allows you to group error handling logic toward the end of the function. The disadvantage is that almost every line following the error-inducing statement has the potential to be executed.

In PCM C++, exceptions are used instead, which takes advantage of the support provided by the language run-time. The error handling in PCM C++ primarily uses an exception class, PinEbufExc, which is a wrapper for the underlying C data structure (pin_errbuf_t). Use the class, PinErrorBuf, to access pin_errbuf_t.

There is one important difference: in the C API, when returning errors from a function using ebuf, you can return other values, such as output flists, by using output parameters. However, in C++, when an exception is thrown, the normal path of return is not available. To accommodate passing error flists back, the PinEbufExc class has a data member, PinFlistOwner.

This example shows error handling in PCM C++ using this method:

ostream& 
operator<<(
ostream &os, 
PinEbufExc &exc)
{
        os << "Pin Exception";
        os << exc.getFlistRef() << endl;
        PIN_LOG(exc, PIN_ERR_LEVEL_ERROR, "");
        return os;
};
  

This example shows error handling in PCM C++ using the exception buffer:

try {
      // Connect to Portal
      PinContextOwner context = PinContext::create();
  
} catch (const PinEbufExc &exc) {
      // Handle the error.
      PIN_LOG(exc, PIN_ERR_LEVEL_ERR, "Connect failed");
} 
  

See Pin_Log for more information.

If an error occurs while processing an flist, the returned flist contains the error message. The following three code excerpts show how to print the flist to various devices.

This example shows the special overridden operator used to print out an flist:

ostream& 
operator<<(ostream &os, PinFlistBase &flist) 
{ 
      char *strp = NULL; 
      PinErrorBuf ebuf; 
      int32 len = 0; 
  
      pin_flist_t *fp = NULL; 
      if (! flist.isNullWrapperPtr()) { 
            fp = flist->get(); 
      } 
      // convert to string
      PIN_FLIST_TO_STR(fp, &strp, &len, &ebuf); 
      // print out to current stream
      os << strp; 
      
      if (strp != NULL) { 
            pin_free(strp); 
      } 
  
      return os; 
}; 
  

To print the flist to the console:

// Print output flist
cout<<"outFlist:"<<endl<<outFlist<<endl;
  

The PinEbufExc object contains the PinErrorBuf object, which is inherited from the PCM C structure, pin_errbuf. This buffer contains all the information about the error.

In PCM C++, define your own operator <<:

ostream& 
operator<<(ostream &os, PinEbufExc &exc) 
{ 
      os << "Pin Exception"; 
      os << exc.getFlistRef() << endl; 
      PIN_LOG(exc, PIN_ERR_LEVEL_ERROR, ""); 
      return os; 
}; 
  

and then use it to print PinEbufExc to the console and write information

to PinLog:

} catch (PinEbufExc &exc) { 
      cout << exc << endl; 
} 

Logging to pinlog

The PinLog class is a minimalist class. It only provides type-safe wrappers that accept the PCM C++ class instances as arguments to the overloaded log() method.

Two macros, PIN_LOG and PIN_MSG, use the PinLog class. They allow you to pick up the current file and line number. Three examples of logging are:

PIN_LOG(flist, PIN_ERR_LEVEL_DEBUG, "Input to XXX");
  
PIN_LOG(ebufException, PIN_ERR_LEVEL_ERROR, "Create Account:");
  
PIN_MSG(PIN_ERR_LEVEL_WARNING, "Exceeding Cache Size");
  

For more information, see Pin_Log and Pin_Msg.

Accessing Configuration Values by Using pin.conf

The PinConf class provides static methods to get configuration values from a pin.conf file. Since the underlying pin_conf() PCM C library function returns an allocated memory block, the PinConf class type safe methods return owner-style smart pointers.

This example uses PinConf to access configuration values in a pin.conf file:

PinIntOwner dbg = PinConf::getInt("ldap_ds", "debug", 1);
int32 pinLdapDebug = (dbg->isNull() ? 0 : dbg->value());

Using PCM C++ with PCM C

There are many situations where you might want to mix the PCM C and PCM C++ APIs.

  • Although PCM C++ provides useful abstractions, it is not complete. For example, it does not support buffer data types.

  • New functionality based on PCM C++ might be developed and has to coexist with legacy code written in PCM C API. Also, this approach allows you to experiment and become familiar with PCM C++ without having to rewrite entire applications.

  • Some PCM C API code is needed in rare situations, for example, invoking the PCM_OP_SEARCH opcode. PCM C++ enables the coexistence and mixing of the two APIs within the same application as follows:

    PinFlistObserver flist = PinFlist::create(); 
    // Get the underlying C flist data structure 
    pin_flist_t *flistp = flist->get(); 
    // Pass the C data structure to some C function 
    PIN_FLIST_PRINT(flistp, 0, ebufp); 
      
    
  • This allows access to the underlying PCM C API objects that PCM C++ manages. For example, the PinFlist class can access the PCM C flist it holds. Obviously, doing destructive things to the underlying C object will make the C++ object inconsistent.

    Factory methods of the various PCM C++ classes take in pointers to PCM C API data structures, in addition to default factory methods that can create the PCM C API data structure automatically. Also, depending on the model of interfacing with the PCM C API, you can control the lifetime of the C data structures by creating observer or owner smart pointers, as in this example:

    main() { 
          pcm_context_t *ctxp = (pcm_context_t*) NULL; 
          int64 dbno = 0;
          PCM_CONNECT(&ctxp, &dbno, ebufp); 
          ... 
    } 
    PinContextObserver context = PinContext::createAsObserved(ctxp);
    

Using the PCM C++ API

The basic structure of a BRM PCM C++ client application is similar to other BRM client applications:

  1. Open a connection using PinContext.

  2. Create an flist.

  3. Perform PCM operations.

  4. Check for errors.

  5. Close the connection.

Opening a PCM Connection

The PinContext class is a wrapper around the pcm_context data structure in the PCM C API. The data structure represents a connection from a BRM client to the server--a Connection Manager (CM).

Use the factory method create() to initiate a connection to the CM. This method uses the connection parameters from either an flist or as specified in the pin.conf file of the client application. Use either of these methods to open a connection:

  • Call the create() factory method in the PinContext class. Using parameters contained in an flist, provide all the login information needed, including the host name and the port number. A program to be used by CSRs can use this method to force authentication.

    For a C++ sample, look for the create_context.cpp sample file in BRM_Home/source/samples/context/C++ directory, where BRM_Home is the directory in which you installed BRM components. For more information, see PCM_CONTEXT_OPEN.

  • If you want your program to automatically log in to BRM, use the create() method without parameters. You must store all the necessary login information in the cm_ptr and the userid connection parameters in your application's pin.conf file. Use one pin.conf file to configure C++ and C applications. A billing application run from a cron job would use this method.

    For more information on the pin.conf file, see "Adding or Changing Login Options".

    For more information, see PCM_CONTEXT_OPEN and PCM_CONNECT.

    Important:

    In your application, when you open a context and connect to the BRM server, perform all the PCM operations before closing the context. Connections add a significant overhead to the system which affects performance. Therefore, to improve performance, perform all the operations within an open context instead of opening and closing contexts frequently. Use CM proxy for applications that cannot maintain an open context for a long time. For more information, see "Using CM Proxy to Allow Unauthenticated Log on" in BRM System Administrator's Guide.

    Like the other PCM C++ classes, PinContext class instances are manipulated by using smart pointer classes: PinContextOwner and PinContextObserver.

    This example shows a connection that gets the logon parameters from the application's pin.conf file:

    try { 
          // Connect to Portal. Get the connection info from the pin.conf file of the 
          // client application. 
          PinContextOwner context = PinContext::create(); 
      
          // The connection is terminated automatically and the PinContext 
          // object managed by the PinContextOwner is destroyed automatically.
      
    } catch (PinEbufException &exc) { 
          // Deal with the error. 
          PIN_LOG(exc, PIN_ERR_LEVEL_ERR, "Connect failed"); 
    }
      
    

    When you create a context using either method (PinContextOwner or PinContextObserver), a pcm_context_t data structure is created automatically in C. A pointer to the data structure is automatically returned (access it by using call Get()). You can create additional context objects by using this pointer. Use this to pass a data structure to another application.

Closing a PCM Connection

Close a connection to BRM by using PCM C++ with one of the following methods:

  • A context can be closed automatically by going out of scope. This occurs when smart pointers have been used. The context is automatically closed when the function ends.

  • If you want to close the connection context (opened as an Observer) with the server (and the data structure in pcm_context_t) before the function ends, use this method:

    void close(int how=n)
      
    
  • To close the connection context (opened as an Owner) with the server and to destroy a PinContext object before the function ends, use this method:

    static void destroy(PinContext *obj)
    

    Note:

    If the context was opened as Owner, destroy also closes the connection context.

The parameters shown above are described in PCM_Context class.

For more information on PinContext functions, see PinContext.

Creating Custom Fields

To create custom fields using PCM C++:

  1. In the customfld.h file, create your #define manually. For example, assume that a custom int field called CUSTOM_FLD_AGE is defined as follows:

    #define CUSTOM_FLD_AGE       10001
    ...
    #DEFINE CUSTOM_FLD_AGE PIN_MAKE_FLD(PIN_FLDT_INT, 10001)
      
    

    The following table lists the field ID ranges for Oracle-only use and for customer use:

    Table 27-2 BRM Field ID Restrictions

    Field ID Range Reserved For

    0 through 9999

    Oracle use only

    10,000 through 999,999

    Customer use

    1,000,000 through 9,999,999

    Oracle use only

    Over 10,000,000

    Customer use


  2. Instantiate a new C++ object:

    const PinIntTypeField tsf_CUSTOM_FLD_AGE(CUSTOM_FLD_AGE);
    

    Note:

    By convention, the prefix tsf_ is used for the C++ version of the field. tsf is an acronym for type-safe field.
  3. Include customfld.h in your application.

  4. Use the custom field with the PinFlist class:

    flist->set(tsf_CUSTOM_FLD_AGE, 22);
    

Creating an Flist

To create and use flists in PCM C++, use the flist factory method.

  1. Create the input flist using PinFlist::create().

  2. set the values into the flist using the methods available in the PinFlist class. The suggested convention is to preface the variable name with "tsf_" (type safe field).

Several sample programs are available that illustrate:

  • Simple flists.

  • Flists with arrays.

  • Flists with substructs.

For more information on the PCM C++ sample files, see "About Using the PCM C++ Sample Programs" in BRM Developer's Reference.

Getting an Flist in Text Format

When debugging, it is often useful to read a text representation of an flist. The FList API provides the toString() method for general purposes. See "Using the toString() Method".

Debugging PCM C++ Programs

Write a sample C++ program to test connections, load flists from files, use the flists as input when calling opcodes on the server, and to display the returned flist.

For more information on error handling, see "Handling Exceptions".

This sample uses overloaded << operators to print information about lists and POIDs to the console. This is helpful in debugging.

ostream& 
operator<<(ostream &os, PinPoidBase &poid) 
{ 
      char str[512]; 
      poid->toString(str, sizeof(str)); 
      os<<str; 
      return os; 
}; 

Troubleshooting

In PCM C++, object ownership is transferred when the copy constructor or the assignment operator is invoked on a smart pointer. This behavior can cause errors that are difficult to debug, as shown below:

class function1
{
      PinPoidOwner m_AcctPoid
      ...
};
  
...
  
function1::process()
{
    ;process()
     {
          PinPoidOwner t=m_AcctPoid;
         // The assignment owner took the memory. When the block ends, t goes 
         // out of scope, the destructor is called, and the memory is freed. 
         // An error might occur because m_AcctPoid now points to freed memory.
       ...
     }
     ...
  
// The following will crash because memory was freed when t went out of scope.
  
Print(m_AcctPoid);