basic implementation of protocol and serialization.

This commit is contained in:
ykiko
2024-07-05 23:32:24 +08:00
parent 15be6b24a6
commit 52056862e1
3 changed files with 852 additions and 0 deletions

608
include/LSP/Protocol.h Normal file
View File

@@ -0,0 +1,608 @@
#include <array>
#include <vector>
#include <optional>
#include <string_view>
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<bool> 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<std::vector<std::string_view>> resourceOperations;
/// The failure handling strategy of a client if applying the workspace edit
/// fails.
/// possible values are in FailureHandlingKind
std::optional<std::string_view> 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<bool> 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<bool> groupsOnLabel;
};
/// Whether the client in general supports change annotations on text edits,
/// create file, rename file and delete file changes.
std::optional<ChangeAnnotationSupport> changeAnnotationSupport;
};
struct DidChangeConfigurationClientCapabilities {
/// Did change configuration notification supports dynamic registration.
std::optional<bool> 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<bool> dynamicRegistration;
/// Whether the client has support for relative patterns or not.
std::optional<bool> supportsRelativePattern;
};
struct WorkspaceSymbolClientCapabilities {
/// Symbol request supports dynamic registration.
std::optional<bool> 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<SymbolKind> valueSet;
};
/// Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
std::optional<SymbolKind_> symbolKind;
struct TagSupport {
/// The tags supported by the client.
std::vector<std::string_view> valueSet;
};
/// The client supports tags on `SymbolInformation` and `WorkspaceSymbol`.
/// Clients supporting tags have to handle unknown tags gracefully.
std::optional<TagSupport> tagSupport;
struct ResolveSupport {
/// The properties that a client can resolve lazily. Usually `location.range`.
std::vector<std::string> 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> resolveSupport;
};
struct ExecuteCommandClientCapabilities {
/// Execute command supports dynamic registration.
std::optional<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> refreshSupport;
};
struct ClientCapabilities {
struct Workplace {
/// The client supports applying batch edits
/// to the workspace by supporting the request
/// 'workspace/applyEdit'
std::optional<bool> applyEdit;
/// Capabilities specific to `WorkspaceEdit`s
std::optional<WorkspaceEditClientCapabilities> workspaceEdit;
/// Capabilities specific to the `workspace/didChangeConfiguration` notification.
std::optional<DidChangeConfigurationClientCapabilities> didChangeConfiguration;
/// Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
std::optional<DidChangeWatchedFilesClientCapabilities> didChangeWatchedFiles;
/// Capabilities specific to the `workspace/symbol` request.
std::optional<WorkspaceSymbolClientCapabilities> symbol;
/// Capabilities specific to the `workspace/executeCommand` request.
std::optional<ExecuteCommandClientCapabilities> executeCommand;
/// The client has support for workspace folders.
std::optional<bool> workspaceFolders;
/// The client supports `workspace/configuration` requests.
std::optional<bool> configuration;
/// Capabilities specific to the semantic token requests scoped to the workspace.
std::optional<SemanticTokensWorkspaceClientCapabilities> semanticTokens;
/// Capabilities specific to the code lens requests scoped to the workspace.
std::optional<CodeLensWorkspaceClientCapabilities> codeLens;
struct FileOperations {
/// Whether the client supports dynamic registration for file requests/notifications.
std::optional<bool> dynamicRegistration;
/// The client has support for sending didCreateFiles notifications.
std::optional<bool> didCreate;
/// The client has support for sending willCreateFiles requests.
std::optional<bool> willCreate;
/// The client has support for sending didRenameFiles notifications.
std::optional<bool> didRename;
/// The client has support for sending willRenameFiles requests.
std::optional<bool> willRename;
/// The client has support for sending didDeleteFiles notifications.
std::optional<bool> didDelete;
/// The client has support for sending willDeleteFiles requests.
std::optional<bool> willDelete;
};
/// The client has support for file requests/notifications.
std::optional<FileOperations> fileOperations;
/// Client workspace capabilities specific to inline values.
std::optional<InlineValueWorkspaceClientCapabilities> inlineValue;
/// Client workspace capabilities specific to inlay hints.
std::optional<InlayHintWorkspaceClientCapabilities> inlayHint;
/// Client workspace capabilities specific to diagnostics.
std::optional<DiagnosticWorkspaceClientCapabilities> diagnostic;
};
/// Workspace specific client capabilities.
std::optional<Workplace> 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<int> processId;
struct ClientInfo {
std::string_view name;
std::optional<std::string_view> version;
};
/// Information about the client
std::optional<ClientInfo> 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<std::string_view> 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<std::vector<WorkspaceFolder>> 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<std::string_view, 7> 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<std::string_view, 7> 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<std::string_view, 1> 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

View File

@@ -0,0 +1,190 @@
// support basic reflection through template meta programming
#include <tuple>
#include <string_view>
#include <source_location>
namespace clice::impl {
struct Any {
consteval Any(std::size_t);
template <typename T>
consteval operator T () const;
};
template <typename T, std::size_t N>
consteval auto test() {
return []<std::size_t... I>(std::index_sequence<I...>) {
return requires { T{Any(I)...}; };
}(std::make_index_sequence<N>{});
}
template <typename T, std::size_t N = 0>
consteval auto member_count() {
if constexpr(test<T, N>() && !test<T, N + 1>()) {
return N;
} else {
return member_count<T, N + 1>();
}
}
template <typename T>
struct Wrapper {
T value;
constexpr Wrapper(T value) : value(value) {}
};
template <Wrapper T>
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 <std::size_t count, typename Object>
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 <typename T>
struct Storage {
inline static T value;
};
} // namespace clice::impl
namespace clice {
template <typename T>
concept Reflectable =
std::is_aggregate_v<std::decay_t<T>> && std::is_default_constructible_v<std::decay_t<T>>;
template <Reflectable Object, typename Callback>
constexpr void for_each(Object&& object, const Callback& callback) {
using T = std::decay_t<Object>;
constexpr auto count = impl::member_count<T>();
auto members = impl::collcet_members<count>(object);
constexpr auto static_members = impl::collcet_members<count>(impl::Storage<T>::value);
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
(callback(impl::member_name<std::get<Is>(static_members)>(), *std::get<Is>(members)), ...);
}(std::make_index_sequence<count>{});
}
}; // namespace clice

View File

@@ -0,0 +1,54 @@
#include "Reflection.h"
#include <iostream>
#include <optional>
namespace clice {
template <typename T>
constexpr inline bool is_optional_v = false;
template <typename T>
constexpr inline bool is_optional_v<std::optional<T>> = true;
template <typename T>
constexpr inline bool is_vector_like_v = false;
template <typename T, std::size_t N>
constexpr inline bool is_vector_like_v<std::array<T, N>> = true;
template <typename T>
constexpr inline bool is_vector_like_v<std::vector<T>> = true;
template <typename Object>
void print(Object&& object) {
using T = std::decay_t<Object>;
if constexpr(std::is_same_v<T, bool>) {
std::cout << (object ? "true" : "false") << std::endl;
} else if constexpr(std::is_integral_v<T> || std::is_floating_point_v<T> ||
std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
std::cout << object << std::endl;
} else if constexpr(std::is_enum_v<T>) {
std::cout << static_cast<std::underlying_type_t<T>>(object) << std::endl;
} else if constexpr(is_optional_v<T>) {
if(object.has_value()) {
print(object.value());
} else {
std::cout << "null" << std::endl;
}
} else if constexpr(is_vector_like_v<T>) {
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