{"id":20564329,"url":"https://github.com/tarantool/tntcxx","last_synced_at":"2025-09-01T06:41:06.215Z","repository":{"id":47708300,"uuid":"301188941","full_name":"tarantool/tntcxx","owner":"tarantool","description":"Tarantool C++ connector","archived":false,"fork":false,"pushed_at":"2025-04-07T12:46:45.000Z","size":1184,"stargazers_count":8,"open_issues_count":36,"forks_count":6,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-04-07T13:38:45.239Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tarantool.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-04T17:40:49.000Z","updated_at":"2025-04-07T12:46:49.000Z","dependencies_parsed_at":"2023-10-23T11:25:32.066Z","dependency_job_id":"d1044e06-427a-482e-8f7a-a7078416b41b","html_url":"https://github.com/tarantool/tntcxx","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftntcxx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftntcxx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftntcxx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftntcxx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tarantool","download_url":"https://codeload.github.com/tarantool/tntcxx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248904640,"owners_count":21180835,"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-16T04:25:42.732Z","updated_at":"2025-09-01T06:41:06.183Z","avatar_url":"https://github.com/tarantool.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tntcxx — Tarantool C++ Connector\n\nThis repository contains the tntcxx Tarantool C++ connector code. tntcxx is an\nopen-source Tarantool C++ connector (compliant to C++17) designed with high\nefficiency in mind.\n\n## Building tntcxx\n\n[CMake](https://cmake.org/) is the official build system for tntcxx.\n\n### CMake Build Instructions\n\ntntcxx comes with a CMake build script ([CMakeLists.txt](./CMakeLists.txt))\nthat can be used on a wide range of platforms (\"C\" stands for cross-platform.).\nIf you don't have CMake installed already, you can download it for free from\n\u003chttps://www.cmake.org/\u003e.\nCMake works by generating native makefiles or build projects that can\nbe used in the compiler environment of your choice.\nFor API/ABI compatibility reasons, we strongly recommend building tntcxx in a\nsubdirectory of your project or as an embedded dependency.\n\n#### Incorporating tntcxx Into a Cmake Project\n\n##### Step-by-Step Instructions\n\n1. Make tntcxx's source code available to the main build. This can be done a few\ndifferent ways:\n    * Download the tntcxx source code manually and place it at a known location.\n    This is the least flexible approach and can make it more difficult to use\n    with continuous integration systems, etc.\n    * Embed the tntcxx source code as a direct copy in the main project's source\n    tree. This is often the simplest approach, but is also the hardest to keep\n    up to date. Some organizations may not permit this method.\n    * Add tntcxx as a [git submodule](https://git-scm.com/docs/git-submodule) or\n    equivalent. This may not always be possible or appropriate. Git submodules,\n    for example, have their own set of advantages and drawbacks.\n    * Use the CMake [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html)\n    commands to download tntcxx as part of the build's configure step. This\n    approach doesn't have the limitations of the other methods.\n\nThe last of the above methods is implemented with a small piece of CMake code\nthat downloads and pulls the tntcxx code into the main build. Just add the\nfollowing snippet to your CMakeLists.txt:\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(\n  tntcxx\n  GIT_REPOSITORY https://github.com/tarantool/tntcxx.git\n)\nFetchContent_MakeAvailable(tntcxx)\n```\n\nAfter obtaining tntcxx sources using the rest of the methods, you can use the\nfollowing CMake command to incorporate tntcxx into your CMake project:\n```cmake\nadd_subdirectory(${TNTCXX_SOURCE_DIR})\n```\n\n2. Now simply link against the tntcxx::tntcxx target as needed:\n```cmake\nadd_executable(example example.cpp)\ntarget_link_libraries(example tntcxx::tntcxx)\n```\n\n##### Running tntcxx Tests with CMake\n\nUse the `-DTNTCXX_BUILD_TESTING=ON` option to run the tntcxx tests. This option\nis enabled by default if the tntcxx project is determined to be the top level\nproject. Note that `BUILD_TESTING` must also be on (the default).\n\nFor example, to run the tntcxx tests, you could use this script:\n```console\ncd path/to/tntcxx\nmkdir build\ncd build\ncmake -DTNTCXX_BUILD_TESTING=ON ..\nmake -j\nctest\n```\n\n### CMake Option Synopsis\n\n- `-DTNTCXX_BUILD_TESTING=ON` must be set to enable testing. This option is\nenabled by default if the tntcxx project is determined to be the top level\nproject.\n\n## Internals\n\nThere are three main parts of C++ connector: IO-zero-copy buffer, msgpack\nencoder/decoder and client handling requests itself.\n\n### Buffer\n\nBuffer is parameterized by allocator, which means that users are able to choose\nwhich allocator will be used to provide memory for buffer's blocks.\nData is orginized into linked list of blocks of fixed size which is specified\nas template parameter of buffer.\n\n### Client API\n\n**TODO: see src/Client/Connection.hpp and src/Client/Connector.hpp**\n\n## Usage\n\n### Embedding\n\nConnector can be embedded in any C++ application with including main header:\n`#include \"\u003cpath-to-cloned-repo\u003e/src/Client/Connector.hpp\"`\n\n### Objects Instantiation\n\nTo create client one should specify buffer's and network provider's implementations\nas template parameters. Connector's main class has the following signature:\n\n```c++\ntemplate\u003cclass BUFFER, class NetProvider = EpollNetProvider\u003cBUFFER\u003e\u003e\nclass Connector;\n```\n\nIf one don't want to bother with implementing its own buffer or network provider,\none can use default one: `tnt::Buffer\u003c16 * 1024\u003e` and\n`EpollNetProvider\u003ctnt::Buffer\u003c16 * 1024\u003e\u003e`.\nSo the default instantiation would look\nlike:\n```c++\nusing Buf_t = tnt::Buffer\u003c16 * 1024\u003e;\nusing Net_t = EpollNetProvider\u003cBuf_t \u003e;\nConnector\u003cBuf_t, Net_t\u003e client;\n```\n\nClient itself is not enough to work with Tarantool instances, so let's also create\nconnection objects. Connection takes buffer and network provider as template\nparameters as well (note that they must be the same as ones of client):\n```c++\nConnection\u003cBuf_t, Net_t\u003e conn(client);\n```\n\n### Connecting\n\nNow assume Tarantool instance is listening `3301` port on localhost. To connect\nto the server we should invoke `Connector::connect()` method of client object and\npass three arguments: connection instance, address and port.\n```c++\nint rc = client.connect(conn, address, port);\n```\n\n### Error Handling\n\nImplementation of connector is exception\nfree, so we rely on return codes: in case of fail, `connect()` will return `rc \u003c 0`.\nTo get error message corresponding to the last error happened during communication\nwith server, we can invoke `Connection::getError()` method:\n```c++\nif (rc != 0) {\n    std::cerr \u003c\u003c conn.getError() \u003c\u003c std::endl;\n}\n```\n\nNote that some methods can return `rc \u003c 0` without any error. For example, `wait`\ncan return `-1` when timeout is exceeded. In order to differentiate between such\nsituations, one can use method `Connection::hasError()`.\n\nTo reset connection after errors (clean up error message and connection status),\none can use `Connection::reset()`.\n\n### Preparing Requests\n\nTo execute simplest request (i.e. ping), one can invoke corresponding method of\nconnection object:\n```c++\nrid_t ping = conn.ping();\n```\nEach request method returns request id, which is sort of future. It can be used\nto get the result of request execution once it is ready (i.e. response). Requests\nare queued in the input buffer of connection until `Connector::wait()` is called.\n\n### Sending Requests\n\nThat said, to send requests to the server side, we should invoke `client.wait()`:\n```c++\nclient.wait(conn, ping, WAIT_TIMEOUT);\n```\nBasically, `wait()` takes connection to poll (both IN and OUT), request id and\noptionally timeout (in milliseconds) parameters. once response for specified\nrequest is ready, `wait()` terminates. It also provides negative return code in\ncase of system related fails (e.g. broken or time outed connection). If `wait()`\nreturns 0, then response is received and expected to be parsed.\n\n### Waiting for Responses\n\nThe connector provides several wait methods. All methods accept an integer `timeout`\nargument with the following semantics:\n* If `timeout \u003e 0`, the connector blocks for `timeout` **milliseconds** or until\n   all required responses are received. Time is measured against the monotonic clock.\n* If `timeout == 0`, the connector decodes all available responses and returns\n   immediately.\n* If `timeout == -1`, the connector blocks until required responses are received\n   (basically, no timeout).\n\nAll the waiting functions (except for `waitAny`, its description will be later)\nreturn `0` on success and `-1` in the case of any internal error (for example,\nwhen the underlying connection is closed) or when timeout is exceeded.\nSee [this section](#error-handling) for error handling details.\n\nMethod `wait` waits for one request:\n```c++\nint rc = client.wait(conn, ping, WAIT_TIMEOUT);\n```\nAn optional argument allows to obtain response right away in the case of success:\n```c++\nResponse\u003cBuf_t\u003e response;\nint rc = client.wait(conn, ping, WAIT_TIMEOUT, \u0026response);\n```\n\nMethod `waitAll` waits for completion of all the given requests of a connection:\n```c++\nstd::vector\u003crid_t\u003e futures{ping1, ping2, call, replace};\nint rc = client.waitAll(conn, futures, WAIT_TIMEOUT);\n```\n\nMethod `waitCount` waits until the given connection will complete any `future_count` requests:\n```c++\nint rc = client.waitCount(conn, future_count, WAIT_TIMEOUT);\n```\n\nMethod `waitAny` is different - it allows to poll all the connections simultaneously.\nIn the case of success, the function returns a connection that received a response.\nIn the case of internal error or when timeout is exceeded, returns `std::nullopt`.\nSee [this section](#error-handling) for error handling details.\n```c++\nstd::optional\u003cConnection\u003cBuf, NetProvider\u003e\u003e conn_ready = client.waitAny(WAIT_TIMEOUT);\n```\n\n### Receiving Responses\n\nTo get the response when it is ready, we can use `Connection::getResponse()`.\nIt takes request id and returns optional object containing response (`nullptr`\nin case response is not ready yet). Note that on each future it can be called\nonly once: `getResponse()` erases request id from internal map once it is\nreturned to user.\n\n```c++\nstd::optional\u003cResponse\u003cBuf_t\u003e\u003e response = conn.getResponse(ping);\n```\nResponse consists of header and body (`response.header` and `response.body`).\nDepending on success of request execution on server side, body may contain\neither runtime error(s) (accessible by `response.body.error_stack`) or data\n(tuples) (`response.body.data`). In turn, data is a vector of tuples. However,\ntuples are not decoded and come in form of pointers to the start and end of\nmsgpacks. See section below to understand how to decode tuples.\n\n### Data Manipulation\n\nNow let's consider a bit more sophisticated requests.\nAssume we have space with `id = 512` and following format on the server:\n`CREATE TABLE t(id INT PRIMARY KEY, a TEXT, b DOUBLE);`\nPreparing analogue of `t:replace(1, \"111\", 1.01);` request can be done this way:\n\n```c++\nstd::tuple data = std::make_tuple(1 /* field 1*/, \"111\" /* field 2*/, 1.01 /* field 3*/);\nrid_t replace = conn.space[512].replace(data);\n```\nTo execute select query `t.index[1]:select({1}, {limit = 1})`:\n\n```c++\nauto i = conn.space[512].index[1];\nrid_t select = i.select(std::make_tuple(1), 1, 0 /*offset*/, IteratorType::EQ);\n```\n\n### Data Readers\n\nResponses from server contain raw data (i.e. encoded into MsgPack tuples).\nLet's define structure describing data stored in space `t`:\n\n```c++\nstruct UserTuple {\n    uint64_t field1;\n    std::string field2;\n    double field3;\n\n    static constexpr auto mpp = std::make_tuple(\n        \u0026UserTuple::field1, \u0026UserTuple::field2, \u0026UserTuple::field3);\n};\n```\n\nMember `mpp` is neccessary - it sets the relationship between the structure\nmembers and associated tuple's fields. It is used by encoder and decoder\nfor Object \u003c-\u003e MsgPack serialization. For instance, such structure will be\nserialied as a MsgPack array `[\u003cfield1\u003e, \u003cfield2\u003e, \u003cfield3\u003e]`. If you need\nto serialize non-static members of objects,\n[pointers to data members](https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_data_members)\ncan be used, just as in this example.\n\nLet's get back to the example with `select`. Consider the request successful.\nWe can decode data in this way:\n\n```c++\nif (response.body.data != std::nullopt) {\n    std::vector\u003cUserTuple\u003e results;\n    bool ok = response.body.data-\u003edecode(results);\n    if (ok)\n        print_results(results);\n}\n```\n\nFirstly, we check if the response actually contains any data (Tarantool has\nsent `IPROTO_DATA` in response). According to\n[`IPROTO` protocol](https://www.tarantool.io/ru/doc/latest/reference/internals/box_protocol/),\nkey `IPROTO_DATA`\n[has](https://www.tarantool.io/ru/doc/latest/reference/internals/iproto/format/#body)\nan array of tuples as value in response to `select`. So, in order to\nsuccessfully decode them, we should pass an array of tuples to decoder - that's\nwhy `std::vector\u003cUserTuple\u003e` is needed. If decoding was successful, `results`\nwill contain all decoded `UserTuples`.\n\n## Multi-Threading Model\n\nA `Connector` object and all its instances of `Connection` objects must be used in a single thread. For multi-threaded usage, create one or several `Connector` instances for each thread. Each `Connection` object must be used only with the `Connector` object that it was created from.\n\nIf custom `Buffer` or `NetProvider` implementations are used for `Connector` objects, the custom implementations must not share any state (e.g., `static` fields).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool%2Ftntcxx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftarantool%2Ftntcxx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool%2Ftntcxx/lists"}