![]() | Note |
---|---|
For detailed registry usage information, please refer to the Core Reference Guide. Here is only the most significant stuff. |
Let's assume we have a configuration file registry.conf containing the following text:
[ ] x : int = 3 s1 : symlink = /sub1/x s2 : symlink = /sub2/s1 [ sub1 ] x : float = 4.3 [ sub2 ] x : string = "abc" |
Now the configuration contents can be loaded using the Registry::load_config_file method. Moreover, we will use Registry::create_variable and Registry::create_symbolic_link (as obvious, creates variable or symbolic link, respectively) to make the contents of the registry database be the same as in the picture in the Section 19.2, “Registry Structure”.
Registry registry; registry.load_config_file( "registry.conf" ); registry.create_variable( "y", "/sub1/sub1", CVAR_TYPE_STRING, "abc" ); // This creates new node /sub1/sub1 and inserts a variable called // y of string type with default value "abc". registry.create_symbolic_link( "s1", "/sub2", "/sub1/sub1/y", false ); // This creates new symbolic link in the /sub2 node. This link // refers the y variable inserted in the previous call to // insert_variable. The last parameter (false) indicates that // the registry won't check whether the symbolic link target really // exists. |
![]() | Note |
---|---|
The configuration file can include other configuration files, as described in the Chapter 26, Registry Configuration File Syntax. There are even more loading routines enabling for example loading configuration relatively to some specific registry node, etc. |
![]() | Note |
---|---|
The registry doesn't allow you to delete variables or symbolic links you have created. |
![]() | Note |
---|---|
You can whenever save the registry into file using the Registry::save_config_file( <filename> ) method. Again, there are more possibilities - for example, it is possible to save only a subtree of the registry, etc. |
There are more possibilities how to access variables. The most common one is the method Registry::access_variable( <full_name> ). This routine automatically handles symbolic links; should the <full_name> refer to a symbolic link (or symbolic links chain), all the links will be resolved and a reference to the target variable (not to an alias) will be returned. Reference to ConfigVariable is returned, but this is not limiting at all, because config variables have retyping ability for all common (C built-in) types (Use for example the read_string() method; similar for other types. These methods perform relevant retyping if needed.)
// Let's assume we have the registry contents the same as in the // last example. ConfigVariableFloat & cvar_float = registry.access_variable( "/sub1/x" ); std::cout << "/sub1/x = " << cvar_float.read_float() << std::endl; // Prints out "/sub1/x = 4.3". ConfigVariableString & cvar_string = registry.access_variable( "/s2" ); std::cout << "/s2 = " << cvar_string.read_string() << std::endl; // Prints out "/s2 = ab" (although /s2 is actualy a link pointing to // the "sub2/s1", which is another symbolic link which points to // the /sub1/sub1/y at last). |
There are several options available for iterating the registry. For mere variable enumerating withing one node, use Iterator (or ConstIterator, respectively); to enumerate a single node's subnodes, SubnodeIterator (or ConstSubnodeIterator, respectively) is a good choice. If you want more complicated enumeration involving more nodes or whole subtree, you have to use the SearchContext and special registry methods.
As mentioned above, the Registry offers several types of iterators. The const and "standard" version of iterator is more or less the same; the difference is only that you cannot modify a config variable using a constant iterator. The difference between 'within-node' and subnodes iterator is also obvious - the first can be used for enumerating variables, the second for subnodes. The last item that can be contained withing registry nodes - symbolic links are enumerated together with variables. It means that the 'within-node' doesn't distinguish between symbolic links and iterators.
As usual when working with iterators, there are several registry methods yielding initial iterators. The following table shows all of them. And, of course, they contain all the operators you would expect to be available when working with pointers.
Method | Description |
---|---|
begin( const std::string & path, const std::string & pattern ) | Returns an iterator refering the first variable (or symbolic link) in the node specified by path. If pattern is given, the iterator will ignore all items that don't match this pattern (wildcards). |
end() | Returns an invalid iterator. This can be used in comparisons to detect that the iterator doesn't refer to a valid variable/symbolic link. |
begin_subnodes( const std::string & path, const std::string & pattern ) | Returns an iterator refering the first subnode of the node specified by path. If pattern is given, the iterator will ignore all subnodes that don't match this pattern (wildcards). |
end_subnodes() | Returns an invalid iterator. This can be used in comparisons to detect that the iterator doesn't refer to a valid subnode. |
![]() | Note |
---|---|
Each of the methods in the table returns either constant or 'standard' iterator, depending on the context. |
The following example prints names of all variables and aliases contained in the root node that begin with the s character; then prints all subnode names:
registry.create_variable( "str1", "/", CVAR_STRING, "first string" ); // lets insert one more value into the registry first: Registry::Iterator it( registry.begin( "/", "s*" ) ); // creates the iterator and binds it with the root node std::cout << "Variables:" << std::endl; while( it != registry.end() ) { std::cout << it->get_name() << std::endl; it++; } Registry::SubnodeIterator sub_it( registry.begin_subnodes( "/", "*" ) ); // creates the subnodes iterator and binds it with the root node std::cout << "Subnodes:" << std::endl; while( sub_it != registry.end_subnodes() ) { std::cout << *sub_it << std::endl; sub_it++; } |
The preceding example should present an output similar to the following:
For more powerful Registry enumeration, you have to use the SearchContext class directly instead of iterators. SearchContext stores all the information about where exactly the last search had stopped. That enables the next search to continue from this spot. SearchContext has many methods to get information about the refered variable or to get the variable itself. The following table summarizes most significant methods:
Method | Description |
---|---|
SearchContext open_registry_node( const std::string & path ) | Returns a search context bound with the registry node specified by the path parameter. This context can be used as a parameter of the following methods. |
bool find_variable( const std::string::value_type * pattern, SearchContext & context ) | Searches for a variable or symbolic link matching the given pattern. This search is not restricted to only one node; on the contrary, it traverses the whole registry tree. If the variable has been found, returns true and fills the context appropriately. If next time this context is passed to the method, the search will continue from where it finished the last time. |
bool search_registry_node( const std::string::value_type * pattern, SearchContext & context ) | This method is exactly the same as the previous one, but this is restricted to just one Registry node. |
The SearchContext state is always specified with one of the following types:
In the following example we are looking for all the variables called x in all the nodes contained in the registry:
SearchContext sc( open_registry_node( "/" ) ); registry.find_variable( "x", sc ); while( sc.get_type() != RRT_NOT_FOUND ) { std::cout << sc.get_name() << " = " << std::cout << sc.get_variable().read_string() << std::endl; registry.find_variable( "x", sc ); } |
Possible output of this example could be:
It is obvious that if access_variable was called each time some configuration variable is called, the efficiency would suffer a great deal (access_variable have to find the relevant node, then resolve symbolic links, etc).
The solution to this matter is the Settings class. It descendants keep references staight to the relevant config variables in the Registry. Thus, when we need to access some registry variable, we use this reference instead of calling the access_variable.
To make use of the Settings class, follow these steps (each of them accompanied with a relevant part of an example; for the example, let's suppose we want to have configuration variable ping_delay for client class Network):
Create a new settings class that inherits from the Settings and that is specific for a class you are writting settings for (let's call it the client class). The new settings class should contain references to all the config variables you want to use within the client class. Also, it should declare a default constructor.
Next, within the client class, create a variable - instance of your new settings class.
class Network
{
// some network stuff here ...
public:
enum EDefaultPingDelay
{
DEFAULT_PING_DELAY = 100
};
class NetworkSettings : public Settings
{
public:
NetworkSettings();
public:
Integer & ping_delay;
};
public:
NetworkSettings settings;
}; |
Implement the default constructor. It should set each reference withing the class to refer to the relevant config variable properly.
Network::NetworkSettings::NetworkSettings() : Settings( "Network" ), |
Now you can access the variables directly from the client class using for example settings.variable_name syntax.
// Inside some of the Network methods implementation...
std::cout << "Ping delay = " << settings.ping_delay << std::endl; |
![]() | Warning |
---|---|
Obviously, the Registry must be initialized prior to initialization of any class using the Settings. |
Sometimes you need to watch some values and their changes in time, etc. The Statistics class provides the common interface for doing so.
It works very much the same as the Settings and thus we won't repeat all the information and examples from the previous section. For more information, please refer to the Massiv Core Reference Guide. Note that the client class is responsible for updating the statistics values.
Nevertheless, we will mention one useful feature of this class, measuring of time differences. Imagine you are writing some lengthy routine using the Core and you need to watch how long this routine runs. The following example shows how to do so (let's assume that SomeClass has the statistics containing the time_difference float variable):
void SomeClass::lengthy_method() { Statistics::MeasureSystemTime measure( statistics.time_difference ); // Lengthy computation follows. ... // System time difference is assigned into the statistics.time_difference variable // at the end of the measure validity scope. } |
![]() | Note |
---|---|
All the statistics variabes are stored in the subtree of /Statistics. |
See Chapter 26, Registry Configuration File Syntax for information about configuration files.