DiCE API nvidia_logo_transpbg.gif Up
Example for Database Elements and Jobs
[Previous] [Next] [Up]

This example demonstrates the implemention of database elements and jobs.

New Topics

  • Implementation of database elements
  • Implementation of jobs
  • Using database elements and jobs

Detailed Description

Implementation of database elements


Database elements are derived from the mi::neuraylib::IElement interface. There are two ways to accomplish this:

  • Derive your own interface IMy_element from mi::base::Interface_declare<...,mi::neuraylib::IElement> and implement it in My_element derived from mi::base::Interface_implement<IMy_element>.
  • Derive your implementation without separate interface from the mixin class mi::neuraylib::Element.

The first approach is required to implement interface hierarchies. It follows the design principle of interfaces with only pure virtual methods and separate implementation classes. The second approach eliminates the need for new interfaces. The mixin class provides default implementations for some of the pure virtual methods. This and the next example use the second approach, not only for database elements, but also for jobs and fragmented jobs.

To implement a database element, you need to provide implementations (at least) for the methods serialize(), deserialize(), copy(), and get_class_name(). In case you store references to other elements, you need to implement get_references() as well. There are several other methods for which the mi::neuraylib::Element mixin provides default implementations. In addition you can implement methods of your choice according to the desired functionality of your class. In this example, there is only a setter and getter for the single data value.

Implementation of jobs


To implement a database job, you need, similar to database elements, to provide implementations (at least) for the methods serialize(), deserialize(), copy(), and get_class_name(). The actual job is implemented in the execute() method. There are several other methods for which the mi::neuraylib::Job mixin provides default implementations.

In this example the job class references another database element. The job creates another instance of such a database element, where the value of the data member has been squared.

Using database elements and jobs


To use your database elements and jobs, and in general, to use the DiCE API, two steps are required:

You can register your classes with the DiCE API using mi::neuraylib::IDice_configuration::register_serializable_class(). A DiCE transaction can be obtained from a regular transaction by means of its get_interface() method.

The DiCE transaction offers similar to a regular transaction methods to store, retrieve, and remove database elements. Elements are identified by tags. Names can be associated with tags for the users convenience but are not used otherwise.

Jobs are executed if their tag is accessed for the first time (and the job has not been executed yet). However, it is strongly recommended to call advise() for such tags first. This allows the parallel execution of jobs, e.g., in other threads or on other hosts.

Example Source

Source Code Location: examples/example_element_and_job.h

