diff --git a/.clang-format b/.clang-format index b5da500..902d954 100644 --- a/.clang-format +++ b/.clang-format @@ -140,7 +140,7 @@ PackConstructorInitializers: BinPack PointerAlignment: Left QualifierAlignment: Leave ReferenceAlignment: Pointer -ReflowComments: true +ReflowComments: false RemoveBracesLLVM: false RemoveSemicolon: false RequiresClausePosition: OwnLine diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index e90faee..2cfd35c 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -7,4 +7,4 @@ runs: shell: bash run: | sudo apt-get update - sudo apt-get install -y libgmock-dev libgtest-dev nlohmann-json3-dev valgrind llvm gcovr + sudo apt-get install -y libgmock-dev libgtest-dev nlohmann-json3-dev valgrind llvm gcovr doxygen graphviz diff --git a/.github/workflows/apidocs.yml b/.github/workflows/apidocs.yml new file mode 100644 index 0000000..ad27354 --- /dev/null +++ b/.github/workflows/apidocs.yml @@ -0,0 +1,49 @@ +name: Deploy API Docs to Pages + +on: + push: + branches: + - master + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + name: Deploy + permissions: + contents: read + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Check out the source code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Install dependencies + uses: ./.github/actions/install-dependencies + + - name: Generate API Docs + run: | + cmake -B build + cmake --build build --target doxygen + + - name: Setup Pages + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 + + - name: Upload artifact + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + with: + path: apidocs + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.gitignore b/.gitignore index 45b4ead..4539c15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.cache/ /.vscode/ +/apidocs/ /build/ /build-*/ diff --git a/CMakeLists.txt b/CMakeLists.txt index c248695..e3eb91e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ set(EXPORT_COMPILE_COMMANDS ON) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) option(BUILD_TESTS "Build tests" ON) +option(BUILD_DOCS "Build documentation" ON) option(ENABLE_MAINTAINER_MODE "Enable maintainer mode" OFF) project( @@ -35,6 +36,31 @@ if(BUILD_TESTS) endif() endif() +if(BUILD_DOCS) + include(FindDoxygen) + find_package(Doxygen) + if(NOT DOXYGEN_FOUND) + message(WARNING "Doxygen not found, documentation will not be built") + set(BUILD_DOCS OFF) + else() + if(TARGET Doxygen::dot) + set(HAVE_DOT "YES") + else() + set(HAVE_DOT "NO") + endif() + configure_file("${CMAKE_SOURCE_DIR}/cmake/Doxyfile.in" "${CMAKE_BINARY_DIR}/Doxyfile" @ONLY) + add_custom_target( + doxygen + COMMAND Doxygen::doxygen "${CMAKE_BINARY_DIR}/Doxyfile" + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_BINARY_DIR}/docs/html/" "${CMAKE_SOURCE_DIR}/apidocs" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + DEPENDS ${PROJECT_NAME} + VERBATIM + ) + endif() +endif() + if(CMAKE_COMPILER_IS_GNU OR CMAKE_COMPILER_IS_CLANG) set(CMAKE_CXX_FLAGS_ASAN "-O1 -g -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") set(CMAKE_CXX_FLAGS_LSAN "-O1 -g -fsanitize=leak -fno-omit-frame-pointer -fno-optimize-sibling-calls") @@ -184,7 +210,7 @@ install( RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}" + FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/wwa/jsonrpc" ) install( diff --git a/README.md b/README.md new file mode 100644 index 0000000..24653c4 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# JSON RPC + +[![Build and Test](https://github.com/sjinks/jsonrpc-cpp/actions/workflows/ci.yml/badge.svg)](https://github.com/sjinks/jsonrpc-cpp/actions/workflows/ci.yml) +[![CodeQL](https://github.com/sjinks/jsonrpc-cpp/actions/workflows/codeql.yml/badge.svg)](https://github.com/sjinks/jsonrpc-cpp/actions/workflows/codeql.yml) + +JSON-RPC 2.0 library for C++ + +## Introduction + +This library provides a robust implementation of the JSON-RPC 2.0 protocol for C++. It allows you to easily handle JSON-RPC requests, and manage responses. + +## Features + +- Full support for JSON-RPC 2.0 +- Easy-to-use API +- Error handling and validation + +## Installation + +To install the library, follow these steps: + +1. Clone the repository: + ```sh + git clone https://github.com/sjinks/jsonrpc-cpp.git + cd jsonrpc-cpp + ``` + +2. Build the project using CMake: + ```sh + cmake -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build + ``` + +3. Install the library: + ```sh + cmake --install build # In Linux, you may have to use `sudo` + ``` + +## Usage + +```cpp +#include + +class my_server { +public: + my_server() + { + this->m_dispatcher.add("add", &my_server::add, this); + } + + void handle_request() + { + // Read the request somehow + const std::string input = read_request(); + + const std::string response = this->m_dispatcher->parse_and_process_request(input); + if (!response.empty()) { + // Send the response + send_response(response); + } + } + +private: + wwa::json_rpc::dispatcher m_dispatcher; + + int add(int a, int b) + { + return a + b; + } +}; +``` diff --git a/cmake/Doxyfile.in b/cmake/Doxyfile.in new file mode 100644 index 0000000..03a3d0f --- /dev/null +++ b/cmake/Doxyfile.in @@ -0,0 +1,274 @@ +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "JSON RPC" +PROJECT_NUMBER = +PROJECT_BRIEF = "JSON-RPC 2.0 library for C++" +PROJECT_LOGO = +OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/docs +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +JAVADOC_BANNER = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +PYTHON_DOCSTRING = YES +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 5 +MARKDOWN_ID_STYLE = GITHUB +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = YES +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +NUM_PROC_THREADS = 1 +TIMESTAMP = NO + +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_PRIV_VIRTUAL = YES +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = YES +RESOLVE_UNNAMED_PARAMS = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = YES +CASE_SENSE_NAMES = SYSTEM +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_HEADERFILE = YES +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = YES +SORT_MEMBERS_CTORS_1ST = YES +SORT_GROUP_NAMES = YES +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = + +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_IF_INCOMPLETE_DOC = YES +WARN_NO_PARAMDOC = YES +WARN_IF_UNDOC_ENUM_VAL = YES +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LINE_FORMAT = "at line $line of file $file" +WARN_LOGFILE = + +INPUT = @PROJECT_SOURCE_DIR@/src/ @PROJECT_SOURCE_DIR@/README.md +INPUT_ENCODING = UTF-8 +INPUT_FILE_ENCODING = +FILE_PATTERNS = *.cpp *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md +FORTRAN_COMMENT_AFTER = 72 + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = YES +CLANG_ADD_INC_PATHS = YES +CLANG_OPTIONS = +CLANG_DATABASE_PATH = @PROJECT_BINARY_DIR@ + +ALPHABETICAL_INDEX = YES +IGNORE_PREFIX = + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE = AUTO_LIGHT +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_DYNAMIC_MENUS = YES +HTML_DYNAMIC_SECTIONS = YES +HTML_CODE_FOLDING = YES +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_FEEDURL = +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher + +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +SITEMAP_URL = + +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = + +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project + +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +FULL_SIDEBAR = YES +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +OBFUSCATE_EMAILS = NO +HTML_FORMULA_FORMAT = png +FORMULA_FONTSIZE = 10 +FORMULA_MACROFILE = +USE_MATHJAX = NO +MATHJAX_VERSION = MathJax_2 +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = + +SEARCHENGINE = NO +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = + +GENERATE_LATEX = NO +GENERATE_RTF = NO +GENERATE_MAN = NO +GENERATE_XML = NO +GENERATE_DOCBOOK = NO +GENERATE_AUTOGEN_DEF = NO +GENERATE_SQLITE3 = NO +GENERATE_PERLMOD = NO + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES + +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = @HAVE_DOT@ +DOT_NUM_THREADS = 0 +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES + +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +DOT_UML_DETAILS = NO +DOT_WRAP_THRESHOLD = 17 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DIR_GRAPH_MAX_DEPTH = 1 +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +DIA_PATH = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +MSCGEN_TOOL = +MSCFILE_DIRS = diff --git a/src/dispatcher.cpp b/src/dispatcher.cpp index d75ec7b..829e60c 100644 --- a/src/dispatcher.cpp +++ b/src/dispatcher.cpp @@ -1,3 +1,8 @@ +/** + * @file + * @brief Implementation of the dispatcher class. + */ + #include "dispatcher.h" #include "dispatcher_p.h" #include "exception.h" @@ -13,11 +18,11 @@ void dispatcher::add_internal_method(std::string_view method, handler_t&& handle this->d_ptr->add_handler(std::string(method), std::move(handler)); } -std::string dispatcher::parse_and_process_request(const std::string& r) +std::string dispatcher::parse_and_process_request(const std::string& request) { - nlohmann::json request; + nlohmann::json req; try { - request = nlohmann::json::parse(r); + req = nlohmann::json::parse(request); } catch (const nlohmann::json::exception& e) { this->on_request(); @@ -29,7 +34,7 @@ std::string dispatcher::parse_and_process_request(const std::string& r) return json.dump(); } - return this->process_request(request); + return this->process_request(req); } std::string dispatcher::process_request(const nlohmann::json& request) diff --git a/src/dispatcher.h b/src/dispatcher.h index 63fc7eb..3446af8 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,6 +1,16 @@ #ifndef FAB131EA_3F90_43B6_833D_EB89DA373735 #define FAB131EA_3F90_43B6_833D_EB89DA373735 +/** + * @file dispatcher.h + * @brief Defines the JSON RPC dispatcher class. + * + * This file contains the definition of the `dispatcher` class, which is responsible for managing JSON RPC method handlers + * and processing JSON RPC requests. The dispatcher supports adding various types of handlers, including plain functions, + * static class methods, lambda functions, and member functions. These handlers can accept and return values that are + * convertible to and from `nlohmann::json` values. + */ + #include #include #include @@ -15,29 +25,159 @@ #include "export.h" #include "traits.h" +/** + * @brief Library namespace. + */ namespace wwa::json_rpc { class dispatcher_private; + +/** + * @brief A class that manages JSON RPC method handlers and processes JSON RPC requests. + * + * The dispatcher class allows adding method handlers for JSON RPC methods and processes JSON RPC requests. + * It supports adding plain functions, static class methods, lambda functions, and member functions as handlers. + * The handlers can accept and return values that can be converted to and from `nlohmann::json` values. + * + * @note The dispatcher class is non-copyable but movable. + * + * @details + * The dispatcher class provides the following functionalities: + * - Adding method handlers for JSON RPC methods. + * - Parsing and processing JSON RPC requests. + * - Invoking method handlers with the appropriate arguments. + * - Handling exceptions thrown by method handlers and returning appropriate JSON RPC error responses. + * + * The dispatcher class also provides virtual methods that can be overridden in derived classes to customize + * the behavior when a request is received, before a method handler is called, and after a method handler is called. + * + * @par Example Usage: + * ```cpp + * dispatcher d; + * d.add("subtract", [](const nlohmann::json& params) { + * int minuend = params["minuend"]; + * int subtrahend = params["subtrahend"]; + * return minuend - subtrahend; + * }); + * + * std::string request = R"({"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 1})"; + * std::string response = d.parse_and_process_request(request); + * ``` + * + * @par Adding Method Handlers: + * Method handlers can be added using the `add` method. The handler function can accept any number of arguments + * as long as they can be converted from a `nlohmann::json` value. The handler function can also return any type + * that can be converted to a `nlohmann::json` value. + * + * @par Handling Exceptions: + * If a handler function throws an exception derived from `std::exception`, the exception will be caught and + * an appropriate JSON RPC error response will be returned. + */ class WWA_JSONRPC_EXPORT dispatcher { private: friend class dispatcher_private; - using handler_t = std::function; + using handler_t = std::function; ///< Method handler type used internally. public: + /** @brief Class constructor. */ dispatcher(); + + /** @brief Class destructor. */ virtual ~dispatcher(); dispatcher(const dispatcher&) = delete; dispatcher& operator=(const dispatcher&) = delete; - dispatcher(dispatcher&&) = default; - dispatcher& operator=(dispatcher&&) = default; + /** + * @brief Move constructor. + * @param rhs Right-hand side object. + */ + dispatcher(dispatcher&& rhs) = default; + + /** + * @brief Move assignment operator. + * @param rhs Right-hand side object. + * @return Reference to this object. + */ + dispatcher& operator=(dispatcher&& rhs) = default; + + /** + * @brief Adds a method handler @a f for the method @a method. + * @tparam F Type of the handler function (@a f). + * @param method The name of the method to add the handler for. + * @param f The handler function. + * @details This overload is used to add a plain function, a static class method, or a lambda function as a handler. + * + * Internally, the handler is a function that accepts a `nlohmann::json` as its argument and returns a `nlohmann::json` value. + * However, the handler function can accept any number of arguments, as long as they + * [can be converted](https://github.com/nlohmann/json?tab=readme-ov-file#arbitrary-types-conversions) from a `nlohmann::json` value. + * The same is true for the return value: it can be any type that can be converted to a `nlohmann::json` value (or `void`). + * Of course, the handler can accept and/or return `nlohmann::json` values directly. + * + * @par Accepting Arguments in a Handler: + * The Specification [defines](https://www.jsonrpc.org/specification#parameter_structures) two types of parameters: *named* and *positional*. + * Since there is no easy way to match named parameters to the handler function arguments, the named parameters are treated as a single structured value. + * @par + * For example, if the parameters are passed like this: `"params": {"subtrahend": 23, "minuend": 42}`, the handler function must accept a single argument of a struct type: + * ```cpp + * struct subtract_params { + * int minuend; + * int subtrahend; + * }; + * + * int subtract(const subtract_params& params) + * { + * return params.minuend - params.subtrahend; + * } + * ``` + * @par + * Because there is no automatic conversion from a JSON object to `subtract_params`, you must define the conversion function yourself. + * The easiest way is to [use a macro](https://github.com/nlohmann/json?tab=readme-ov-file#simplify-your-life-with-macros): + * ```cpp + * NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(subtract_params, minuend, subtrahend); + * ``` + * @par + * In the case of positional parameters, the handler function must accept the same number of arguments as the number of parameters passed in the request. + * @par + * If the handler needs to accept a variable number of arguments, it must accept a single `nlohmann::json` argument and parse it as needed, like this: + * ```cpp + * this->m_dispatcher.add("sum", [](const nlohmann::json& params) { + * std::vector v; + * params.get_to(v); + * return std::accumulate(v.begin(), v.end(), 0); + * }); + * ``` + * + * @par Returning Values from a Handler: + * @li The handler can return any value as long as it can be converted to a `nlohmann::json` value. If there is no default conversion available, + * the handler can either return a `nlohmann::json` value directly, + * or use [a custom `to_json()` function](https://github.com/nlohmann/json?tab=readme-ov-file#arbitrary-types-conversions). + * @li If the handler function returns `void`, it will be automatically converted to `null` in the JSON response. + * + * @par Exception Handling: + * @note If the hander function throws an exception (derived from `std::exception`), the exception will be caught, and the error will be returned in the JSON response: + * @li json_rpc::exception will be converted to a JSON RPC error object using json_rpc::exception::to_json(); + * @li other exceptions derived from std::exception will be converted to a JSON RPC error object with code @a -32603 (exception::INTERNAL_ERROR) + * and the exception message ([what()](https://en.cppreference.com/w/cpp/error/exception/what)) as the error message. + */ template void add(std::string_view method, F&& f) { this->add(method, std::forward(f), nullptr); } + /** + * @brief Adds a method to the dispatcher with the specified instance and function. + * @tparam C The type of the class instance. + * @tparam F The type of the function to be added. + * @param method The name of the method to be added. + * @param f The function to be added, which will be bound to the instance. + * @param instance The instance of the class to which the function belongs. + * @overload + * @details This template method allows adding a method to the dispatcher by binding a member function + * of a class instance. It uses function traits to deduce the argument types and creates a closure + * that is then added to the internal method map. + */ template void add(std::string_view method, F&& f, C instance) { @@ -50,18 +190,121 @@ class WWA_JSONRPC_EXPORT dispatcher { this->add_internal_method(method, std::forward(closure)); } - std::string parse_and_process_request(const std::string& r); + /** + * @brief Parses and processes a JSON RPC request. + * + * @param request The JSON RPC request as a string. + * @return The response serialized into a JSON string. + * @retval "" If the request is a [Notification](https://www.jsonrpc.org/specification#notification), the method returns an empty string. + * + * @details This method performs the following steps: + * @li Parses the JSON RPC request. + * @li Passes the parsed JSON to the process_request() method. + * @li If the request is invalid, returns an appropriate error response. + * + * If the request cannot be parsed, the method returns a JSON RPC error response with code @a -32700 (exception::PARSE_ERROR). + * + * Exceptions derived from `std::exception` thrown by the handler function are caught and returned as JSON RPC error responses. + * @li `json_rpc::exception` will be converted to a JSON RPC error object using `json_rpc::exception::to_json()`. + * @li Other exceptions derived from `std::exception` will be converted to a JSON RPC error object with code `-32603` (exception::INTERNAL_ERROR) and the exception message as the error message. + */ + std::string parse_and_process_request(const std::string& request); + + /** + * @brief Processes a JSON RPC request. + * + * @param request The JSON RPC request as a `nlohmann::json` object. + * @return The response serialized into a JSON string. + * @retval "" If the request is a [Notification](https://www.jsonrpc.org/specification#notification), the method returns an empty string. + * + * @details This method performs the following steps: + * @li Parses the JSON RPC request. + * @li Passes the parsed JSON to the appropriate handler function. + * @li If the request is invalid, returns an appropriate error response. + * + * If the request cannot be parsed, the method returns a JSON RPC error response with code `-32700` (exception::PARSE_ERROR). + * + * Exceptions derived from `std::exception` thrown by the handler function are caught and returned as JSON RPC error responses: + * @li `json_rpc::exception` will be converted to a JSON RPC error object using `json_rpc::exception::to_json()`. + * @li Other exceptions derived from `std::exception` will be converted to a JSON RPC error object with code `-32603` (exception::INTERNAL_ERROR) and the exception message as the error message. + */ std::string process_request(const nlohmann::json& request); + /** + * @brief Invoked when a request is received. + * + * @details This method does nothing by default. It is intended to be overridden in a derived class. + * For example, it can be used to log requests or increment a counter. + * + * @note In the case of a valid [batch request](https://www.jsonrpc.org/specification#batch), + * this method is invoked for every request in the batch but **not** for the batch itself. + * However, if the batch request is invalid (e.g., is empty), this method is invoked once with an empty method name. + */ virtual void on_request(); + + /** + * @brief Invoked right before the method handler is called. + * + * @details This method does nothing by default. It is intended to be overridden in a derived class. For example, it can start a timer to measure the method execution time. + * + * @param method The name of the method to be called. + */ virtual void on_method(const std::string& method); + + /** + * @brief Invoked after the method handler is called. + * + * @details This method does nothing by default. It is intended to be overridden in a derived class. + * For example, it can be used to stop the timer started in on_method(). + * + * @param method The name of the called method. It can be empty for an invalid batch request. + * @param code The result code: 0 if the method was processed successfully, or an error code + * if an exception was thrown (e.g., exception::INTERNAL_ERROR) + * or the request could not be processed (e.g., exception::INVALID_PARAMS). + */ virtual void on_request_processed(const std::string& method, int code); private: + /** + * @brief Pointer to the implementation (Pimpl idiom). + * + * @details This unique pointer holds the private implementation details of the dispatcher class. + * It is used to hide the implementation details and reduce compilation dependencies. + */ std::unique_ptr d_ptr; + /** + * @brief Adds a method handler for the specified method. + * + * @param method The name of the method. + * @param handler The handler function. + * + * @details This method registers a handler function for a given method name. + * The handler function will be invoked when a request for the specified method is received. + */ void add_internal_method(std::string_view method, handler_t&& handler); + /** + * @brief Invokes a handler function with the provided arguments. + * + * @tparam F The type of the handler function. + * @tparam Tuple The type of the arguments tuple. + * @param f The handler function. + * @param tuple The arguments as a tuple. + * @return The result of the handler function converted to a JSON value. + * @retval nlohmann::json::null_t If the handler function returns void. + * + * @details This method invokes the handler function with the arguments passed as a tuple. + * It uses the `if constexpr` construct to handle the case when the handler function returns void. + * + * The `if constexpr` construct allows the method to determine at compile time whether + * the handler function returns void. If the return type is void, + * the method calls the handler function and returns a JSON null value. + * If the return type is not void, the method calls the handler function + * and returns the result converted to a JSON value. + * + * The `std::apply` function is used to unpack the tuple and pass the arguments to the handler function. + */ template nlohmann::json invoke_function(F&& f, Tuple&& tuple) const { @@ -77,6 +320,34 @@ class WWA_JSONRPC_EXPORT dispatcher { } } + /** + * @brief Creates a closure for invoking a member function with JSON parameters. + * + * @tparam C The type of the class instance (can be a pointer or null pointer). + * @tparam F The type of the member function (if C is not `std::nullptr_t`) or the function. + * @tparam Args The type of the arguments tuple. + * @tparam Indices The indices of the arguments in the tuple. + * + * @param inst The instance of the class (can be a pointer or null pointer). + * @param f The member function to be invoked. + * + * @return A lambda function that takes a JSON object as a parameter and invokes the member function with the appropriate arguments. + * + * @details This method creates a closure (lambda function) that can be used to invoke a member function with arguments extracted from a JSON object. + * + * The closure performs the following steps: + * 1. Checks if the JSON object is an array. + * 2. If the JSON object is an array and the member function takes a single argument of type `nlohmann::json`, it directly passes the JSON object to the member function. + * 3. If the JSON object is an array and the number of elements matches the number of arguments expected by the member function, it extracts the arguments from the JSON array and invokes the member function. + * 4. If the JSON object is not an array or the number of elements does not match the number of arguments, it throws a json_rpc::exception with the `exception::INVALID_PARAMS` code. + * + * The `invoke_function` method is used to invoke the member function with the extracted arguments. + * + * The `std::apply` function is used to unpack the tuple and pass the arguments to the member function. + * + * Compile-time checks ensure that the code is type-safe and that certain conditions are met before the code is compiled. + * This helps catch potential errors early in the development process and improves the overall robustness of the code. + */ template constexpr auto create_closure(C inst, F&& f, std::index_sequence) // NOLINT(readability-function-cognitive-complexity) diff --git a/src/dispatcher_p.cpp b/src/dispatcher_p.cpp index eabf933..7ded7db 100644 --- a/src/dispatcher_p.cpp +++ b/src/dispatcher_p.cpp @@ -2,8 +2,28 @@ #include "dispatcher.h" #include "exception.h" +/** + * @file + * @brief Implementation of the private members and methods of the JSON RPC dispatcher class. + * + * This file contains the definitions of the private members and methods used by the `dispatcher` class to manage method handlers and process requests. + * It includes functions for parsing, validating, and invoking JSON RPC requests, as well as generating error responses. + */ + namespace { +/** + * @brief Checks if the provided JSON value is a valid JSON RPC request ID. + * + * @param id The JSON value to check. + * @return `true` if the JSON value is a valid request ID, `false` otherwise. + * + * @details This function checks if the provided JSON value is a valid JSON RPC request ID. + * According to the JSON RPC specification, a valid request ID can be a string, a number, or null. + * Additionally, this function also considers a discarded JSON value as valid. + * + * @see https://www.jsonrpc.org/specification#request_object + */ bool is_valid_request_id(const nlohmann::json& id) { return id.is_string() || id.is_number() || id.is_null() || id.is_discarded(); @@ -13,7 +33,23 @@ bool is_valid_request_id(const nlohmann::json& id) namespace wwa::json_rpc { -// NOLINTNEXTLINE(misc-use-anonymous-namespace) -- cannot move to an anonymous namespace because of ADL +/** + * @brief Deserializes a JSON object into a `jsonrpc_request` structure. + * + * @param j The JSON object to deserialize. + * @param r The `jsonrpc_request` structure to populate. + * + * @details This function deserializes a JSON object into a `jsonrpc_request` structure. + * * It extracts the `jsonrpc` version, `method` name, `params`, and `id` from the JSON object. + * * If the `params` field is not present, it defaults to an empty array. + * * If the `params` field is an object, it is wrapped in an array. + * + * @note This function cannot be moved to an anonymous namespace because of Argument-Dependent Lookup (ADL). + * + * @see https://www.jsonrpc.org/specification#request_object + * @see https://github.com/nlohmann/json?tab=readme-ov-file#arbitrary-types-conversions + */ +// NOLINT(misc-use-anonymous-namespace) -- cannot move to an anonymous namespace because of ADL static void from_json(const nlohmann::json& j, jsonrpc_request& r) { r.params = nlohmann::json(nlohmann::json::value_t::discarded); diff --git a/src/dispatcher_p.h b/src/dispatcher_p.h index ab44cdf..207e29f 100644 --- a/src/dispatcher_p.h +++ b/src/dispatcher_p.h @@ -1,21 +1,35 @@ #ifndef FB656817_7041_48D5_80B2_347168163158 #define FB656817_7041_48D5_80B2_347168163158 +/** + * @file + * @brief Contains the private implementation details of the JSON RPC dispatcher class. + */ + #include #include #include #include #include "dispatcher.h" -struct jsonrpc_request; - namespace wwa::json_rpc { class exception; +/** + * @brief Custom hasher for `std::string_view`. + * + * This struct provides a custom hash function for `std::string_view` to be used in unordered containers. + */ struct hasher { - using is_transparent = void; + using is_transparent = void; ///< Indicates that the hasher supports transparent key lookup. + /** + * @brief Computes the hash value for a given `std::string_view`. + * + * @param s The `std::string_view` to hash. + * @return The hash value. + */ std::size_t operator()(std::string_view s) const noexcept { const std::hash h; @@ -23,26 +37,104 @@ struct hasher { } }; +/** + * @brief Represents a JSON RPC request. + * + * This struct holds the components of a JSON RPC request, including the JSON RPC version, method name, parameters, and ID. + * + * @see https://www.jsonrpc.org/specification#request_object + */ struct jsonrpc_request { - std::string jsonrpc; - std::string method; - nlohmann::json params; - nlohmann::json id; + std::string jsonrpc; ///< The JSON RPC version. + std::string method; ///< The name of the method to be invoked. + nlohmann::json params; ///< The parameters for the method. + nlohmann::json id; ///< The ID of the request. }; +/** + * @brief Private implementation of the JSON RPC dispatcher class. + * + * This class contains the private members and methods used by the `dispatcher` class to manage method handlers and process requests. + */ class dispatcher_private { public: + /** + * @brief Constructs a new `dispatcher_private` object. + * + * @param q Pointer to the public `dispatcher` object. + */ explicit dispatcher_private(dispatcher* q) : q_ptr(q) {} + + /** + * @brief Adds a method handler. + * + * @param method The name of the method. + * @param handler The handler function. + * + * @details This method registers a handler function for a given method name. + * The handler function will be invoked when a request for the specified method is received. + */ void add_handler(std::string&& method, dispatcher::handler_t&& handler); + + /** + * @brief Processes a JSON RPC request. + * + * @param request The JSON RPC request as a `nlohmann::json` object. + * @return The response serialized into a JSON object. + * + * @details This method parses the JSON RPC request, validates it, and invokes the appropriate method handler. + */ nlohmann::json process_request(const nlohmann::json& request); + + /** + * @brief Generates an error response. + * + * @param e The exception containing the error details. + * @param id The ID of the request. + * @return The error response serialized into a JSON object. + * + * @details This method creates a JSON RPC error response based on the provided exception and request ID. + * @see exception::to_json() + */ static nlohmann::json generate_error_response(const exception& e, const nlohmann::json& id); private: - dispatcher* q_ptr; + dispatcher* q_ptr; ///< Pointer to the public `dispatcher` object. + /** @brief Map of method names to handler functions. */ std::unordered_map> m_methods; + /** + * @brief Parses a JSON RPC request. + * + * @param request The JSON RPC request as a `nlohmann::json` object. + * @return The parsed JSON RPC request. + * + * @details This method extracts the components of a JSON RPC request from the provided JSON object. + */ static jsonrpc_request parse_request(const nlohmann::json& request); + + /** + * @brief Validates a JSON RPC request. + * + * @param r The JSON RPC request to validate. + * + * @details This method checks the validity of the JSON RPC request, ensuring that all required fields are present and correctly formatted. + * @throws exception If the request is invalid. + * @see exception::INVALID_REQUEST, exception::INVALID_PARAMS + */ static void validate_request(const jsonrpc_request& r); + + /** + * @brief Invokes a method handler. + * + * @param method The name of the method to invoke. + * @param params The parameters for the method. + * @return The result of the method invocation serialized into a JSON object. + * + * @details This method finds the handler for the specified method and invokes it with the provided parameters. + * @throws exception If the method is not found or the invocation fails. + * @see exception::METHOD_NOT_FOUND + */ nlohmann::json invoke(const std::string& method, const nlohmann::json& params); }; diff --git a/src/exception.cpp b/src/exception.cpp index d52e720..5b987aa 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -1,3 +1,11 @@ +/** + * @file + * @brief Implementation of the JSON RPC exception class. + * + * This file contains the implementation of the destructor for the `exception` class. + * The destructor is defined here to avoid vtables in every translation unit that includes the header. + */ + #include "exception.h" wwa::json_rpc::exception::~exception() = default; diff --git a/src/exception.h b/src/exception.h index ab48048..f96ceab 100644 --- a/src/exception.h +++ b/src/exception.h @@ -1,6 +1,12 @@ #ifndef CC75354D_5C03_4B34_B773_96A9E6189611 #define CC75354D_5C03_4B34_B773_96A9E6189611 +/** + * @file exception.h + * @brief Contains the definition of the JSON RPC Exception class. + * @see https://www.jsonrpc.org/specification#error_object + */ + #include #include #include @@ -10,41 +16,195 @@ namespace wwa::json_rpc { -static constexpr std::string_view err_not_jsonrpc_2_0_request = "Not a JSON-RPC 2.0 request"; +/** + * @defgroup error_message Error Messages + * @brief Error messages used by the library. These constants can be useful in unit tests. + * @{ + */ + +/** + * @brief Error message for when the request is not a JSON-RPC 2.0 request. + * @see exception::INVALID_REQUEST + * @see https://www.jsonrpc.org/specification#request_object + * @details > `jsonrpc`: A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". + */ +static constexpr std::string_view err_not_jsonrpc_2_0_request = "Not a JSON-RPC 2.0 request"; + +/** + * @brief Error message for when the parameters passed to the method are not correct. + * @see exception::INVALID_PARAMS + */ static constexpr std::string_view err_invalid_params_passed_to_method = "Invalid parameters passed to method"; -static constexpr std::string_view err_method_not_found = "Method not found"; -static constexpr std::string_view err_empty_method = "Method cannot be empty"; + +/** + * @brief Error message for when the method is not found. + * @see exception::METHOD_NOT_FOUND + */ +static constexpr std::string_view err_method_not_found = "Method not found"; + +/** + * @brief Error message for when the method is empty. + * @see exception::INVALID_REQUEST + */ +static constexpr std::string_view err_empty_method = "Method cannot be empty"; + +/** + * @brief Error message for when the parameters are not an array or an object. + * @see exception::INVALID_PARAMS + */ static constexpr std::string_view err_bad_params_type = "Parameters must be either an array or an object or omitted"; -static constexpr std::string_view err_bad_id_type = "ID must be either a number, a string, or null"; -static constexpr std::string_view err_empty_batch = "Empty batch request"; +/** + * @brief Error message for when the ID is not a number, a string, or null. + * @see exception::INVALID_REQUEST + * @see https://www.jsonrpc.org/specification#request_object + * @details > An identifier established by the Client that MUST contain a String, Number, or NULL value if included. + */ +static constexpr std::string_view err_bad_id_type = "ID must be either a number, a string, or null"; + +/** + * @brief Error message for when the batch request is empty. + * @see exception::INVALID_REQUEST + * @see https://www.jsonrpc.org/specification#batch + */ +static constexpr std::string_view err_empty_batch = "Empty batch request"; +/** @} */ + +/** + * @brief JSON RPC Exception class. + * + * This class represents an exception that can occur during the processing of JSON RPC requests. + * It includes an error code, a message, and optional additional data. + */ class WWA_JSONRPC_EXPORT exception : public std::exception { public: - static constexpr int PARSE_ERROR = -32700; - static constexpr int INVALID_REQUEST = -32600; + /** + * @defgroup error_codes Error Codes + * @brief Error codes defined by the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#request_object). + * @see https://www.jsonrpc.org/specification#error_object + * @{ + */ + + /** + * @brief Invalid JSON was received by the server. + * + * An error occurred on the server while parsing the JSON text. + */ + static constexpr int PARSE_ERROR = -32700; + + /** + * @brief The JSON sent is not a valid Request object. + */ + static constexpr int INVALID_REQUEST = -32600; + + /** + * @brief The method does not exist or is not available. + */ static constexpr int METHOD_NOT_FOUND = -32601; - static constexpr int INVALID_PARAMS = -32602; - static constexpr int INTERNAL_ERROR = -32603; + /** + * @brief Invalid method parameter(s). + */ + static constexpr int INVALID_PARAMS = -32602; + + /** + * @brief Internal JSON-RPC error. + */ + static constexpr int INTERNAL_ERROR = -32603; + /** @} */ + + /** + * @brief Construct a new exception object with additional data. + * + * @tparam T Type of the @a data. Must be [convertible to `nlohmann::json`](https://github.com/nlohmann/json?tab=readme-ov-file#arbitrary-types-conversions). + * @param code Indicates the error type that occurred. + * @param message Provides a short description of the error. The message SHOULD be limited to a concise single sentence. + * @param data Additional information about the error. + * + * @see https://www.jsonrpc.org/specification#error_object + */ template exception(int code, std::string_view message, const T& data) : m_message(message), m_data(data), m_code(code) {} + /** + * @brief Construct a new exception object. + * + * @param code Indicates the error type that occurred. + * @param message Provides a short description of the error. The message SHOULD be limited to a concise single sentence. + * + * @see https://www.jsonrpc.org/specification#error_object + */ exception(int code, std::string_view message) : m_message(message), m_code(code) {} - exception(const exception&) = default; - exception(exception&&) = default; - exception& operator=(const exception&) = default; - exception& operator=(exception&&) = default; - + /** + * @brief Default copy constructor. + */ + exception(const exception&) = default; + + /** + * @brief Default move constructor. + */ + exception(exception&&) = default; + + /** + * @brief Default copy assignment operator. + * + * @param rhs Right-hand side of the assignment. + * @return Reference to this object. + */ + exception& operator=(const exception& rhs) = default; + + /** + * @brief Default move assignment operator. + * + * @param rhs Right-hand side of the assignment. + * @return Reference to this object. + */ + exception& operator=(exception&& rhs) = default; + + /** + * @brief Default destructor + */ ~exception() override; + /** + * @brief Returns the error code. + * + * @return Error code. + */ [[nodiscard]] int code() const noexcept { return this->m_code; } + + /** + * @brief Returns the error message. + * + * @return Error message. + */ [[nodiscard]] const std::string& message() const noexcept { return this->m_message; } + + /** + * @brief Returns custom data associated with the error. + * + * @return Custom data in JSON format. + */ [[nodiscard]] const nlohmann::json& data() const noexcept { return this->m_data; } + /** + * @brief Returns the error message. + * + * @see https://en.cppreference.com/w/cpp/error/exception/what + * @return Pointer to a null-terminated string with explanatory information. + * + * @see message() + */ [[nodiscard]] const char* what() const noexcept override { return this->m_message.c_str(); } + /** + * @brief Returns the error message as an Error Object. + * + * @see https://www.jsonrpc.org/specification#error_object + * @return Error Object as JSON. + */ [[nodiscard]] nlohmann::json to_json() const { nlohmann::json j{ @@ -60,9 +220,9 @@ class WWA_JSONRPC_EXPORT exception : public std::exception { } private: - std::string m_message; - nlohmann::json m_data; - int m_code; + std::string m_message; ///< Error message. + nlohmann::json m_data; ///< Custom data associated with the error. + int m_code; ///< Error code. }; } // namespace wwa::json_rpc diff --git a/src/export.h b/src/export.h index 59bb112..5525202 100644 --- a/src/export.h +++ b/src/export.h @@ -1,6 +1,25 @@ #ifndef E66C1505_D447_4384_BB35_55B23FF31F0A #define E66C1505_D447_4384_BB35_55B23FF31F0A +/** + * @file + * @brief Defines macros for exporting and importing symbols in the JSON RPC library. + * + * This file contains macros that control the visibility of symbols in the JSON RPC library. + * It handles the differences between static and dynamic linking, as well as platform-specific + * visibility attributes for Windows and Unix-like systems. + */ + +/** + * @def WWA_JSONRPC_EXPORT + * @brief Macro for exporting symbols when building the library dynamically or importing symbols when using the library dynamically. + */ + +/** + * @def WWA_JSONRPC_NO_EXPORT + * @brief Macro for hiding symbols. + */ + #ifdef WWA_JSONRPC_STATIC_DEFINE # define WWA_JSONRPC_EXPORT # define WWA_JSONRPC_NO_EXPORT diff --git a/src/traits.h b/src/traits.h index 05a12b8..a9f7c4f 100644 --- a/src/traits.h +++ b/src/traits.h @@ -1,65 +1,169 @@ #ifndef DE443A53_EEA9_4918_BCFB_AE76A19FB197 #define DE443A53_EEA9_4918_BCFB_AE76A19FB197 +/** + * @file traits.h + * @brief Contains type traits for function handling in the JSON RPC library. + * @internal + */ + #include #include +/** + * @brief Contains the implementation details of the JSON RPC library. + * @internal + */ namespace wwa::json_rpc::details { +/** + * @addtogroup function_traits Function Traits + * @brief Type traits for extracting function return types and argument types. + * @internal + * @{ + */ + +/** + * @brief Primary template for function traits. + * + * This template is specialized for various function types to extract the return type and argument types. + * + * @tparam T The function type. + */ template struct function_traits; +/** + * @brief Specialization for function pointers. + * + * @tparam R The return type of the function. + * @tparam Args The argument types of the function. + */ template struct function_traits { - using return_type = R; - using args_tuple = std::tuple; + using return_type = R; ///< The return type of the function. + using args_tuple = std::tuple; ///< A tuple of the argument types. }; +/** + * @brief Specialization for noexcept function pointers. + * + * @tparam R The return type of the function. + * @tparam Args The argument types of the function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits { - using return_type = R; - using args_tuple = std::tuple; + using return_type = R; ///< The return type of the member function. + using args_tuple = std::tuple; ///< A tuple of the argument types. }; +/** + * @brief Specialization for const member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for volatile member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for const volatile member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for noexcept member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for const noexcept member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for volatile noexcept member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for const volatile noexcept member function pointers. + * + * @tparam R The return type of the member function. + * @tparam C The class type. + * @tparam Args The argument types of the member function. + */ template struct function_traits : function_traits {}; +/** + * @brief Specialization for `std::function`. + * + * @tparam R The return type of the function. + * @tparam Args The argument types of the function. + */ template struct function_traits> { - using return_type = R; - using args_tuple = std::tuple; + using return_type = R; ///< The return type of the function. + using args_tuple = std::tuple; ///< A tuple of the argument types. }; +/** + * @brief Specialization for functors (objects with `operator()`). + * + * @tparam T The functor type. + */ template struct function_traits { private: - using call_type = function_traits; + using call_type = function_traits; ///< Type traits for the call operator. public: - using return_type = typename call_type::return_type; - using args_tuple = typename call_type::args_tuple; + using return_type = typename call_type::return_type; ///< The return type of the functor. + using args_tuple = typename call_type::args_tuple; ///< A tuple of the argument types. }; +/** @} */ } // namespace wwa::json_rpc::details