{"id":20358208,"url":"https://github.com/irods/irods_library_examples","last_synced_at":"2026-03-19T15:27:47.317Z","repository":{"id":46332653,"uuid":"193386257","full_name":"irods/irods_library_examples","owner":"irods","description":"Examples demonstrating how to use iRODS APIs","archived":false,"fork":false,"pushed_at":"2021-12-31T01:52:50.000Z","size":17,"stargazers_count":2,"open_issues_count":2,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-01-15T01:43:01.485Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/irods.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-23T19:14:07.000Z","updated_at":"2021-12-31T01:52:53.000Z","dependencies_parsed_at":"2022-08-22T08:50:44.730Z","dependency_job_id":null,"html_url":"https://github.com/irods/irods_library_examples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irods%2Firods_library_examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irods%2Firods_library_examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irods%2Firods_library_examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/irods%2Firods_library_examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/irods","download_url":"https://codeload.github.com/irods/irods_library_examples/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241895344,"owners_count":20038512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T23:25:56.217Z","updated_at":"2026-03-05T20:43:02.976Z","avatar_url":"https://github.com/irods.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# iRODS Library Examples\nThe goal of this repository is to provide simple examples demonstrating how to use the new libraries available in iRODS.\n\n### Table of Contents\n- [iRODS Query Iterator](#irods-query-iterator)\n- [iRODS Query Builder](#irods-query-builder)\n- [iRODS Connection Pool](#irods-connection-pool)\n- [iRODS Thread Pool](#irods-thread-pool)\n- [iRODS Filesystem](#irods-filesystem)\n- [iRODS IOStreams](#irods-iostreams)\n- [iRODS Query Processor](#irods-query-processor)\n- [iRODS With Durability](#irods-with-durability)\n- [iRODS Key Value Proxy](#irods-key-value-proxy)\n\n## iRODS Query Iterator\nAvailable Since: v4.2.5\n\nDemonstrates how to use `irods::query` to query the catalog.\n```c++\n#include \u003cirods/irods_query.hpp\u003e\n\n#include \u003cstring\u003e\n#include \u003ciostream\u003e\n\nvoid print_all_resource_names(rcComm_t\u0026 _conn)\n{\n    // Print all resource names known to iRODS.\n    for (auto\u0026\u0026 row : irods::query\u003crcComm_t\u003e{\u0026_conn, \"select RESC_NAME\"}) {\n        std::cout \u003c\u003c row[0] \u003c\u003c '\\n';\n    }\n}\n```\n\n## iRODS Query Builder\nAvailable Since: v4.2.7\n\nDemonstrates how to construct query iterators via the query builder.\n```c++\n#include \u003cirods/query_builder.hpp\u003e\n\n#include \u003cvector\u003e\n\nvoid make_query()\n{\n    auto conn = // Our iRODS connection.\n\n    // Construct the builder.\n    // Builders can be copied and moved.\n    irods::experimental::query_builder builder; \n\n    // Set the arguments for how the query should be constructed.\n    // A reference to the builder object is always returned after setting an argument.\n    //\n    // Here, we are setting the type of query to construct as well as the zone\n    // for which the query applies.\n    //\n    // The type always defaults to \"general\".\n    builder.type(irods::experimental::query_type::general)\n           .zone_hint(\"other_zone\");\n\n    // To construct the query, call build and pass the C type of\n    // the connection and the SQL-like query statement.\n    //\n    // If the query string is empty, an exception will be thrown.\n    auto general_query = builder.build\u003crcComm_t\u003e(conn, \"select COLL_NAME\");\n\n    // Use the query object as you normally would.\n    for (auto\u0026\u0026 row : general_query) {\n        // Process results ...\n    }\n\n    // We can create more query objects using the same builder.\n    // Let's try a specific query!\n\n    // For specific queries, it is important to remember that the argument vector\n    // is not copied into the query object. This means the argument vector must live\n    // longer than the query object constructed by the builder.\n    std::vector\u003cstd::string\u003e args{\"/other_zone/home/rods\"};\n\n    // All that is left is to update the builder options.\n    // The zone is already set from a previous call.\n    // So just bind the arguments and change the query type.\n    auto specific_query = builder.type(irods::experimental::query_type::specific)\n                                 .bind_arguments(args)\n                                 .build\u003crcComm_t\u003e(conn, \"ShowCollAcls\");\n\n    for (auto\u0026\u0026 row : specific_query) {\n        // Process results ...\n    }\n\n    // Builders can also be reset to their original state by calling clear.\n    builder.clear();\n}\n```\n\n## iRODS Connection Pool\nAvailable Since: v4.2.5\n\nDemonstrates how to use `irods::connection_pool`.\n```c++\n#include \u003cirods/rodsClient.h\u003e\n#include \u003cirods/connection_pool.hpp\u003e\n\nvoid init_connection_pool()\n{\n    rodsEnv env;\n\n    if (getRodsEnv(\u0026env) \u003c 0) {\n        // Handle error.\n    }\n\n    const int connection_pool_size = 4;\n    const int refresh_time_in_secs = 600;\n\n    // Creates a connection pool that manages 4 rcComm_t connections\n    // and refreshes each connection every 600 seconds.\n    irods::connection_pool pool{connection_pool_size,\n                                env.rodsHost,\n                                env.rodsPort,\n                                env.rodsUserName,\n                                env.rodsZone,\n                                refresh_time_in_secs};\n\n    // As an alternative to the steps above, you can use the following free\n    // function to construct a connection pool. This function simply automates\n    // all of the steps preceding this line. The primary difference is that the\n    // pool is allocated on the heap and is returned via a shared pointer.\n    //\n    //     auto pool = irods::make_connection_pool(4);\n\n    // Get a connection from the pool.\n    // \"conn\" is returned to the pool when it goes out of scope.\n    // The type returned from the pool is moveable, but it cannot be copied.\n    auto conn = pool.get_connection();\n\n    // The object returned from the pool is a proxy for an rcComm_t and\n    // can be implicitly cast to a reference to rcComm_t.\n    rcComm_t\u0026 reference = conn;\n\n    // Here is an example of casting to a pointer.\n    // Use this for C APIs.\n    auto* pointer = static_cast\u003crcComm_t*\u003e(conn);\n\n    // You can also take ownership of connections created by the connection pool.\n    // Taking ownership means the connection is no longer managed by the connection pool\n    // and you are responsible for cleaning up any resources allocated by the connection.\n    // Once the connection is released, the connection pool will create a new connection\n    // in its place.\n    auto* released_conn = conn.release();\n\n    // Because connections can be released from the pool, it makes sense to provide an\n    // operation for checking if the connection proxy still manages a valid connection.\n    // Connection objects are now convertible to bool.\n    if (conn) {\n        // Do something with the connection ...\n    }\n}\n```\n\n## iRODS Thread pool\nAvailable Since: v4.2.5\n\nDemonstrates how to use `irods::thread_pool`.\n```c++\n#include \u003cirods/thread_pool.hpp\u003e\n\nvoid schedule_task_on_thread_pool()\n{\n    // Creates a thread pool with 4 threads.\n    // iRODS thread pool will never launch more than \"std::thread::hardware_concurrency()\" threads.\n    irods::thread_pool pool{4};\n\n    // This is one way to schedule a task for execution\n    // \"irods::thread_pool::defer\" schedules the task on the thread pool. If the current thread\n    // belongs to the thread pool, then the task is scheduled after the current thread returns and\n    // control is returned back to the thread pool. The task is never run inside of the \"defer\" call.\n    irods::thread_pool::defer(pool, [] {\n        // Do science later!\n    });\n\n    // This is a function object.\n    struct scientific_task\n    {\n        void operator()()\n        {\n            // Do science!\n        }\n    };\n\n    scientific_task task;\n\n    // This is just like \"defer\" except the task is scheduled immediately. The task is never\n    // executed inside of the \"post\" call.\n    irods::thread_pool::post(pool, task);\n\n    // This is just like \"post\" except, if the current thread belongs to the thread pool, then\n    // the task is executed directly inside of the call to \"dispatch\".\n    irods::thread_pool::dispatch(pool, [] {\n        // Do science!\n    });\n\n    // Wait until ALL tasks have completed.\n    // If this is not called, then on destruction of the thread pool, all tasks that have not\n    // been executed are cancelled. Tasks that are still executing are allowed to finish.\n    pool.join();\n}\n```\n\n## iRODS Filesystem\nAvailable Since: v4.2.6\n\nDemonstrates how to iterate over collections as well as other functionality.\nBecause it implements the ISO C++17 Standard Filesystem library, you may use the documentation at [cppreference](https://cppreference.com).\n\nHere are some helpful links:\n- [iRODS Filesystem Headers](https://github.com/irods/irods/blob/master/lib/filesystem/include/filesystem)\n- [Most commonly used functions](https://github.com/irods/irods/blob/master/lib/filesystem/include/filesystem/filesystem.hpp)\n```c++\n// If you are writing server-side code and wish to enable the server-side API, you must\n// define the following macro before including the library.\n//\n//    IRODS_FILESYSTEM_ENABLE_SERVER_SIDE_API\n//\n#include \u003cirods/filesystem.hpp\u003e\n\nvoid iterating_over_collections()\n{\n    // IMPORTANT!!!\n    // ~~~~~~~~~~~~\n    // Notice that this library exists under the \"experimental\" namespace.\n    // This is important if you're considering using this library. It means that any\n    // library under this namespace could change in the future. Changes are likely\n    // to only occur based on feedback from the community.\n    namespace fs = irods::experimental::filesystem;\n\n    // iRODS Filesystem has two namespaces, client and server.\n    // Not all classes and functions require the use of these namespaces.\n\n    try {\n        auto conn = // Our iRODS connection.\n\n        // Here's an example of how to iterate over a collection on the client-side.\n        // Notice how the \"client\" namespace follows the \"fs\" namespace alias.\n        // This is required by some functions and classes to control which implementation\n        // should be used. If you wanted to do this on the server-side, you would replace\n        // \"client\" with \"server\". This does not recurse into subcollections.\n        for (auto\u0026\u0026 e : fs::client::collection_iterator{conn, \"/path/to/collection\"}) {\n            // Do something with the collection entry.\n        }\n\n        // To recursively iterate over a collection and all of its children, use a\n        // recursive iterator.\n        for (auto\u0026\u0026 e : fs::client::recursive_collection_iterator{conn, \"/path/to/collection\"}) {\n            // Do something with the collection entry.\n        }\n\n        // These iterators support shallow copying. This means, if you copy an iterator,\n        // subsequent operations on the copy, such as iterating to the next entry, will\n        // be visible to the original iterator.\n\n        //\n        // Let's try something new.\n        //\n        \n        // How about getting the size of a data object.\n        auto size = fs::client::data_object_size(conn, \"/path/to/data_object\");\n\n        // Or checking if an object exists.\n        if (fs::client::exists(conn, path)) {\n            // Do something with it.\n        }\n    }\n    catch (const fs::filesystem_error\u0026 e) {\n        // Handle error.\n    }\n}\n```\n\n## iRODS IOStreams\nAvailable Since: v4.2.6\n\nDemonstrates how to use `dstream` and `default_transport` to read and write data objects.\n```c++\n// Defines 3 classes:\n// - idstream: Input-only stream for reading data objects.\n// - odstream: Output-only stream for writing data objects.\n// - dstream : Bidirectional stream for reading and writing data objects.\n#include \u003cirods/dstream.hpp\u003e\n\n// Defines the default transport mechanism for transporting bytes via the iRODS protocol.\n//\n// If you are writing server-side code and wish to enable the server-side API, you must\n// define the following macro before including the transport library following this comment.\n//\n//     IRODS_IO_TRANSPORT_ENABLE_SERVER_SIDE_API\n//\n#include \u003cirods/transport/default_transport.hpp\u003e\n\nvoid write_to_data_object()\n{\n    // IMPORTANT!!!\n    // ~~~~~~~~~~~~\n    // Notice that this library exists under the \"experimental\" namespace.\n    // This is important if you're considering using this library. It means that any\n    // library under this namespace could change in the future. Changes are likely\n    // to only occur based on feedback from the community.\n    namespace io = irods::experimental::io;\n\n    auto conn = // Our iRODS connection.\n\n    // Instantiates a new transport object which uses the iRODS protocol to read and\n    // write bytes into a data object. Transport objects are designed to be used by IOStreams\n    // objects such as dstream. \"default_transport\" is a wrapper around the iRODS C API for\n    // reading and writing data objects.\n    //\n    // You can add support for more transport protocols by implementing the following interface:\n    //\n    //     https://github.com/irods/irods/blob/master/lib/core/include/transport/transport.hpp\n    //\n    io::client::default_transport xport{conn};\n\n    // Here, we are creating a new output stream for writing. If the data object exists, then\n    // the existing data object is opened, else a new data object is created.\n    // We could have also used \"dstream\" itself, but then we'd need to pass in openmode flags\n    // to instruct iRODS on how to open the data object.\n    io::odstream out{xport, \"/path/to/data_object\"};\n\n    if (!out) {\n        // Handle error.\n    }\n\n    std::array\u003cchar, 4_Mb\u003e buffer{}; // Buffer full of data.\n\n    // This is the fastest way to write data into iRODS via the new stream API.\n    // Bytes written this way are stored directly in the buffer as is.\n    out.write(buffer.data(), buffer.size());\n\n    // This will also write data into the data object. This is slower than the previous method\n    // because stream operators format data.\n    out \u003c\u003c \"Here is some more data ...\\n\";\n}\n\nvoid read_from_data_object()\n{\n    namespace io = irods::experimental::io;\n\n    auto conn = // Our iRODS connection.\n\n    // See function above for information about this type.\n    io::client::default_transport xport{conn};\n\n    // Here, we are creating a new input stream for reading. \n    io::idstream in{xport, \"/path/to/data_object\"};\n\n    if (!in) {\n        // Handle error.\n    }\n\n    std::array\u003cchar, 4_Mb\u003e buffer{}; // Buffer to hold data.\n\n    // This is the fastest way to write data into iRODS via the new stream API.\n    // Bytes read this way are stored directly in the buffer as is.\n    in.read(buffer.data(), buffer.size());\n\n    // Read a single character sequence into \"word\".\n    // This assumes the input stream contains a sequence of printable characters.\n    std::string word;\n    in \u003e\u003e word;\n\n    std::string line;\n    while (std::getline(in, line)) {\n        // Read every line of the input stream until eof.\n    }\n}\n```\n\n## iRODS Query Processor\nAvailable Since: v4.2.6\n\nDemonstrates how to use `irods::query_processor`.\n```c++\n#include \u003cirods/query_processor.hpp\u003e\n\n#include \u003ciostream\u003e\n#include \u003cvector\u003e\n#include \u003cmutex\u003e\n\nvoid process_all_query_results()\n{\n    // This will hold all data object absolute paths found by the query processor.\n    std::vector\u003cstd::string\u003e paths;\n\n    // Protects the paths vector from simultaneous updates.\n    std::mutex mtx;\n\n    using query_processor = irods::query_processor\u003crcComm_t\u003e;\n\n    // This is where we create our query processor. As you can see, we pass it\n    // the query string as well as the handler. The handler will process each row\n    // (i.e. std::vector\u003cstd::string\u003e) asynchronously. This means that it is your\n    // responsibility to protect shared data if necessary.\n    //\n    // In the following example, the handler creates a path from \"_row\" and stores\n    // it in the referenced \"paths\" container. Notice how a mutex is acquired before\n    // adding the path to the container.\n    query_processor qproc{\"select COLL_NAME, DATA_NAME\", [\u0026paths](const auto\u0026 _row) {\n        std::lock_guard lk{mtx};\n        paths.push_back(_row[0] + '/' + _row[1]);\n    }};\n\n    auto thread_pool = // Our iRODS thread pool.\n    auto conn = // Our iRODS connection.\n\n    // This is how we run the query. Notice how the execute call accepts a thread\n    // pool and connection. This allows developers to run queries on different\n    // thread pools.\n    //\n    // The object returned is a handle to a std::future containing error information.\n    // By doing this, the execution of the query and handling of its results are done\n    // asynchronously, therefore the application is not blocked from doing other work.\n    auto errors = qproc.execute(thread_pool, conn);\n\n    // Because the errors are returned via a std::future, calling \".get()\" will cause\n    // the application to wait until all query results have been processed by the\n    // handler provided on construction.\n    for (auto\u0026\u0026 error : errors.get()) {\n        // Handle errors.\n    }\n\n    // Print all the results.\n    for (auto\u0026\u0026 path : paths) {\n        std::cout \u003c\u003c \"path: \" \u003c\u003c path \u003c\u003c '\\n';\n    }\n}\n```\n\n## iRODS With Durability\nAvailable Since: v4.2.8\n\nDemonstrates how to use `irods::with_durability`.\n```c++\n#include \u003cirods/with_durability.hpp\u003e\n\n#include \u003cirods/connection_pool.hpp\u003e\n\nvoid get_collection_status_over_unreliabile_network()\n{\n    namespace ix = irods::experimental;\n    namespace fs = irods::experimental::filesystem;\n\n    // Holds the status of the collection.\n    fs::status s;\n\n    // This is where we define our rules for how durable we want a particular set of\n    // operations should be. Notice how we've set the number of retries and the delay\n    // multiplier. These options are completely optional. The last result will be\n    // returned to the call site. All intermediate results will be lost.\n    //\n    // The most important thing to understand about this function is the function-like\n    // object that will be invoked. It is up to the developer to instruct \"with_durability\"\n    // of when the set of operations have succeeded or failed, etc.\n    //\n    // See the following for more details:\n    //\n    //     https://github.com/irods/irods/blob/master/lib/core/include/with_durability.hpp\n    //\n    auto exec_result = ix::with_durability(ix::retries{5}, ix::delay_multiplier{2.f}, [\u0026] {\n        try {\n            auto conn_pool = irods::make_connection_pool();\n            s = fs::client::status(conn_pool-\u003eget_connection(), \"/tempZone/home/rods\");\n            return ix::execution_result::success;\n        }\n        catch (const fs::filesystem_error\u0026) {\n            return ix::execution_result::failure;\n        }\n\n        // Any exception that escapes the function-like object will be caught by the\n        // \"with_durability\" function.\n    });\n\n    // Here, we check the result of the operations and decide what should happen next. \n    if (ix::execution_result::success != exec_result) {\n        // Handle failure.\n    }\n\n    // Print whether the collection exists.\n    std::cout \u003c\u003c \"Status of collection: \" \u003c\u003c fs::client::exists(s) \u003c\u003c '\\n';\n}\n```\n\n## iRODS Key Value Proxy\nAvailable Since: v4.2.8\n\nDemonstrates how to use `irods::key_value_proxy`.\n```c++\n#include \u003cirods/key_value_proxy.hpp\u003e\n\n#include \u003cirods/dataObjOpen.h\u003e\n#include \u003cirods/stringOpr.h\u003e\n\n#include \u003cstring\u003e\n\nvoid manipulating_keyValuePair_t()\n{\n    // Let's open a replica for writing using the iRODS C API.\n    dataObjInp_t input{};\n    input.createMode = 0600;\n    input.openFlags = O_WRONLY;\n    rstrcpy(input.objPath, \"/tempZone/home/rods/foo\", MAX_NAME_LEN);\n\n    // Now, we need to set the options that target a specific replica.\n    // Normally we'd use the \"keyValuePair_t\" family of functions to add/remove\n    // options. Instead, we'll use the \"key_value_proxy\" class.\n    irods::experimental::key_value_proxy kvp{input.condInput};\n\n    // This proxy object does not own the underlying keyValuePair_t. It simply provides\n    // a map-like interface to manipulate and inspect it.\n\n    // Let's target a specific replica of this data object.\n    // This adds the \"REPL_NUM_KW\" key to the underlying keyValuePair_t and sets its\n    // value to the string \"2\". The string is copied into the object.\n    kvp[REPL_NUM_KW] = \"2\";\n\n    // This proxy type also supports checking if the kevValuePair_t contains a specific key.\n    if (kvp.contains(RESC_NAME_KW)) {\n        // Do something ...\n    }\n\n    // Extracting a value is easy too.\n    const std::string value = kvp[RESC_HIER_STR_KW];\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firods%2Firods_library_examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Firods%2Firods_library_examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firods%2Firods_library_examples/lists"}