String Formatting Helpers

fmt

For C++ users, conduit includes a built-in version of the fmt library (https://fmt.dev/). Since other projects also bundle fmt, the conduit version is modified to place everything in the conduit_fmt namespace instead of the default fmt namespace. This is a safe approach to avoid potential confusion and static linking consequences.

When using conduit in C++, you can use its built-in fmt as follows:

// conduit_fmt is installed along with conduit
#include "conduit_fmt/conduit_fmt.h"

// fmt features are in the conduit_fmt namespace
std::string res = conduit_fmt::format("The answer is {}.", 42);
std::cout << res << std::endl;

res = conduit_fmt::format("The answer is {answer:0.4f}.",
                          conduit_fmt::arg("answer",3.1415));
std::cout << res << std::endl;
The answer is 42.
The answer is 3.1415.

conduit::utils::format

In addition to direct fmt support, conduit utils provides conduit::utils::format methods that enable fmt style string formatting with the arguments are passed as a conduit::Node tree. These simplify use cases such as generating path string, allowing the pattern string and arguments to be stored as part of a conduit hierarchy (and in HDF5, YAML, etc files). This feature is also available in Conduit’s Python API (conduit.utils.format).

conduit::utils::format(string, args)

The args case allows named arguments (args passed as object) or ordered args (args passed as list).

conduit::utils::format(string, args) – object case:

// conduit::utils::format w/ args + object
// processes named args passed via a conduit Node
Node args;
args["answer"] = 42;
std::string res = conduit::utils::format("The answer is {answer:04}.",
                                        args);
std::cout << res << std::endl;

args.reset();
args["adjective"] =  "other";
args["answer"] = 3.1415;

res = conduit::utils::format("The {adjective} answer is {answer:0.4f}.",
                             args);

std::cout << res << std::endl;
The answer is 0042.
The other answer is 3.1415.

conduit::utils::format(string, args) – list case:

// conduit::utils::format w/ args + list
// processes ordered args passed via a conduit Node
Node args;
args.append() = 42;
std::string res = conduit::utils::format("The answer is {}.", args);
std::cout << res << std::endl;

args.reset();
args.append() = "other";
args.append() = 3.1415;

res = conduit::utils::format("The {} answer is {:0.4f}.", args);

std::cout << res << std::endl;
The answer is 42.
The other answer is 3.1415.

conduit::utils::format(string, maps, map_index)

The maps case also supports named or ordered args and works in conjunction with a map_index. The map_index is used to fetch a value from an array, or list of strings, which is then passed to fmt. The maps style of indexed indirection supports generating path strings for non-trivial domain partition mappings in Blueprint.

conduit::utils::format(string, maps, map_index) – object case:

// conduit::utils::format w/ maps + object
// processing named args passed via a conduit Node, indexed by map_index
Node maps;
maps["answer"].set({ 42.0, 3.1415});

std::string res = conduit::utils::format("The answer is {answer:04}.",
                                         maps, 0);
std::cout << res << std::endl;

res = conduit::utils::format("The answer is {answer:04}.", maps, 1);
std::cout << res << std::endl << std::endl;


maps.reset();
maps["answer"].set({ 42.0, 3.1415});
Node &slist = maps["position"];
slist.append() = "first";
slist.append() = "second";


res = conduit::utils::format("The {position} answer is {answer:0.4f}.",
                             maps, 0);

std::cout << res << std::endl;

res = conduit::utils::format("The {position} answer is {answer:0.4f}.",
                             maps, 1);

std::cout << res << std::endl;
The answer is 0042.
The answer is 3.1415.

The first answer is 42.0000.
The second answer is 3.1415.

conduit::utils::format(string, maps, map_index ) – list case:

// conduit::utils::format w/ maps + list
// processing ordered args passed via a conduit Node, indexed by map_index
Node maps;
maps.append() = { 42.0, 3.1415};
std::string res = conduit::utils::format("The answer is {}.",
                                         maps, 0);
std::cout << res << std::endl;

res = conduit::utils::format("The answer is {}.", maps, 1);
std::cout << res << std::endl << std::endl;

maps.reset();

// first arg
Node &slist = maps.append();
slist.append() = "first";
slist.append() = "second";

// second arg
maps.append() = { 42.0, 3.1415};

res = conduit::utils::format("The {} answer is {:0.4f}.", maps, 0);
std::cout << res << std::endl;

res = conduit::utils::format("The {} answer is {:0.4f}.", maps, 1);
std::cout << res << std::endl;
The answer is 42.
The answer is 3.1415.

The first answer is 42.0000.
The second answer is 3.1415.