CoreDX DDS Hello World Example in C++

Here is a 'C++' CoreDX DDS version of the typical "Hello World" application.

This example consists of two applications: one application that publishes a simple string message Topic (the data contains the string "hello world!"); and one application that subscribes to this topic.

The source code presented here is the 'C++' programming language version of the 'hello world' applications. It can interoperate with 'hello world' applications written in other languages, or running on different hardware platforms.

file: hello.ddl

struct StringMsg
{
   string msg;
};

file: hello_pub.cc

/****************************************************************
 *
 *  file:  hello_pub.c
 *  desc:  Provides a simple C++ 'hello world' DDS publisher.
 *         This publishing application will send data
 *         to the example 'hello world' subscribing 
 *         application (hello_sub).
 * 
 ****************************************************************
 *
 *  This file is provided by Twin Oaks Computing, Inc
 *  as an example. It is provided in the hope that it will be 
 *  useful but WITHOUT ANY WARRANTY; without even the implied 
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 *  PURPOSE. TOC Inc assumes no liability or responsibilty for 
 *  the use of this information for any purpose.
 *  
 ****************************************************************/
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <dds/dds.hh>
#include "hello.hh"
#include "helloTypeSupport.hh"
#include "helloDataWriter.hh"


/****************************************************************
 * main()
 *
 * Perform CoreDX DDS setup activities:
 *   - create a Domain Participant
 *   - create a Publisher
 *   - register the StringMsg data type
 *   - create a Topic
 *   - create a DataWriter 
 * Write data
 ****************************************************************/

int main(int argc, char * argv[])
{
  DomainParticipant  * domain;
  Publisher          * publisher;
  Topic              * topic;
  DataWriter         * dw;
  StringMsg          stringMsg;
  ReturnCode_t       retval;

  class DomainParticipantFactory * dpf = 
    DomainParticipantFactory::get_instance();

  /* create a DomainParticipant */
  domain = dpf->create_participant( 0, 
				    PARTICIPANT_QOS_DEFAULT, 
				    NULL, 
				    0 );
  if ( domain == NULL )
    {
      printf("ERROR creating domain participant.\n");
      return -1;
    }
  
  /* create a Publisher */
  publisher = domain->create_publisher(PUBLISHER_QOS_DEFAULT, 
				       NULL, 
				       0 );
  if ( publisher == NULL )
    {
      printf("ERROR creating publisher.\n");
      return -1;
    }
  
  /* Register the data type with the CoreDX middleware. 
   * This is required before creating a Topic with
   * this data type. 
   */
  StringMsgTypeSupport stringMsgTS;
  retval = stringMsgTS.register_type( domain, NULL );
  if (retval != RETCODE_OK)
    {
      printf("ERROR registering type\n");
      return -1;
    }
  
  /* Create a DDS Topic with the StringMsg data type. */
  topic = domain->create_topic("helloTopic", 
			       "StringMsg", 
			       TOPIC_QOS_DEFAULT, 
			       NULL, 
			       0 );
  if ( topic == NULL )
    {
      printf("ERROR creating topic.\n");
      return -1;
    }
  
  /* Create a DataWriter on the hello topic, with
   * default QoS settings and no listeners.
   */
  dw = publisher->create_datawriter( topic, 
				     DATAWRITER_QOS_DEFAULT, 
				     NULL, 
				     0 );
  if (dw == NULL)
    {
      printf("ERROR creating data writer\n");
      return -1;
    }

  /* Initialize the data to send.  The StringMsg data type
   * has just one string member.
   * Note: Alwyas initialize a string member with
   * allocated memory -- the destructor will free 
   * all string members.  
   */
  stringMsg.msg = new char[strlen("Hello WORLD from C++!")+1];
  strcpy(stringMsg.msg, "Hello WORLD from C++!");
  
  while ( 1 )
    {
      ReturnCode_t ret = dw->write ( &stringMsg, HANDLE_NIL ); 
      printf("wrote a sample\n");
      fflush(stdout);
      if ( ret != RETCODE_OK)
	{
	  printf("ERROR writing sample\n");
	  return -1;
	}
#ifdef _WIN32
      Sleep(1000);
#else
      sleep(1);
#endif
    }

    /* Cleanup */
    retval = domain -> delete_contained_entities();
    if ( retval != DDS_RETCODE_OK )
      printf("ERROR (%s): Unable to cleanup DDS entities\n",
	     DDS_error(retval));


  return 0;
}

file: hello_sub.cc

/****************************************************************
 *
 *  file:  hello_sub.c
 *  desc:  Provides a simple C++ 'hello world' DDS subscriber.
 *         This subscribing application will receive data
 *         from the example 'hello world' publishing 
 *         application (hello_pub).
 * 
 ****************************************************************
 *
 *  This file is provided by Twin Oaks Computing, Inc
 *  as an example. It is provided in the hope that it will be 
 *  useful but WITHOUT ANY WARRANTY; without even the implied 
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 *  PURPOSE. TOC Inc assumes no liability or responsibilty for 
 *  the use of this information for any purpose.
 *  
 ****************************************************************/
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <dds/dds.hh>
#include "hello.hh"
#include "helloTypeSupport.hh"
#include "helloDataReader.hh"

