Author: İlhami KIVRAK & Gökhan KILIÇ, Senior SW Engineers – Management Applications Group
People get confused when they firstly hear that it’s possible to develop object-oriented software with C and UML. On the other hand this is quite normal to the developers who are familiar with the IBM Rhapsody. The magic behind this technology is that IBM Rhapsody in C platform generates the code just like the early C++ compilers. Yes, it is true that once upon a time C++ toolchains had a C translators such as AT&T CFront translator of Stroustrup .
Anyways, since we learn that Rhapsody in C provides us an object-oriented API just like earlier versions of C++, it is time to do an object-oriented design pattern using this platform. Actually, we will not only design a singleton pattern but also a thread created this way. We will demonstrate a simple logger implementation as an example and we will not go into the details like, timestamping, log level etc. to prevent simplicity.
Here is the main idea: There will be an extern scoped C function named “log_message”. All modules will just know this prototype and will not be aware of any logger thread and its details working in background. “log_message” will send an event that contains the log message to the singleton instance of logger thread which runs at a lower priority with a limited event queue size.
- Create a Rhapsody Package and an implementation Class. Implementation Package’s global function “log_message” will be responsible for copying the log message and sending it as an event to the implementation Class. Implementation class is an event receiver thread that receives log messages from the event queue. ( ev_loggerLogMessage ) Doing so, Rhapsody framework will handle log buffering.
2. In order to use the thread as singleton, implementation Package should have class pointer as an attribute “ptrLogger”. This pointer will be allocated using singleton pattern with “getLoggerInstance” method. Please note that you have to set simpleLogger_Class as an active class in order to run it as a thread.
3. Up to here, there is nothing advanced. To run implementation Class as a thread, we have to use Rhapsody’s internal mechanism with auto generated methods “Class_Create” which allocates memory for class and initialize as a thread, and “startBehavior” which runs the thread. When these methods are successfully completed, the thread will be passed to the next step (“waiting_state”). At “waiting_state” thread waits until it receives log messages from the event queue. LogParameter that contains file name, line and message, will be sent via “ev_logglerLogMessage” event. When the event is received, thread will pass it to the “writing_state”. At “writing_state” message will be printed based on the parameters. Then thread returns to the “waiting_state” and waits until the next message event comes in. LogParameter variable can have attributes additionally such as log level, timestamp, function name, module name, etc.
4. Since we are ready to receive and print message, it is time to send log messages to singleton instance of logger thread. As we mentioned previously, modules will just know the “log_message(char * file , int line , char* format, …)” function however will not be aware of any logger thread etc.
We will define a macro as
So that, now logger is easy to use and messages can only send their messages with LOG_MESSAGE (“…”) as a print function.
5. Let’s test singleton logger class with tester Class. Tester class should have a <usage>dependency to “Logger_Implementation” package. Now, you can log your messages with LOG_MESSAGE(…) macro. Here is the some test messages from our test class:
We have created a singleton logger thread and its adapter function. Adapter is called with macro LOG_MESSAGE. With this way, it has been possible to pass the module name, module id, etc. information dynamically at translation time.
Adapter function is a packaging tool to pack the message and the other parameters into a structure and send it as an event. It is protected by Rhapsody mutexes for multi-thread access. The adapter spends time only to pack the message, not I/O waiting with printfs. Packed messages will be consumed by the low prio logger thread. So that, you will have both performance and filtering benefits while logging your messages.
1. Release Notes. UNIX System V AT&T Translator, 1985, 36 pages.