The Hello class implements the HelloInterface interface. It remembers its name, number of times hello() has been called since the creation of the Hello object and the number of times the hello() method has been called since the most recent simulation startup.
The relevant part of the IDL looks like this:
(1) #import "hello_interface.idl" (2) (3) namespace example { (4) (5) class (6) < (7) simulation_startup_notify (8) > (9) Hello : HelloInterface (10) { (11) method register_to_naming (12) ( (13) in string name (14) ) : bool; (15) (16) property string name; (17) property vlint32 total_call_count; (18) property vlint32 current_call_count; (19) } (20) (21) } // namespace example |
As in C++, descriptions of all base classes must be included, otherwise the inheritance specification is not valid.
Only one class attribute is assigned a value directly. Setting the simulation_startup_notify attribute ensures that all Hello objects will be notified when the simulation starts. Because the root and kind attributes have the inherit semantics, and their value is not directly set, values from the HelloInterface class will be inherited; therefore Hello will be garbage collector root server-only objects. The abstract attribute does not have the inherit semantics, so the Hello class is not abstract.
![]() | Note |
---|---|
The only reason why the root attribute is set in the HelloInterface class is to make it default for all derived classes. It has no meaning for the HelloInterface class itself, because it is declared abstract. |
The Hello class introduces a new method, register_to_naming(). It has three properties: name, total_call_count and current_call_count.
The implementation of the Hello class is pretty straightforward, except for a bit weird register_to_naming(), which demonstrates usage of RPC.
The class has three properties:
(75) Massiv::Core::PString name; (76) /**< Internal name of the object. (77) It's used to print the hello message and to register (78) the object to the global naming service. */ (79) (80) Massiv::Core::PVlInt32 total_call_count; (81) /**< Number of calls to hello() since object creation. */ (82) (83) Massiv::Core::PVlInt32 current_call_count; (84) /**< Number of calls to hello() since node startup. */ |
Their types correspond to types used in the IDL, as described in Section 10.13, “Property and Argument Types”.
The initialize() method is called automatically on each object being created by the CreateObject helper class (described in Section 4.3.2, “Instantiation and Finalization”). It's a replacement of a standard C++ constructor - because at the time the real constructor is called the object is not initialized yet, you cannot modify its properties in the constructor. Unlike constructor, initialize() will never automatically call initialize() of base classes nor of member objects.
Any Hello instance initializes its name in initialize():
(10) void Hello::initialize (11) ( (12) const std::string & the_name (13) ) (14) { (15) name = the_name; (16) } |
If you wanted to create a new Hello object called “name”, the code would look like this:
Pointer< Hello > hello ( Massiv::Core::CreateObject< Hello >( "name" ) ); |
Because we want to count number of times the hello method has been called since the simulation startup, we must reset the current_call_count property to zero on each startup. We have set the simulation_startup_notify class attribute in the IDL description, so the object_updated() method will be called on each startup:
(99) void Hello::object_updated (100) ( (101) UpdateReason reason (102) ) (103) { (104) if( reason == SIMULATION_STARTUP ) (105) { (106) current_call_count = 0; (107) } (108) } |
![]() | Note |
---|---|
You can also monitor other changes of an object. See the Massiv Core Reference Guide for a complete documentation of the Object::object_updated() method. |
Now we can implement the hello() method:
(18) std::string Hello::hello (19) ( (20) const std::string & callee_name (21) ) (22) { (23) std::ostringstream oss; (24) oss << "Hello to " << callee_name << " from " << name << std::endl; (25) oss << "(called " << total_call_count << " times, " << (26) current_call_count << " since startup)" << std::endl; (27) const std::string s = oss.str(); (28) (29) total_call_count++; (30) current_call_count++; (31) (32) Global::log_info( Status::FACILITY_LIB, Status::PRIORITY_LOW, s ); (33) return s; (34) } |
The method will create a message that greets the callee, and prints the name of the Hello object and values of the two counters mentioned above. Then it will increase the count, log the message and return it to the callee.
The last method, register_to_naming(), is a bit more complex and demonstrates usage of the dynamic RPC. You won't usually have to write a method like this. For example the Massiv Demo does not use the dynamic RPC at all for example.
This method registers the Hello object to a naming service object of type Demo::Lib::NamingService. Because it uses the dynamic RPC, the Hello class can be compiled without any header from the Massiv Demo and the library can be linked to any application, even if it does not implement a naming service at all. The call will fail in that case, destroy the Hello object and return false.
The register_to_naming() method is intended to be run from the console of the Massiv Demo. These commands create a Hello object and register it to the global naming service under the name “hello”:
/connect Server1 /createobject example::Hello > $OBJID /rcall $OBJID example::Hello register_to_naming "hello" |
If the registration succeeds, anyone can query the global naming service object for the pointer to an object called “hello”, and then use the HelloInterface interface to call the hello() method. If the registration failed, the Hello object is destroyed. Therefore it's safe to run these commands multiple times: exactly one object will remain registered to the naming service and other objects will destroy themselves.
The method expects a single argument - the name of the object. This is required because if an object is created from the Massiv Demo console, its initialize() method is not called:
(36) bool Hello::register_to_naming (37) ( (38) const std::string & the_name (39) ) (40) { (41) initialize( the_name ); |
We initialize two local variables. The self object pointer points to the Hello object itself. The failed is initialized to true and is set later to false on success:
(43) const WeakPointer< Object > self( this ); (44) bool failed = true; |
All real work is enclosed within a try block. If an exception is thrown, we assume that the registration has failed.
(45) try (46) { |
Firstly, we ask the Core for the object id of the naming service object and create a remote pointer to the service object. The object id is stored in the registry in Settings/WellKnownObjectIdDatabase/naming_service_object. The Core defines location of this config variable, but it does not care about the interface of the naming service object at all - that's up to the application:
(47) const ObjectId naming_id = Global::well_known_object_id_database(). (48) get_naming_service_object(); (49) const Remote< Object > naming( naming_id ); |
Next, we acquire the metaobject of the Demo::Lib::NamingServer class:
(51) const MetaObject * const metaobject = Global::class_manager(). (52) get_metaobject( "Demo::Lib::NamingService" ); |
We have to serialize the arguments for the register_object() method to a text stream. The expected arguments are:
Name under which the object should be registered.
Pointer to the object.
A boolean flag; false means that the registration should fail if a different object is already registered under the same name.
(54) std::stringstream ss; (55) { (56) TextWriter tw( ss ); (57) const Serializer::Description desc; (58) name.text_write( tw, desc ); (59) tw.write_space(); (60) self.text_write( tw, desc ); (61) tw.write_space(); (62) const SBool replace = false; (63) replace.text_write( tw, desc ); (64) } |
Then we perform the dynamic RPC call:
(66) TextReader tr( ss ); (67) std::auto_ptr< MethodPacket > results = metaobject-> (68) remote_call_method( naming, "register_object", tr ); |
The register_object() method returns false if the registration failed. Because we don't know the type of the method packet, we must check its contents in their textual form.
(70) if( results->get_argument_value( -1 ) == "true" ) (71) { (72) failed = false; (73) } (74) else (75) { (76) std::ostringstream oss; (77) oss << "Object " << name << " already registered " (78) " to the global naming service." << std::endl; (79) Global::log_warning( Status::FACILITY_LIB, (80) Status::PRIORITY_HIGH, oss.str() ); (81) } (82) } |
Log a warning if the registration failed.
(83) catch( std::exception & e ) (84) { (85) std::ostringstream oss; (86) oss << "Failed to register " << name << " to naming service: " (87) << e.what(); (88) Global::log_warning( Status::FACILITY_LIB, (89) Status::PRIORITY_HIGH, oss.str() ); (90) } |
If the registration failed, we destroy the Hello object.
(92) if( failed ) (93) { (94) Massiv::System::dispose_gc_root( self ); (95) } (96) return failed ? false : true; (97) } |