#ifdef _WIN32
#  define SLEEP(a)    Sleep(a*1000)
#else
#  define SLEEP(a)    sleep(a);
#endif

int all_done = 0;

/****************************************************************
 * Construct a DataReaderListener and override the 
 * on_data_available() method with our own.  All other
 * listener methods will be default (no-op) functions.
 ****************************************************************/
class SubListener : public DataReaderListener
{
public:
  void on_data_available( DataReader * dr );
};

/****************************************************************
 * DataReader Listener Method: on_data_avail()
 *
 * This listener method is called when data is available to
 * be read on this DataReader.
 ****************************************************************/
void SubListener::on_data_available( DataReader * dr)
{
  StringMsgPtrSeq   samples;
  SampleInfoSeq     samples_info;
  ReturnCode_t      retval;
  SampleStateMask   ss = DDS_ANY_SAMPLE_STATE;
  ViewStateMask     vs = DDS_ANY_VIEW_STATE;
  InstanceStateMask is = DDS_ANY_INSTANCE_STATE;

  /* Convert to our type-specific DataReader */
  StringMsgDataReader * reader = StringMsgDataReader::narrow( dr );

  /* Take any and all available samples.  The take() operation
   * will remove the samples from the DataReader so they
   * won't be available on subsequent read() or take() calls.
   */
  retval = reader->take( &samples, &samples_info, 
			 LENGTH_UNLIMITED, 
			 ss, 
			 vs, 
			 is );
  if ( retval == RETCODE_OK )
    {
      /* iterrate through the samples */
      for ( unsigned int i = 0;i < samples.size(); i++)
	{
	  /* If this sample does not contain valid data,
	   * it is a dispose or other non-data command,
	   * and, accessing any member from this sample 
	   * would be invalid.
	   */
	  if ( samples_info[i]->valid_data)
	    printf("Sample Received:  msg %d = %s\n", i, 
		   samples[i]->msg);
	}

      fflush(stdout);

      /* read() and take() always "loan" the data, we need to
       * return it so CoreDX can release resources associated
       * with it.  
       */
      reader->return_loan( &samples, &samples_info );
    }
  else
    {
      printf("ERROR (%s) taking samples from DataReader\n",
	     DDS_error(retval));
    }
}

/****************************************************************
 * main()
 *
 * Perform CoreDX DDS setup activities:
 *   - create a Domain Participant
 *   - create a Subscriber
 *   - register the StringMsg data type
 *   - create a Topic
 *   - create a DataReader and attach the listener created above
 * And wait for data
 ****************************************************************/

#if defined(__vxworks) && !defined(__RTP__)
int hello_sub(char * args)
#else
int main(int argc, char * argv[])
#endif
{
  DomainParticipant   * domain;
  Subscriber          * subscriber;
  Topic               * topic;
  DataReader          * dr;
  SubListener           drListener;
  ReturnCode_t          retval;

  /* Get an instance of the DDS DomainPartiticpantFactory -- 
   * we will use this to create a DomainParticipant.
   */
  DomainParticipantFactory * dpf = 
    DomainParticipantFactory::get_instance();

  /* create a DomainParticipant */
  domain = 
    dpf->create_participant( 0, 
			     PARTICIPANT_QOS_DEFAULT, 
			     NULL, 
			     0 );
  if ( domain == NULL )
    {
      printf("ERROR creating domain participant.\n");
      return -1;
    }

  /* create a Subscriber */
  subscriber = domain->create_subscriber(SUBSCRIBER_QOS_DEFAULT,
					 NULL,
					 0 );
  if ( subscriber == NULL )
    {
      printf("ERROR creating subscriber\n");
      return -1;
    }
  
  /* Register the data type with the CoreDX middleware. 
   * This is required before creating a Topic with
   * this data type. 
   */
  StringMsgTypeSupport stringMsgTS;
  retval = stringMsgTS.register_type( domain, NULL );
  if (retval != RETCODE_OK)
    {
      printf("ERROR (%s) registering type\n", DDS_error(retval));
      return -1;
    }
  
  /* create a DDS Topic with the StringMsg data type. */
  topic = domain->create_topic( "helloTopic", 
				"StringMsg", 
				TOPIC_QOS_DEFAULT, 
				NULL, 
				0 );
  if ( ! topic )
    {
      printf("ERROR creating topic\n");
      return -1;
    }
  
  /* create a DDS_DataReader on the hello topic (notice
   * the TopicDescription is used) with default QoS settings, 
   * and attach our listener with our on_data_available method.
   */
  dr = subscriber->create_datareader( (TopicDescription*)topic, 
				      DATAREADER_QOS_DEFAULT,
				      &drListener, 
				      DATA_AVAILABLE_STATUS );
  if ( ! dr )
    {
      printf("ERROR creating data reader\n");
      return -1;
    }
  
  /* Wait forever.  When data arrives at our DataReader, 
   * our dr_on_data_avilalbe method will be invoked.
   */
  while ( !all_done )
    SLEEP(30);

  /* Cleanup */
  retval = domain -> delete_contained_entities();
  if ( retval != DDS_RETCODE_OK )
    printf("ERROR (%s): Unable to cleanup DDS entities\n",
	   DDS_error(retval));
  
  return 0;
}