/******************************************************************************
* Copyright 2024 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_element_and_job.h
//
// An user-defined database element and job shared by several examples.
#ifndef EXAMPLE_ELEMENT_AND_JOB_H
#define EXAMPLE_ELEMENT_AND_JOB_H
#include <mi/dice.h>
// A simple database element which stores one mi::Uint64 values.
class My_element
: public mi::neuraylib::Element<0xc3711242,0x5aed,0x4bd7,0xa0,0xa4,0x9c,0x2d,0x7f,0x48,0xfc,0x40>
{
public:
My_element( mi::Uint64 data = 0) : m_data( data) { }
void serialize( mi::neuraylib::ISerializer* serializer) const { serializer->write( &m_data); }
void deserialize( mi::neuraylib::IDeserializer* deserializer) { deserializer->read( &m_data); }
mi::neuraylib::IElement* copy() const { return new My_element( *this); }
const char* get_class_name() const { return "My_element"; }
mi::Uint64 get_data() const { return m_data; }
void set_data( mi::Uint64 data) { m_data = data; }
private:
mi::Uint64 m_data;
};
// A simple job class which accesses a database element of type My_element and returns a new
// database element of the same type. The job can be delegated to other hosts.
class My_job
: public mi::neuraylib::Job<0x55ae55aa,0x7996,0x4b49,0xba,0xd8,0xe2,0xdc,0x4b,0xa4,0x85,0x57>
{
public:
My_job( mi::neuraylib::Tag tag = mi::neuraylib::Tag()) : m_tag( tag) { }
void serialize( mi::neuraylib::ISerializer* serializer) const { serializer->write( &m_tag); }
void deserialize( mi::neuraylib::IDeserializer* deserializer) { deserializer->read( &m_tag); }
mi::neuraylib::IJob* copy() const { return new My_job( *this); }
const char* get_class_name() const { return "My_job"; }
// Executes the job by accessing the database element indicated by the stored tag.
// Returns a new database element where the value of the data member has been squared.
{
mi::base::Handle<const My_element> element( transaction->access<My_element>( m_tag));
mi::Uint64 result = element->get_data() * element->get_data();
return new My_element( result);
}
// Sets the tag for the database element to be used during job execution.
void set_tag( mi::neuraylib::Tag tag) { m_tag = tag; }
private:
// Tag of the database element to be used during job execution
};
#endif // EXAMPLE_ELEMENT_AND_JOB_H
Handle class template for interfaces, automatizing the lifetime control via reference counting.
Definition: handle.h:113
This mixin class can be used to implement the mi::neuraylib::IElement interface.
Definition: dice.h:1542
Source for deserializing objects from byte streams.
Definition: ideserializer.h:35
virtual bool read(bool *value, Size count=1)=0
Reads values of type bool from the deserializer.
A transaction provides a consistent view on the database.
Definition: dice.h:272
virtual const base::IInterface * access(Tag_struct tag)=0
Retrieves an element from the database.
This interface represents the abstract base class for all database elements.
Definition: dice.h:837
This interface represents the base class for all database jobs.
Definition: dice.h:957
Target for serializing objects to byte streams.
Definition: iserializer.h:171
virtual bool write(const bool *value, Size count=1)=0
Writes values of type bool to the serializer.
This mixin class can be used to implement the mi::neuraylib::IJob interface.
Definition: dice.h:1653
A tag represents a unique identifier for database elements in the database.
Definition: iserializer.h:54
DiCE API.
unsigned long long Uint64
64-bit unsigned integer.
Definition: types.h:62

Source Code Location: examples/example_element_and_job.cpp

/******************************************************************************
* Copyright 2023 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_element_and_job.cpp
#include <mi/dice.h>
// Include code shared by all examples.
#include "example_shared.h"
// Include definition of My_element and My_job.
#include "example_element_and_job.h"
// Test the classes My_element and My_job.
void test_my_element_and_my_job( mi::neuraylib::IDice_transaction* transaction)
{
// Create a new database element.
mi::base::Handle<My_element> element( new My_element( 42));
// Check that there is no database element yet with the name "the_element". Database elements
// are typically identified via their tags. Names are just for convenience of the user.
mi::neuraylib::Tag element_tag = transaction->name_to_tag( "the_element");
check_success( !element_tag);
// Store the database element and obtain the resulting tag. Note that after the element is
// stored in the database the pointer to it may *not* be used anymore. Access the element via
// the database if you need it later.
element_tag = transaction->store( element.get(), mi::neuraylib::Tag(), "the_element");
check_success( element_tag);
// Check that there is now a database element with the name "the_element".
mi::neuraylib::Tag element_tag2 = transaction->name_to_tag( "the_element");
check_success( element_tag2 == element_tag);
// Access the database element and check the value.
mi::base::Handle<const My_element> access1( transaction->access<My_element>( element_tag));
check_success( access1->get_data() == 42);
// Edit the stored database element. The will create a new version of the database element,
// such that access1 is still valid.
mi::base::Handle<My_element> edit( transaction->edit<My_element>( element_tag));
edit->set_data( 2);
// Commit the edited database element. The same can be achieved by letting the object go out
// of scope (or by calling edit.reset()).
edit = 0;
// Check that the initial access is unchanged.
check_success( access1->get_data() == 42);
// Re-access the database element and check that is shows the new value.
mi::base::Handle<const My_element> access2( transaction->access<My_element>( element_tag));
check_success( access2->get_data() == 2);
// Create a job and reference the database element stored above. The job will access it during
// its execution. The execution may be done on a different host. In this case the database
// element will be transported to that host.
mi::base::Handle<My_job> job( new My_job( element_tag));
// Check that there is no job yet with the name "the_job". Jobs are typically identified via
// their tags. Names are just for convenience of the user.
mi::neuraylib::Tag job_tag = transaction->name_to_tag( "the_job");
check_success( !job_tag);
// Store the job in the database. As for the database element do *not* use the pointer to the
// job anymore.
job_tag = transaction->store( job.get(), mi::neuraylib::Tag(), "the_job");
check_success( job_tag);
// Check that there is now a job with the name "the_job".
mi::neuraylib::Tag job_tag2 = transaction->name_to_tag( "the_job");
check_success( job_tag2 == job_tag);
// Advise the job. This tells the scheduler that the job result will be needed soon. Advising
// the job instead of directly accessing its result allows the scheduler to execute it already
// asynchronously in the background.
transaction->advise( job_tag);
// Access the job result. This method does not return before the job was executed. It returns a
// database element of type My_element.
mi::base::Handle<const My_element> access3( transaction->access<My_element>( job_tag));
// Check the result of the job execution.
check_success( access3->get_data() == 4);
}
// Register the serializable classes.
void configuration( mi::neuraylib::INeuray* neuray)
{
// Register the classes My_element and My_job with DiCE.
check_success( dice_configuration.is_valid_interface());
check_success( dice_configuration->register_serializable_class<My_element>());
check_success( dice_configuration->register_serializable_class<My_job>());
}
// Obtain a DiCE transaction and call test_elements_and_jobs().
void run( mi::neuraylib::INeuray* neuray)
{
// Get the database, the global scope of the database and create a transaction in the global
// scope.
check_success( database.is_valid_interface());
mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
scope->create_transaction<mi::neuraylib::IDice_transaction>());
check_success( transaction.is_valid_interface());
// Test the My_element and My_job classes.
test_my_element_and_my_job( transaction.get());
// All transactions need to get committed or aborted.
transaction->commit();
}
int main( int /*argc*/, char* /*argv*/[])
{
// Access the DiCE library
mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
check_success( neuray.is_valid_interface());
// Configure the DiCE library
configuration( neuray.get());
// Start the DiCE library
mi::Sint32 result = neuray->start();
check_start_success( result);
// Run the tests for My_element and My_job
run( neuray.get());
// Shut down the DiCE library
check_success( neuray->shutdown() == 0);
neuray = 0;
// Unload the DiCE library
check_success( unload());
keep_console_open();
return EXIT_SUCCESS;
}
This interface is used to interact with the distributed database.
Definition: idatabase.h:293
This interface allows configuration of DiCE.
Definition: dice.h:82
virtual Tag_struct name_to_tag(const char *name)=0
Returns the tag associated with a name in the database.
virtual base::IInterface * edit(Tag_struct tag)=0
Retrieves an element from the database and returns it ready for editing.
virtual Tag_struct store(IElement *element, Tag_struct tag=NULL_TAG, const char *name=0, Privacy_level privacy_level=LOCAL_SCOPE)=0
Stores a new database element in the database.
virtual void advise(Tag_struct tag)=0
Advises the database that a given tag is required soon.
virtual Sint32 commit()=0
Commits the transaction.
This is an object representing the DiCE library.
Definition: ineuray.h:44
virtual Sint32 shutdown(bool blocking=true)=0
Shuts down the library.
virtual base::IInterface * get_api_component(const base::Uuid &uuid) const =0
Returns an API component from the DiCE API.
virtual Sint32 start(bool blocking=true)=0
Starts the operation of the DiCE library.
signed int Sint32
32-bit signed integer.
Definition: types.h:46
[Previous] [Next] [Up]