Medical Imaging Interaction Toolkit  2024.12.99-0da743f6
Medical Imaging Interaction Toolkit
Example 1 - Service Event Listener

This example creates a simple module that listens for service events. This example does not do much at first, because it only prints out the details of registering and unregistering services. In the next example we will create a module that implements a service, which will cause this module to actually do something. For now, we will just use this example to help us understand the basics of creating a module and its activator.

A module gains access to the C++ Micro Services API using a unique instance of ModuleContext. This unique module context can be used during static initialization of the module or at any later point during the life-time of the module. To execute code during static initialization (and de-initialization) time, the module must provide an implementation of the ModuleActivator interface; this interface has two methods, Load() and Unload(), that both receive the module's context and are called when the module is loaded (statically initialized) and unloaded, respectively.

Note
You do not need to remember the ModuleContext instance within the ModuleActivator::Load() method and provide custom access methods for later retrieval. Use the GetModuleContext() function to easily retrieve the current module's context.

In the following source code, our module implements the ModuleActivator interface and uses the context to add itself as a listener for service events (in the eventlistener/Activator.cpp file):

namespace {
class Activator : public ModuleActivator
{
private:
void Load(ModuleContext* context)
{
std::cout << "Starting to listen for service events." << std::endl;
context->AddServiceListener(this, &Activator::ServiceChanged);
}
void Unload(ModuleContext* context)
{
context->RemoveServiceListener(this, &Activator::ServiceChanged);
std::cout << "Stopped listening for service events." << std::endl;
// Note: It is not required that we remove the listener here,
// since the framework will do it automatically anyway.
}
void ServiceChanged(const ServiceEvent event)
{
std::string objectClass = ref_any_cast<std::vector<std::string> >(event.GetServiceReference().GetProperty(ServiceConstants::OBJECTCLASS())).front();
if (event.GetType() == ServiceEvent::REGISTERED)
{
std::cout << "Ex1: Service of type " << objectClass << " registered." << std::endl;
}
else if (event.GetType() == ServiceEvent::UNREGISTERING)
{
std::cout << "Ex1: Service of type " << objectClass << " unregistered." << std::endl;
}
else if (event.GetType() == ServiceEvent::MODIFIED)
{
std::cout << "Ex1: Service of type " << objectClass << " modified." << std::endl;
}
}
};
}

After implementing the C++ source code for the module activator, we must export the activator such that the C++ Micro Services library can create an instance of it and call the Load() and Unload() methods:

Now we need to compile the source code. This example uses CMake as the build system and the top-level CMakeLists.txt file could look like this:

project(CoreExamples)
cmake_minimum_required(VERSION 2.8)
find_package(CppMicroServices NO_MODULE REQUIRED)
include_directories(${CppMicroServices_INCLUDE_DIRS})
#-----------------------------------------------------------------------------
# Set C/CXX flags
#-----------------------------------------------------------------------------
#if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CppMicroServices_CXX_FLAGS}")
# set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CppMicroServices_CXX_FLAGS_RELEASE}")
# set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CppMicroServices_CXX_FLAGS_DEBUG}")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CppMicroServices_C_FLAGS}")
# set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${CppMicroServices_C_FLAGS_RELEASE}")
# set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${CppMicroServices_C_FLAGS_DEBUG}")
#endif()
#-----------------------------------------------------------------------------
# Init output directories
#-----------------------------------------------------------------------------
set(CoreExamples_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set(CoreExamples_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set(CoreExamples_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
foreach(_type ARCHIVE LIBRARY RUNTIME)
if(NOT CMAKE_${_type}_OUTPUT_DIRECTORY)
set(CMAKE_${_type}_OUTPUT_DIRECTORY ${CoreExamples_${_type}_OUTPUT_DIRECTORY})
endif()
endforeach()
function(CreateExample _name)
add_library(Example-${_name} ${ARGN})
set_property(TARGET Example-${_name} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${_name})
set_property(TARGET Example-${_name} APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE)
endif()
if(${_name}_DEPENDS)
foreach(_dep ${${_name}_DEPENDS})
include_directories(${PROJECT_SOURCE_DIR}/${_dep})
target_link_libraries(Example-${_name} Example-${_dep})
endforeach()
endif()
target_link_libraries(Example-${_name} ${CppMicroServices_LIBRARIES})
set_target_properties(Example-${_name} PROPERTIES OUTPUT_NAME ${_name})
endfunction()
add_subdirectory(eventlistener)

and the CMakeLists.txt file in the eventlistener subdirectory is:

set(_srcs Activator.cpp)
usFunctionGenerateModuleInit(_srcs)
CreateExample(eventlistener ${_srcs})

The call to usFunctionGenerateModuleInit is necessary to integrate the shared library as a module within the C++ Micro Service library. If you are not using CMake, you have to place a macro call to #US_INITIALIZE_MODULE yourself into the module's source code, e.g. in Activator.cpp. Have a look at the Getting Started documentation for more details about using CMake or other build systems (e.g. Makefiles) when writing modules.

To run the examples contained in the C++ Micro Services library, we use a small driver program called usCoreExamplesDriver:

CppMicroServices-build> bin/usCoreExamplesDriver
> h
h               This help text
l <id | name>   Load the module with id <id> or name <name>
u <id>          Unload the module with id <id>
s               Print status information
q               Quit
>

Typing s at the command prompt lists the available, loaded, and unloaded modules. To load the eventlistener module, type l eventlistener at the command prompt:

> s
Id | Name                 | Status
-----------------------------------
 - | dictionaryclient     | -
 - | dictionaryclient2    | -
 - | dictionaryclient3    | -
 - | dictionaryservice    | -
 - | eventlistener        | -
 - | frenchdictionary     | -
 - | spellcheckclient     | -
 - | spellcheckservice    | -
 1 | CppMicroServices     | LOADED
> l eventlistener
Starting to listen for service events.
>

The above command loaded the eventlistener module (by loading its shared library). Keep in mind, that this module will not do much at this point since it only listens for service events and we are not registering any services. In the next example we will register a service that will generate an event for this module to receive. To exit the usCoreExamplesDriver, use the q command.

Next: Example 2 - Dictionary Service Module

usModuleActivator.h
ModuleContext::AddServiceListener
void AddServiceListener(const ServiceListener &delegate, const std::string &filter=std::string())
ModuleContext
Definition: usModuleContext.h:91
US_USE_NAMESPACE
#define US_USE_NAMESPACE
Definition: usGlobalConfig.h:75
us::ServiceConstants::OBJECTCLASS
const US_Core_EXPORT std::string & OBJECTCLASS()
US_BUILD_SHARED_LIBS
#define US_BUILD_SHARED_LIBS
Definition: usGlobalConfig.h:9
US_EXPORT_MODULE_ACTIVATOR
#define US_EXPORT_MODULE_ACTIVATOR(_activator_type)
Export a module activator class.
Definition: usModuleActivator.h:119
ModuleContext::RemoveServiceListener
void RemoveServiceListener(const ServiceListener &delegate)
usModuleContext.h