Odbxx: Difference between revisions

From MidasWiki
Jump to navigation Jump to search
(The midas documentation has moved to https://midas.triumf.ca/docs/)
Tag: Replaced
 
(10 intermediate revisions by 2 users not shown)
Line 1: Line 1:
A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.
The midas documentation has moved. See [https://midas.triumf.ca/docs/develop/api/cpp/odbxx/ Midas C++ API - midas::odb (odbxx)] for the new documentation about the odbxx C++ interface to the ODB.
 
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].
 
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.
 
== Basic usage ==
 
The simplest usage is like:
 
<pre>// Grab a bit of the ODB
midas::odb exp("/Experiment");
 
// Simple read
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;
 
// Make a change. The new value is automatically sent to the ODB.
// Most C++ operators are supported (++, += etc), or you can do a simple
// re-assignment like `exp["Transition timeout"] = 12345;`.
exp["Transition timeout"] += 100;
 
// Read the new value
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;
</pre>
 
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:
 
<pre>int curr_timeout = exp["Transition timeout"];</pre>
 
'''Note''': The ODB directory you connect to ("/Experiment" in the above example), has to start with a "/" to tell the midas::odb object
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.
 
== Automatic refreshing ==
 
You may temporarily disable the automatic updating to/from the ODB
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.
 
If auto-refresh is enabled (the default), your new values are sent to
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB
is queried for new values whenever you access the value. In the above
example, the ODB is queried 4 times (during construction of <code>exp</code>, and
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1
time (when <code>exp["Transition timeout"]</code> is assigned to).
 
See the [[#Callback functions]] section below for details on how to have a function
called when a value changes.
 
== Arrays/vectors ==
 
ODB arrays are represented by std vectors.
 
You can access/edit individual elements using []:
 
<pre>odb["Example"][1] = 1.2;</pre>
 
You can completely re-assign content using a std::vector or std::array:
 
<pre>std::vector<float> vec = std::vector<float>(10);
odb["Example"] = vec;
</pre>
 
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,
it will be truncated; if shorter it will be extended with default values
(0 or an empty string).
 
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre>
 
Note that arithmetic operators are supported for arrays, and will apply
the operation to ALL ELEMENTS IN THE ARRAY:
 
<pre>// Create the vector
std::vector<float> vec = std::vector<float>(2);
vec[0] = 3;
vec[1] = 5;
 
// Assign in ODB
odb["Example"] = vec;
 
// Multiply ALL elements by 2
odb["Example"] *= 2;
 
// odb["Example"] now contains {6, 10}.
</pre>
 
You can directly iterate over arrays/vectors:
 
<pre>// Iterating using standard begin/end.
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {
  int val = *it;
  std::cout << val << std::endl;
}
</pre>
 
<pre>// Iterating using C++11 range-based for loop.
for (int val : o["Int Array"]) {
  std::cout << val << std::endl;
}
</pre>
 
== Strings ==
 
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code>
family of functions, where strings are returned as char*). You may have vectors of strings.
 
== Creating new bits of the ODB ==
 
You can automatically create bits of the ODB by passing a struct to the
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:
 
<pre>// Define the ODB structure
midas::odb o = {
  {"Int32 Key", 42},
  {"Bool Key", true},
  {"Subdir", {
      {"Float key", 1.2f},    // floats must be explicitly specified
  }},
  {"Int Array", {1, 2, 3}},
  {"Double Array", {1.2, 2.3, 3.4}},
  {"String Array", {"Hello1", "Hello2", "Hello3"}},
  {"Large Array", std::array<int, 10>{} },  // array with explicit size
  {"Large String", std::string(63, '\0') },  // string with explicit size
};
 
// Then sync the structure. This function
// - keeps the existing value of any keys that are in the ODB and your code
// - creates any keys that are in your code but not yet in the ODB
o.connect("/Test/Settings");
 
// If you make the `write_defaults` argument true, then the function
// - overwrites the value of any keys that are in the ODB with the value in your code
// - creates any keys that are in your code but not yet in the ODB
o.connect("/Test/Settings", true);
 
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and
// - keeps the existing value of any keys that are in the ODB and your code
// - creates any keys that are in your code but not yet in the ODB
// - deletes any keys that are in the ODB but not your code
// - updates the order of keys in the ODB to match your code
o.connect_and_fix_structure("/Test/Settings");
</pre>
 
Note that the ODB path in teh odb::connect() call must start with a '/'. The ODB root path like <code>o.connect("/");</code> is not allowed in this call.
 
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:
 
<pre>midas::odb existing_key("/MyExistingKey");
existing_key["MyNewSubKey"] = 1.23;
</pre>
 
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.
 
<pre>midas::odb existing_key("/MyExistingKey");
double val = existing_key["MyNewSubKey"](1.23);
</pre>
 
== Iterating over subkeys ==
 
You can iterate over subkeys using normal iterator functions.
 
<pre>// Iterating using standard begin/end.
midas::odb exp("/Experiment");
 
for (auto it = exp.begin(); it != exp.end(); it++) {
  midas::odb& subkey = *it;
  std::cout << subkey.get_name() << " = " << subkey << std::endl;
}
</pre>
 
<pre>// Iterating using C++11 range-based for loop.
for (midas::odb& subkey : exp) {
  std::cout << subkey.get_name() << " = " << subkey << std::endl;
}
</pre>
 
You can check whether a subkey exists using <code>odb::is_subkey()</code>.
 
== Callback functions ==
 
You may also set up callback functions that are called whenever a value
changes, using the <code>odb::watch()</code> function. Note that you must call
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it
is <code>cm_yield()</code> itself that calls your callback function.
 
The callback functions can either be a "normal" function, a C++ lambda, or a member function of a C++ class.
In all cases it should accept one argument - a <code>midas::odb</code> object (passed
by reference) that contains the new state.
 
<pre>// Example with a lambda:
midas::odb to_watch("/Experiment");
to_watch.watch([](midas::odb &arg) {
  std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;
});
</pre>
 
<pre>// Example with a "normal" function:
void my_function(midas::odb &arg) {
  std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;
}
 
midas::odb to_watch("/Experiment");
to_watch.watch(my_function);
</pre>
 
<pre>// Example with a member function of a class:
#include <functional>
 
void MyClass::my_function(midas::odb& arg) {
  std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;
}
 
void MyClass::some_initialisation_code() {
  // Arguments to std::bind() are: "member function to call", "object to call that function on", "placeholder for midas::odb arg"
  std::function<void(midas::odb&)> callback = std::bind(&MyClass::my_function, this, std::placeholders::_1);
 
  midas::odb to_watch("/Experiment");
  to_watch.watch(callback);
}
</pre>
 
== Utility functions ==
 
There are various utility functions which can be used:
 
==== void odb::create(const char *name, int type) ====
 
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.
 
==== void odb::delete_key() ====
 
This member function of a midas::odb object deletes that object from the ODB:
 
<pre>midas::odb o("/Some/ODB/Path");
o.delete_key();
</pre>
 
==== int odb::delete_key(const std::string &name) ====
 
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.
 
==== bool odb::exists(const std::string *name) ====
 
This boolean function checks if a key given by its name exists in the ODB.
 
==== void odb::exists(const std::string *name) ====
 
This boolean function checks if a key given by its name exists in the ODB.
 
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====
 
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.
 
== Example code ==
 
A full working example exploring most of the features can be found in
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).

Latest revision as of 22:01, 30 October 2026

The midas documentation has moved. See Midas C++ API - midas::odb (odbxx) for the new documentation about the odbxx C++ interface to the ODB.