From 52056862e104be2b01f341a79f80bb50bb56fbf1 Mon Sep 17 00:00:00 2001 From: ykiko Date: Fri, 5 Jul 2024 23:32:24 +0800 Subject: [PATCH] basic implementation of protocol and serialization. --- include/LSP/Protocol.h | 608 +++++++++++++++++++++++++++++++++++ include/Support/Reflection.h | 190 +++++++++++ include/Support/Serialize.h | 54 ++++ 3 files changed, 852 insertions(+) create mode 100644 include/LSP/Protocol.h create mode 100644 include/Support/Reflection.h create mode 100644 include/Support/Serialize.h diff --git a/include/LSP/Protocol.h b/include/LSP/Protocol.h new file mode 100644 index 00000000..44ae001e --- /dev/null +++ b/include/LSP/Protocol.h @@ -0,0 +1,608 @@ +#include +#include +#include +#include + +namespace clice { + +// Defined by JSON RPC. +enum class ErrorCode { + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + + ServerNotInitialized = -32002, + UnknownErrorCode = -32001, + + // Defined by the protocol. + RequestCancelled = -32800, + ContentModified = -32801, +}; + +struct ResourceOperationKind { + /// Supports creating new files and folders. + constexpr inline static std::string_view Create = "create"; + + /// Supports renaming existing files and folders. + constexpr inline static std::string_view Rename = "rename"; + + /// Supports deleting existing files and folders. + constexpr inline static std::string_view Delete = "delete"; +}; + +struct FailureHandlingKind { + /// Applying the workspace change is simply aborted if one of the changes + /// provided fails. All operations executed before the failing operation stay + /// executed. + constexpr inline static std::string_view Abort = "abort"; + + /// All operations are executed transactionally. That is they either all + /// succeed or no changes at all are applied to the workspace. + constexpr inline static std::string_view Transactional = "transactional"; + + /// If the workspace edit contains only textual file changes they are executed + /// transactionally. If resource changes (create, rename or delete file) are + /// part of the change the failure handling strategy is abort. + constexpr inline static std::string_view TextOnlyTransactional = "textOnlyTransactional"; + + /// The client tries to undo the operations already executed. But there is no + /// guarantee that this is succeeding. + constexpr inline static std::string_view Undo = "undo"; +}; + +/// A symbol kind. +enum class SymbolKind { + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, + Object = 19, + Key = 20, + Null = 21, + EnumMember = 22, + Struct = 23, + Event = 24, + Operator = 25, + TypeParameter = 26, +}; + +struct WorkspaceEditClientCapabilities { + /// The client supports versioned document changes in `WorkspaceEdit`s + std::optional documentChanges; + + /// The resource operations the client supports. Clients should at least + /// support 'create', 'rename' and 'delete' files and folders. + /// possible values are in ResourceOperationKind + std::optional> resourceOperations; + + /// The failure handling strategy of a client if applying the workspace edit + /// fails. + /// possible values are in FailureHandlingKind + std::optional failureHandling; + + /// Whether the client normalizes line endings to the client specific + /// setting. + /// If set to `true` the client will normalize line ending characters + /// in a workspace edit to the client specific new line character(s). + std::optional normalizesLineEndings; + + struct ChangeAnnotationSupport { + /// Whether the client groups edits with equal labels into tree nodes, + /// for instance all edits labelled with "Changes in Strings" would + /// be a tree node. + std::optional groupsOnLabel; + }; + + /// Whether the client in general supports change annotations on text edits, + /// create file, rename file and delete file changes. + std::optional changeAnnotationSupport; +}; + +struct DidChangeConfigurationClientCapabilities { + /// Did change configuration notification supports dynamic registration. + std::optional dynamicRegistration; +}; + +struct DidChangeWatchedFilesClientCapabilities { + /// Did change watched files notification supports dynamic registration. + /// Please note that the current protocol doesn't support static + /// configuration for file changes from the server side. + std::optional dynamicRegistration; + + /// Whether the client has support for relative patterns or not. + std::optional supportsRelativePattern; +}; + +struct WorkspaceSymbolClientCapabilities { + /// Symbol request supports dynamic registration. + std::optional dynamicRegistration; + + struct SymbolKind_ { + /// The symbol kind values the client supports. When this + /// property exists the client also guarantees that it will + /// handle values outside its set gracefully and falls back + /// to a default value when unknown. + /// + /// If this property is not present the client only supports + /// the symbol kinds from `File` to `Array` as defined in + /// the initial version of the protocol. + std::vector valueSet; + }; + + /// Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. + std::optional symbolKind; + + struct TagSupport { + /// The tags supported by the client. + std::vector valueSet; + }; + + /// The client supports tags on `SymbolInformation` and `WorkspaceSymbol`. + /// Clients supporting tags have to handle unknown tags gracefully. + std::optional tagSupport; + + struct ResolveSupport { + /// The properties that a client can resolve lazily. Usually `location.range`. + std::vector properties; + }; + + /// The client support partial workspace symbols. The client will send the + /// request `workspaceSymbol/resolve` to the server to resolve additional + /// properties. + std::optional resolveSupport; +}; + +struct ExecuteCommandClientCapabilities { + /// Execute command supports dynamic registration. + std::optional dynamicRegistration; +}; + +struct SemanticTokensWorkspaceClientCapabilities { + /// Whether the client implementation supports a refresh request sent from + /// the server to the client. + /// + /// Note that this event is global and will force the client to refresh all + /// semantic tokens currently shown. It should be used with absolute care + /// and is useful for situation where a server for example detect a project + /// wide change that requires such a calculation. + std::optional refreshSupport; +}; + +struct CodeLensWorkspaceClientCapabilities { + /** + * Whether the client implementation supports a refresh request sent from the + * server to the client. + * + * Note that this event is global and will force the client to refresh all + * code lenses currently shown. It should be used with absolute care and is + * useful for situation where a server for example detect a project wide + * change that requires such a calculation. + */ + std::optional refreshSupport; +}; + +/// Client workspace capabilities specific to inline values. +struct InlineValueWorkspaceClientCapabilities { + /// Whether the client implementation supports a refresh request sent from + /// the server to the client. + /// + /// Note that this event is global and will force the client to refresh all + /// inline values currently shown. It should be used with absolute care and + /// is useful for situation where a server for example detect a project wide + /// change that requires such a calculation. + std::optional refreshSupport; +}; + +/// Client workspace capabilities specific to inlay hints. +struct InlayHintWorkspaceClientCapabilities { + /// Whether the client implementation supports a refresh request sent from + /// the server to the client. + /// + /// Note that this event is global and will force the client to refresh all + /// inlay hints currently shown. It should be used with absolute care and + /// is useful for situation where a server for example detects a project wide + /// change that requires such a calculation. + std::optional refreshSupport; +}; + +/// Workspace client capabilities specific to diagnostic pull requests. +struct DiagnosticWorkspaceClientCapabilities { + /// Whether the client implementation supports a refresh request sent from + /// the server to the client. + /// + /// Note that this event is global and will force the client to refresh all + /// pulled diagnostics currently shown. It should be used with absolute care + /// and is useful for situation where a server for example detects a project + /// wide change that requires such a calculation. + std::optional refreshSupport; +}; + +struct ClientCapabilities { + struct Workplace { + /// The client supports applying batch edits + /// to the workspace by supporting the request + /// 'workspace/applyEdit' + std::optional applyEdit; + + /// Capabilities specific to `WorkspaceEdit`s + std::optional workspaceEdit; + + /// Capabilities specific to the `workspace/didChangeConfiguration` notification. + std::optional didChangeConfiguration; + + /// Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + std::optional didChangeWatchedFiles; + + /// Capabilities specific to the `workspace/symbol` request. + std::optional symbol; + + /// Capabilities specific to the `workspace/executeCommand` request. + std::optional executeCommand; + + /// The client has support for workspace folders. + std::optional workspaceFolders; + + /// The client supports `workspace/configuration` requests. + std::optional configuration; + + /// Capabilities specific to the semantic token requests scoped to the workspace. + std::optional semanticTokens; + + /// Capabilities specific to the code lens requests scoped to the workspace. + std::optional codeLens; + + struct FileOperations { + /// Whether the client supports dynamic registration for file requests/notifications. + std::optional dynamicRegistration; + + /// The client has support for sending didCreateFiles notifications. + std::optional didCreate; + + /// The client has support for sending willCreateFiles requests. + std::optional willCreate; + + /// The client has support for sending didRenameFiles notifications. + std::optional didRename; + + /// The client has support for sending willRenameFiles requests. + std::optional willRename; + + /// The client has support for sending didDeleteFiles notifications. + std::optional didDelete; + + /// The client has support for sending willDeleteFiles requests. + std::optional willDelete; + }; + + /// The client has support for file requests/notifications. + std::optional fileOperations; + + /// Client workspace capabilities specific to inline values. + std::optional inlineValue; + + /// Client workspace capabilities specific to inlay hints. + std::optional inlayHint; + + /// Client workspace capabilities specific to diagnostics. + std::optional diagnostic; + }; + + /// Workspace specific client capabilities. + std::optional workspace; + + /// Text document specific client capabilities. + /// TODO: textDocument?: TextDocumentClientCapabilities; + + /// Capabilities specific to the notebook document support. + /// TODO: notebookDocument?: NotebookDocumentClientCapabilities; + + /// Window specific client capabilities. + /// TODO: window: {...} + + /// General client capabilities. + /// TODO: general: {...} + + /// Experimental client capabilities. + /// experimental?: LSPAny; +}; + +/// TODO: +struct URI {}; + +struct WorkspaceFolder { + /// The associated URI for this workspace folder. + URI uri; + + /// The name of the workspace folder. Used to refer to this + /// workspace folder in the user interface. + std::string name; +}; + +struct InitializeParams { + /// The process Id of the parent process that started the server. Is null if + /// the process has not been started by another process. If the parent + /// process is not alive then the server should exit (see exit notification) + /// its process. + std::optional processId; + + struct ClientInfo { + std::string_view name; + std::optional version; + }; + + /// Information about the client + std::optional clientInfo; + + /// The locale the client is currently showing the user interface + /// in. This must not necessarily be the locale of the operating + /// system. + /// + /// Uses IETF language tags as the value's syntax + /// (See https://en.wikipedia.org/wiki/IETF_language_tag) + /// + /// @since 3.16.0 + std::optional locale; + + /// User provided initialization options. + /// TODO: initializationOptions?: LSPAny; + + /// The capabilities provided by the client (editor or tool). + ClientCapabilities capabilities; + + /// The initial trace setting. If omitted trace is disabled ('off'). + /// TODO: trace?: TraceValue; + + /// The workspace folders configured in the client when the server starts. + /// This property is only available if the client supports workspace folders. + /// It can be `null` if the client supports workspace folders but none are + /// configured. + std::optional> workspaceFolders; +}; + +/*===========================================================================// +// RESPONSES // +//===========================================================================*/ + +struct PositionEncodingKind { + /// Character offsets count UTF-8 code units (e.g bytes). + constexpr inline static std::string_view UTF8 = "utf-8"; + + /// Character offsets count UTF-16 code units. + /// This is the default and must always be supported by servers. + constexpr inline static std::string_view UTF16 = "utf-16"; + + /// Character offsets count UTF-32 code units. + /// Implementation note: these are the same as Unicode code points, + /// so this `PositionEncodingKind` may also be used for an + /// encoding-agnostic representation of character offsets. + constexpr inline static std::string_view UTF32 = "utf-32"; +}; + +/// Defines how the host (editor) should sync document changes to the language server. +enum class TextDocumentSyncKind { + /// Documents should not be synced at all. + None = 0, + + /// Documents are synced by always sending the full content of the document. + Full = 1, + + /// Documents are synced by sending the full content on open. After that only + /// incremental updates to the document are sent. + Incremental = 2, +}; + +/// Completion options. +struct CompletionOptions { + /// + /// The additional characters, beyond the defaults provided by the client (typically + ///[a-zA-Z]), that should automatically trigger a completion request. For example + ///`.` in JavaScript represents the beginning of an object property or method and is + /// thus a good candidate for triggering a completion request. + /// + /// Most tools trigger a completion request automatically without explicitly + /// requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they + /// do so when the user starts to type an identifier. For example if the user + /// types `c` in a JavaScript file code complete will automatically pop up + /// present `console` besides others as a completion item. Characters that + /// make up identifiers don't need to be listed here. + std::array triggerCharacters = {".", "<", ">", ":", "\"", "/", "*"}; + + /// The list of all possible characters that commit a completion. This field + /// can be used if clients don't support individual commit characters per + /// completion item. See client capability `completion.completionItem.commitCharactersSupport`. + /// + /// If a server provides both `allCommitCharacters` and commit characters on + /// an individual completion item the ones on the completion item win. + /// + /// allCommitCharacters?: string[]; + /// NOTICE: We don't set `(` etc as allCommitCharacters as they interact poorly with snippet results. + /// See https://github.com/clangd/vscode-clangd/issues/357 + /// Hopefully we can use them one day without this side-effect: + /// https://github.com/microsoft/vscode/issues/42544 + + /// The server provides support to resolve additional information for a completion item. + bool resolveProvider = false; + + /// The server supports the following `CompletionItem` specific capabilities. + /// TODO: completionItem?: {...} +}; + +struct SignatureHelpOptions { + /// The characters that trigger signature help automatically. + std::array triggerCharacters = {"(", ")", "{", "}", "<", ">", ","}; + + /// List of characters that re-trigger signature help. + /// These trigger characters are only active when signature help is already showing. + /// All trigger characters are also counted as re-trigger characters. + std::array retriggerCharacters = {","}; +}; + +struct CodeLensOptions { + /// Code lens has a resolve provider as well. + bool resolveProvider = false; +}; + +struct DocumentLinkOptions { + /// Document links have a resolve provider as well. + bool resolveProvider = false; +}; + +struct DocumentOnTypeFormattingOptions { + /// A character on which formatting should be triggered, like `{`. + std::string_view firstTriggerCharacter = "\n"; + + /// More trigger characters. + /// moreTriggerCharacter?: string[]; +}; + +struct ServerCapabilities { + /// The position encoding the server picked from the encodings offered + /// by the client via the client capability `general.positionEncodings`. + /// + /// If the client didn't provide any position encodings the only valid + /// value that a server can return is 'utf-16'. + /// + /// If omitted it defaults to 'utf-16'. + /// + /// possible values: ['utf-8', 'utf-16', 'utf-32'] in PositionEncodingKind + std::string_view positionEncoding = PositionEncodingKind::UTF8; + + /// Defines how text documents are synced. Is either a detailed structure + /// defining each notification or for backwards compatibility the + /// TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`. + TextDocumentSyncKind textDocumentSync = TextDocumentSyncKind::Incremental; + + /// Defines how notebook documents are synced. + /// TODO: notebookDocumentSync?: NotebookDocumentSyncOptions | NotebookDocumentSyncRegistrationOptions; + + /// The server provides completion support. + CompletionOptions completionProvider; + + /// The server provides hover support. + bool hoverProvider = true; + + /// The server provides signature help support. + SignatureHelpOptions signatureHelpProvider; + + /// The server provides go to declaration support. + bool declarationProvider = true; + + /// The server provides goto definition support. + bool definitionProvider = true; + + /// The server provides goto type definition support. + bool typeDefinitionProvider = true; + + /// The server provides goto implementation support. + bool implementationProvider = true; + + /// The server provides find references support. + bool referencesProvider = true; + + /// The server provides document highlight support. + bool documentHighlightProvider = true; + + /// The server provides document symbol support. + bool documentSymbolProvider = true; + + /// The server provides code actions. The `CodeActionOptions` return type is + /// only valid if the client signals code action literal support via the + /// client capability `textDocument.codeAction.codeActionLiteralSupport`. + bool codeActionProvider = true; // TODO: provide CodeActionOptions + + /// The server provides code lens. + CodeLensOptions codeLensProvider; + + /// The server provides document link support. + DocumentLinkOptions documentLinkProvider; + + /// The server provides color provider support. + bool colorProvider = false; // TODO: check what is colorProvider + + /// The server provides document formatting. + bool documentFormattingProvider = true; + + /// The server provides document range formatting. + bool documentRangeFormattingProvider = true; + + /// The server provides document formatting on typing. + DocumentOnTypeFormattingOptions documentOnTypeFormattingProvider; + + /// The server provides rename support. RenameOptions may only be + /// specified if the client states that it supports + /// `prepareSupport` in its initial `initialize` request. + bool renameProvider = true; + + /// The server provides folding provider support. + bool foldingRangeProvider = true; + + /// The server provides execute command support. + /// executeCommandProvider?: ExecuteCommandOptions; + + /// The server provides selection range support. + bool selectionRangeProvider = true; + + /// The server provides linked editing range support. + bool linkedEditingRangeProvider = true; + + /// The server provides call hierarchy support. + bool callHierarchyProvider = true; + + /// The server provides semantic tokens support. + // TODO: semanticTokensProvider?: SemanticTokensOptions + + /// Whether server provides moniker support. + bool monikerProvider = false; // TODO: further discussion + + /// The server provides type hierarchy support. + bool typeHierarchyProvider = true; + + /// The server provides inline values. + bool inlineValueProvider = true; + + /// The server provides inlay hints. + bool inlayHintProvider = true; + + /// The server has support for pull model diagnostics. + /// TODO: diagnosticProvider?: DiagnosticOptions + + /// The server provides workspace symbol support. + bool workspaceSymbolProvider = true; + + /// The server is interested in file notifications/requests. + /// TODO: fileOperations?: {...} +}; + +struct InitializeResult { + /// The capabilities the language server provides. + ServerCapabilities capabilities; + + struct ServerInfo { + /// The name of the server as defined by the server. + std::string_view name = "clice"; + + /// The server's version as defined by the server. + std::string_view version = "0.0.1"; + }; + + /// Information about the server. + ServerInfo serverInfo; +}; + +} // namespace clice diff --git a/include/Support/Reflection.h b/include/Support/Reflection.h new file mode 100644 index 00000000..186e9bc5 --- /dev/null +++ b/include/Support/Reflection.h @@ -0,0 +1,190 @@ +// support basic reflection through template meta programming +#include +#include +#include + +namespace clice::impl { + +struct Any { + consteval Any(std::size_t); + + template + consteval operator T () const; +}; + +template +consteval auto test() { + return [](std::index_sequence) { + return requires { T{Any(I)...}; }; + }(std::make_index_sequence{}); +} + +template +consteval auto member_count() { + if constexpr(test() && !test()) { + return N; + } else { + return member_count(); + } +} + +template +struct Wrapper { + T value; + + constexpr Wrapper(T value) : value(value) {} +}; + +template +constexpr auto member_name() { + std::string_view name = std::source_location::current().function_name(); +#if __GNUC__ && (!__clang__) && (!_MSC_VER) + std::size_t start = name.rfind("::") + 2; + std::size_t end = name.rfind(')'); + return name.substr(start, end - start); +#elif __clang__ + std::size_t start = name.rfind(".") + 1; + std::size_t end = name.rfind('}'); + return name.substr(start, end - start); +#elif _MSC_VER + std::size_t start = name.rfind("->") + 2; + std::size_t end = name.rfind('}'); + return name.substr(start, end - start); +#else + static_assert(false, "Not supported compiler"); +#endif +} + +template +constexpr auto collcet_members(Object&& object) { + // clang-format off + if constexpr(count == 1) { + auto&& [a] = object; + return std::tuple{&a}; + } else if constexpr(count == 2) { + auto&& [a, b] = object; + return std::tuple{&a, &b}; + } else if constexpr(count == 3) { + auto&& [a, b, c] = object; + return std::tuple{&a, &b, &c}; + } else if constexpr(count == 4) { + auto&& [a, b, c, d] = object; + return std::tuple{&a, &b, &c, &d}; + } else if constexpr(count == 5) { + auto&& [a, b, c, d, e] = object; + return std::tuple{&a, &b, &c, &d, &e}; + } else if constexpr(count == 6) { + auto&& [a, b, c, d, e, f] = object; + return std::tuple{&a, &b, &c, &d, &e, &f}; + } else if constexpr(count == 7) { + auto&& [a, b, c, d, e, f, g] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g}; + } else if constexpr(count == 8) { + auto&& [a, b, c, d, e, f, g, h] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h}; + } else if constexpr(count == 9) { + auto&& [a, b, c, d, e, f, g, h, i] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i}; + } else if constexpr(count == 10) { + auto&& [a, b, c, d, e, f, g, h, i, j] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j}; + } else if constexpr(count == 11) { + auto&& [a, b, c, d, e, f, g, h, i, j, k] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k}; + } else if constexpr(count == 12) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l}; + } else if constexpr(count == 13) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m}; + } else if constexpr(count == 14) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n}; + } else if constexpr(count == 15) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o}; + } else if constexpr(count == 16) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p}; + } else if constexpr(count == 17) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q}; + } else if constexpr(count == 18) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r}; + } else if constexpr(count == 19) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s}; + } else if constexpr(count == 20) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t}; + } else if constexpr(count == 21) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u}; + } else if constexpr(count == 22) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, + &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v}; + } else if constexpr(count == 23) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, + &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w}; + } else if constexpr(count == 24) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, + &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x}; + } else if constexpr(count == 25) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, + &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y}; + } else if constexpr(count == 26) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, + &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z}; + } else if constexpr(count == 27) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, + &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0}; + } else if constexpr(count == 28) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, + &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1}; + } else if constexpr(count == 29) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, + &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2}; + } else if constexpr(count == 30) { + auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2, _3] = object; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, + &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2, &_3}; + } else { + static_assert(count <= 30, "Not supported member count"); + } + // clang-format on +} + +template +struct Storage { + inline static T value; +}; + +} // namespace clice::impl + +namespace clice { + +template +concept Reflectable = + std::is_aggregate_v> && std::is_default_constructible_v>; + +template +constexpr void for_each(Object&& object, const Callback& callback) { + using T = std::decay_t; + constexpr auto count = impl::member_count(); + auto members = impl::collcet_members(object); + constexpr auto static_members = impl::collcet_members(impl::Storage::value); + [&](std::index_sequence) { + (callback(impl::member_name(static_members)>(), *std::get(members)), ...); + }(std::make_index_sequence{}); +} + +}; // namespace clice diff --git a/include/Support/Serialize.h b/include/Support/Serialize.h new file mode 100644 index 00000000..bae3a3ee --- /dev/null +++ b/include/Support/Serialize.h @@ -0,0 +1,54 @@ +#include "Reflection.h" +#include +#include + +namespace clice { + +template +constexpr inline bool is_optional_v = false; + +template +constexpr inline bool is_optional_v> = true; + +template +constexpr inline bool is_vector_like_v = false; + +template +constexpr inline bool is_vector_like_v> = true; + +template +constexpr inline bool is_vector_like_v> = true; + +template +void print(Object&& object) { + using T = std::decay_t; + if constexpr(std::is_same_v) { + std::cout << (object ? "true" : "false") << std::endl; + } else if constexpr(std::is_integral_v || std::is_floating_point_v || + std::is_same_v || std::is_same_v) { + std::cout << object << std::endl; + } else if constexpr(std::is_enum_v) { + std::cout << static_cast>(object) << std::endl; + } else if constexpr(is_optional_v) { + if(object.has_value()) { + print(object.value()); + } else { + std::cout << "null" << std::endl; + } + } else if constexpr(is_vector_like_v) { + std::cout << "[" << std::endl; + for(auto&& value: object) { + print(value); + } + std::cout << "]" << std::endl; + } else { + std::cout << "{" << std::endl; + for_each(object, [&](auto&& name, auto&& value) { + std::cout << name << ": "; + print(value); + }); + std::cout << "}" << std::endl; + } +} +} // namespace clice +