Important C++ Casting Issues

Note

Follow the procedures outlined here to use the PortTranslator port unless you are absolutely sure what you are doing.

Since the only way to get foreign objects into and out of the SIDL/Bable world is to use the SIDL opaque type, certain non-obvious procedures must be observed in the C++ use of the SIDL/Babel PortTranslator. This is necessary because of two independent but, for the programmer, interacting things:

Ultimately, the thing to keep in mind is that PortTranslator expects and returns void* that are always cast from the type classic::gov::cca::Port* and must be static_cast'ed back to that type before anything else is done. A full example is built with the Ccaffeine distribution and can be found in $CCAFE_ROOT/cxx/dc/babel/component/PortTranslatorStarter. The following is an excerpt from that component that illustrates how to import and export Classic ports in the Babel religion:



/**
 * Obtain Services handle, through which the 
 * component communicates with the framework. 
 * This is the one method that every CCA Component
 * must implement. 
 */
void
ccafe_eg::PortTranslatorStarter_impl::setServices (
  /*in*/ gov::cca::Services services ) 
throw () 
{
  // DO-NOT-DELETE splicer.begin(ccafe_eg.PortTranslatorStarter.setServices)

  svc = services;

  try {
  
    /** Check to see if we are alive or dead. */
    if(services._not_nil()) { 
      gov::cca::TypeMap tm = svc.createTypeMap(); // nil TypeMap
      gov::cca::ports::GoPort gp = self;
      svc.addProvidesPort(gp, std::string("go"), 
  std::string("gov.cca.ports.GoPort"), tm);

      // We register for a plain old StringConsumerPort, this is a
      // Classic port that will (hopefully) be connected to us.
      svc.registerUsesPort(std::string("classicOut"), 
   std::string("StringConsumerPort"), tm);

      // We register this guy in the normal way for the Babel religion.
      svc.registerUsesPort(std::string("babelOut"), 
   std::string("ccafe0.StringConsumerPort"), tm);
      // Now for parameters.  We do the full enchalada here, dynamic
      // parameters included.  Dynamic parameters call back on this
      // class to tell the component that the parameters have changed.
      // This is appropriately harder to accomplish than simple static
      // parameters.  Note that static parameters will *change*
      // dynamically when changed in the GUI, they just won't notify
      // you of the change, at the time the change happens.

      // Here we fish out the Service provided by Ccaffeine to do
      // dynamic parameters.
      svc.registerUsesPort("classicParam", 
   "gov.cca.ParameterPortFactoryService", tm);

      // Since it is a Service, we can get it right away.
      gov::cca::Port p = svc.getPort("classicParam");

      // Since it is a classic service it comes in a PortTranslator
      // package, which we must cast down from gov::cca::Port, using
      // (sigh) the "=" operator.
      ccaffeine::ports::PortTranslator portX = p; //cast

      // Since I almost never make mistakes, but I hear others do, it
      // is good to check to see that the cast succeeded.
      if(portX._not_nil()) {


// Here we use the getClassicPort() method on PortTranslator
// that must always be casted blindly to a
// classic::gov::cca::Port* and absolutely nothing else.
classic::gov::cca::Port * cp = 
  static_cast < classic::gov::cca::Port * > (portX.getClassicPort());
if(cp == NULL) { // icky.
  fprintf(stderr, ":-( the port returned  is not one of "
  "classic::gov::cca::Port or is NULL\n");
  return;
}

// Now we do the dynamic_cast down to the specific
// classic::gov::cca::Port: ConfigurableParameterFactory that
// will help us do dynamic parameters.
ConfigurableParameterFactory *cpf = 
  dynamic_cast < ConfigurableParameterFactory * > (cp);

// Now we get the parameters set up in another routine.
ConfigurableParameterPort *pp = setupParameters(cpf);

// Now we begin the process of adding a classic port to this
// component: the parameter port that GUI users can click on
// and configure.

// First we *must* get it into the type that the
// PortTranslator is expecting: cast it to a
// classic::gov::cca::Port*.
classic::gov::cca::Port * clscp = 
  dynamic_cast <  classic::gov::cca::Port *  > (pp);
if(clscp == NULL) {
  fprintf(stderr, "Something went wrong with the ParameterPort "
  "returned from setupParameters\n");
}

// Next pass it to PortTranslator by createFromClassic(void*)
void * vp = static_cast <  void *  > (clscp);  
ccaffeine::ports::PortTranslator provideX = 
  ccaffeine::ports::PortTranslator::createFromClassic(vp);

// Add the configure port that we want to provide.
svc.addProvidesPort(provideX, "configure", "ParameterPort", tm);

// Let go of the ParameterPortFactoryService we got previously.
svc.releasePort("classicParam");
svc.unregisterUsesPort("classicParam");

      }
    }

  } catch(std::exception &  e) {
    fprintf(stderr, "Exception caught: %s\n", e.what());
  }

  // DO-NOT-DELETE splicer.end(ccafe_eg.PortTranslatorStarter.setServices)
}