From 61035b7f9cf9790157a473bceed903f7b6c6f5cc Mon Sep 17 00:00:00 2001 From: Arthur Vasseur Date: Thu, 19 Feb 2026 18:57:39 +0100 Subject: [PATCH 1/4] feat: add ChunkedStreamBuf and AsyncChunkedStreamBuf for efficient data handling --- .../PackageGenerator/ChunkedStreamBuf.hpp | 524 ++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 Src/Concerto/PackageGenerator/ChunkedStreamBuf.hpp diff --git a/Src/Concerto/PackageGenerator/ChunkedStreamBuf.hpp b/Src/Concerto/PackageGenerator/ChunkedStreamBuf.hpp new file mode 100644 index 0000000..2e64a7e --- /dev/null +++ b/Src/Concerto/PackageGenerator/ChunkedStreamBuf.hpp @@ -0,0 +1,524 @@ +// +// Created by arthur +// + +#ifndef CONCERTO_PKGGENERATOR_CHUNKEDSTREAMBUF_HPP +#define CONCERTO_PKGGENERATOR_CHUNKEDSTREAMBUF_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace cct +{ + /** + * A streambuf that stores data in fixed-size, non-contiguous chunks. + * Avoids the contiguous allocation and geometric reallocation of std::ostringstream. + * Memory usage is always within one chunk of the actual data size. + * + * Usage: + * ChunkedStreamBuf buf; + * std::ostream stream(&buf); + * stream << "hello"; + * buf.WriteTo(outFile); + */ + class ChunkedStreamBuf : public std::streambuf + { + public: + static constexpr std::size_t DefaultChunkSize = 256 * 1024; // 256 KB + + explicit ChunkedStreamBuf(std::size_t chunkSize = DefaultChunkSize) : + m_chunkSize(chunkSize > 0 ? chunkSize : DefaultChunkSize), + m_fullChunkBytes(0) + { + AllocateChunk(); + } + + [[nodiscard]] std::size_t GetTotalBytes() const noexcept + { + return m_fullChunkBytes + static_cast(pptr() - pbase()); + } + + [[nodiscard]] bool Empty() const noexcept + { + return GetTotalBytes() == 0; + } + + void WriteTo(std::ostream& out) const + { + for (std::size_t i = 0; i < m_chunks.size(); ++i) + { + std::size_t size = (i + 1 < m_chunks.size()) + ? m_chunkSize + : static_cast(pptr() - pbase()); + if (size > 0) + out.write(m_chunks[i].data(), static_cast(size)); + } + } + + struct ChunkView + { + const char* data; + std::size_t size; + }; + + class Iterator + { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = ChunkView; + using difference_type = std::ptrdiff_t; + using pointer = const ChunkView*; + using reference = ChunkView; + + Iterator(const ChunkedStreamBuf* buf, std::size_t index) : + m_buf(buf), + m_index(index) + { + } + + ChunkView operator*() const + { + const auto& chunk = m_buf->m_chunks[m_index]; + std::size_t size = (m_index + 1 < m_buf->m_chunks.size()) + ? m_buf->m_chunkSize + : static_cast(m_buf->pptr() - m_buf->pbase()); + return {chunk.data(), size}; + } + + Iterator& operator++() + { + ++m_index; + return *this; + } + Iterator operator++(int) + { + Iterator tmp = *this; + ++m_index; + return tmp; + } + bool operator==(const Iterator& other) const + { + return m_buf == other.m_buf && m_index == other.m_index; + } + bool operator!=(const Iterator& other) const + { + return !(*this == other); + } + + private: + const ChunkedStreamBuf* m_buf; + std::size_t m_index; + }; + + [[nodiscard]] Iterator begin() const + { + return Iterator(this, 0); + } + [[nodiscard]] Iterator end() const + { + return Iterator(this, m_chunks.size()); + } + + protected: + int_type overflow(int_type ch) override + { + if (ch == traits_type::eof()) + return traits_type::not_eof(ch); + + m_fullChunkBytes += m_chunkSize; + AllocateChunk(); + *pptr() = static_cast(ch); + pbump(1); + return ch; + } + + std::streamsize xsputn(const char* s, std::streamsize count) override + { + std::streamsize remaining = count; + while (remaining > 0) + { + std::streamsize available = epptr() - pptr(); + if (available == 0) + { + m_fullChunkBytes += m_chunkSize; + AllocateChunk(); + available = static_cast(m_chunkSize); + } + + std::streamsize toWrite = std::min(remaining, available); + std::memcpy(pptr(), s, static_cast(toWrite)); + pbump(static_cast(toWrite)); + s += toWrite; + remaining -= toWrite; + } + return count; + } + + private: + void AllocateChunk() + { + m_chunks.emplace_back(m_chunkSize); + auto& chunk = m_chunks.back(); + setp(chunk.data(), chunk.data() + m_chunkSize); + } + + std::deque> m_chunks; + std::size_t m_chunkSize; + std::size_t m_fullChunkBytes; + }; + + /** + * Convenience ostream that owns a ChunkedStreamBuf. + * + * Usage: + * ChunkedOStream stream; + * stream << "data"; + * stream.WriteTo(outFile); + */ + class ChunkedOStream : public std::ostream + { + public: + explicit ChunkedOStream(std::size_t chunkSize = ChunkedStreamBuf::DefaultChunkSize) : + std::ostream(&m_buf), + m_buf(chunkSize) + { + } + + [[nodiscard]] ChunkedStreamBuf& GetBuffer() noexcept + { + return m_buf; + } + [[nodiscard]] const ChunkedStreamBuf& GetBuffer() const noexcept + { + return m_buf; + } + [[nodiscard]] std::size_t GetTotalBytes() const noexcept + { + return m_buf.GetTotalBytes(); + } + [[nodiscard]] bool Empty() const noexcept + { + return m_buf.GetTotalBytes() == 0; + } + + void WriteTo(std::ostream& out) const + { + m_buf.WriteTo(out); + } + + private: + ChunkedStreamBuf m_buf; + }; + + /** + * A streambuf that flushes completed chunks asynchronously via a background thread. + * + * When a chunk is full, it is moved to a flush queue and written to the destination + * by a dedicated thread, while the writer thread continues filling the next chunk. + * Chunks are recycled after flushing to avoid repeated allocations. + * + * Backpressure: if the flush queue reaches maxQueuedChunks, the writer blocks + * until the flush thread drains a chunk. + * + * Usage: + * std::ofstream file("output.cpp"); + * AsyncChunkedStreamBuf buf(file); + * std::ostream stream(&buf); + * stream << generatedCode; + * // on destruction, remaining data is flushed and thread is joined + * + * Or with a custom callback (e.g. socket): + * AsyncChunkedStreamBuf buf([&](const char* data, std::size_t size) { + * send(socket, data, size, 0); + * }); + * + * The flush callback must not throw. + */ + class AsyncChunkedStreamBuf : public std::streambuf + { + public: + using FlushCallback = std::function; + + static constexpr std::size_t DefaultChunkSize = 256 * 1024; // 256 KB + static constexpr std::size_t DefaultMaxQueuedChunks = 8; // 2 MB max in-flight + + explicit AsyncChunkedStreamBuf( + FlushCallback callback, + std::size_t chunkSize = DefaultChunkSize, + std::size_t maxQueuedChunks = DefaultMaxQueuedChunks) : + m_chunkSize(chunkSize > 0 ? chunkSize : DefaultChunkSize), + m_maxQueuedChunks(maxQueuedChunks > 0 ? maxQueuedChunks : DefaultMaxQueuedChunks), + m_callback(std::move(callback)) + { + m_currentChunk.resize(m_chunkSize); + setp(m_currentChunk.data(), m_currentChunk.data() + m_chunkSize); + m_flushThread = std::jthread([this](const std::stop_token&) + { FlushLoop(); }); + } + + explicit AsyncChunkedStreamBuf( + std::shared_ptr dest, + std::size_t chunkSize = DefaultChunkSize, + std::size_t maxQueuedChunks = DefaultMaxQueuedChunks) : + AsyncChunkedStreamBuf( + [dest = std::move(dest)](const char* data, std::size_t size) + { + CCT_REFL_AUTO_PROFILER_SCOPE; + if (dest) + dest->write(data, static_cast(size)); + }, + chunkSize, + maxQueuedChunks) + { + } + + explicit AsyncChunkedStreamBuf( + std::ostream& dest, + std::size_t chunkSize = DefaultChunkSize, + std::size_t maxQueuedChunks = DefaultMaxQueuedChunks) : + AsyncChunkedStreamBuf( + [&dest](const char* data, std::size_t size) + { + CCT_REFL_AUTO_PROFILER_SCOPE; + dest.write(data, static_cast(size)); + }, + chunkSize, + maxQueuedChunks) + { + } + + ~AsyncChunkedStreamBuf() override + { + { + std::unique_lock lock(m_mutex); + auto bytes = static_cast(pptr() - pbase()); + if (bytes > 0) + { + m_flushQueue.push_back({.m_buffer = std::move(m_currentChunk), .m_written = bytes}); + } + m_done = true; + } + m_flushCV.notify_one(); + } + + AsyncChunkedStreamBuf(const AsyncChunkedStreamBuf&) = delete; + AsyncChunkedStreamBuf& operator=(const AsyncChunkedStreamBuf&) = delete; + AsyncChunkedStreamBuf(AsyncChunkedStreamBuf&&) = delete; + AsyncChunkedStreamBuf& operator=(AsyncChunkedStreamBuf&&) = delete; + + [[nodiscard]] std::size_t GetTotalBytes() const noexcept + { + return m_submittedBytes.load(std::memory_order_relaxed) + static_cast(pptr() - pbase()); + } + + protected: + int_type overflow(int_type ch) override + { + if (ch == traits_type::eof()) + return traits_type::not_eof(ch); + + SubmitCurrentChunk(m_chunkSize); + AcquireNewChunk(); + *pptr() = static_cast(ch); + pbump(1); + return ch; + } + + std::streamsize xsputn(const char* s, std::streamsize count) override + { + std::streamsize remaining = count; + while (remaining > 0) + { + auto available = static_cast(epptr() - pptr()); + if (available == 0) + { + SubmitCurrentChunk(m_chunkSize); + AcquireNewChunk(); + available = static_cast(m_chunkSize); + } + + std::streamsize toWrite = std::min(remaining, available); + std::memcpy(pptr(), s, static_cast(toWrite)); + pbump(static_cast(toWrite)); + s += toWrite; + remaining -= toWrite; + } + return count; + } + + int sync() override + { + auto bytes = static_cast(pptr() - pbase()); + if (bytes > 0) + { + SubmitCurrentChunk(bytes); + AcquireNewChunk(); + } + // Wait for all queued chunks to be flushed + { + std::unique_lock lock(m_mutex); + m_drainCV.wait(lock, [this] + { return m_flushQueue.empty() && !m_flushing; }); + } + return 0; + } + + private: + struct ChunkEntry + { + std::vector m_buffer; + std::size_t m_written = 0; + }; + + void SubmitCurrentChunk(std::size_t validBytes) + { + CCT_REFL_AUTO_PROFILER_SCOPE; + std::unique_lock lock(m_mutex); + // Backpressure: block if flush queue is full + m_writerCV.wait(lock, [this] + { return m_flushQueue.size() < m_maxQueuedChunks; }); + m_flushQueue.push_back({.m_buffer = std::move(m_currentChunk), .m_written = validBytes}); + m_submittedBytes.fetch_add(validBytes, std::memory_order_relaxed); + m_flushCV.notify_one(); + } + + void AcquireNewChunk() + { + CCT_REFL_AUTO_PROFILER_SCOPE; + std::lock_guard lock(m_mutex); + if (!m_freePool.empty()) + { + m_currentChunk = std::move(m_freePool.back()); + m_freePool.pop_back(); + } + else + { + m_currentChunk.resize(m_chunkSize); + } + setp(m_currentChunk.data(), m_currentChunk.data() + m_chunkSize); + } + + void FlushLoop() + { + while (true) + { + ChunkEntry entry; + { + std::unique_lock lock(m_mutex); + m_flushCV.wait(lock, [this] + { return !m_flushQueue.empty() || m_done; }); + CCT_REFL_AUTO_PROFILER_SCOPE; + if (m_flushQueue.empty() && m_done) + { + return; + } + + if (m_flushQueue.empty()) + { + continue; + } + + entry = std::move(m_flushQueue.front()); + m_flushQueue.pop_front(); + m_flushing = true; + } + + m_callback(entry.m_buffer.data(), entry.m_written); + + { + std::lock_guard lock(m_mutex); + m_flushing = false; + m_freePool.push_back(std::move(entry.m_buffer)); + m_writerCV.notify_one(); + m_drainCV.notify_all(); + } + } + } + + std::size_t m_chunkSize; + std::size_t m_maxQueuedChunks; + FlushCallback m_callback; + + std::vector m_currentChunk; + std::deque m_flushQueue; + std::vector> m_freePool; + std::mutex m_mutex; + std::condition_variable m_flushCV; // wake flush thread + std::condition_variable m_writerCV; // backpressure relief + std::condition_variable m_drainCV; // sync() drain notification + bool m_done = false; + bool m_flushing = false; + std::atomic m_submittedBytes{0}; + + std::jthread m_flushThread; + }; + + /** + * Convenience ostream that owns an AsyncChunkedStreamBuf. + * + * Usage: + * std::ofstream file("output.cpp"); + * AsyncChunkedOStream stream(file); + * stream << generatedCode; + * stream.flush(); // waits for all data to be written + */ + class AsyncChunkedOStream : public std::ostream + { + public: + explicit AsyncChunkedOStream( + AsyncChunkedStreamBuf::FlushCallback callback, + std::size_t chunkSize = AsyncChunkedStreamBuf::DefaultChunkSize, + std::size_t maxQueuedChunks = AsyncChunkedStreamBuf::DefaultMaxQueuedChunks) : + std::ostream(&m_buf), + m_buf(std::move(callback), chunkSize, maxQueuedChunks) + { + } + + explicit AsyncChunkedOStream( + std::shared_ptr dest, + std::size_t chunkSize = AsyncChunkedStreamBuf::DefaultChunkSize, + std::size_t maxQueuedChunks = AsyncChunkedStreamBuf::DefaultMaxQueuedChunks) : + std::ostream(&m_buf), + m_buf(std::move(dest), chunkSize, maxQueuedChunks) + { + } + + explicit AsyncChunkedOStream( + std::ostream& dest, + std::size_t chunkSize = AsyncChunkedStreamBuf::DefaultChunkSize, + std::size_t maxQueuedChunks = AsyncChunkedStreamBuf::DefaultMaxQueuedChunks) : + std::ostream(&m_buf), + m_buf(dest, chunkSize, maxQueuedChunks) + { + } + + [[nodiscard]] std::size_t GetTotalBytes() const noexcept + { + return m_buf.GetTotalBytes(); + } + [[nodiscard]] AsyncChunkedStreamBuf& GetBuffer() noexcept + { + return m_buf; + } + + private: + AsyncChunkedStreamBuf m_buf; + }; + +} // namespace cct + +#endif // CONCERTO_PKGGENERATOR_CHUNKEDSTREAMBUF_HPP From a8f05359569a4b23019872c79714e79a4276a163 Mon Sep 17 00:00:00 2001 From: Arthur Vasseur Date: Thu, 19 Feb 2026 19:12:51 +0100 Subject: [PATCH 2/4] feat: introduce plugin system --- Docs/PluginSystem.md | 258 ++++ Src/Concerto/CppPlugin/CppPlugin.c | 1225 +++++++++++++++++ Src/Concerto/HeaderPlugin/HeaderPlugin.c | 222 +++ .../ClangParser/ClangParser.cpp | 39 +- .../CppGenerator/CppGenerator.cpp | 825 ----------- .../CppGenerator/CppGenerator.hpp | 29 - Src/Concerto/PackageGenerator/Defines.hpp | 2 +- .../FileGenerator/FileGenerator.cpp | 97 -- .../FileGenerator/FileGenerator.hpp | 57 - .../HeaderGenerator/HeaderGenerator.cpp | 200 --- .../HeaderGenerator/HeaderGenerator.hpp | 27 - .../PackageGenerator/Plugin/PluginApi.cpp | 597 ++++++++ .../PackageGenerator/Plugin/PluginApi.h | 241 ++++ .../Plugin/PluginRegistry.cpp | 600 ++++++++ .../Plugin/PluginRegistry.hpp | 111 ++ .../Plugin/ReflectionGeneratorPlugin.hpp | 117 ++ Src/Concerto/PackageGenerator/main.cpp | 296 +++- .../Reflection/Enumeration/Enumeration.cpp | 3 +- .../Enumeration/EnumerationClass.hpp | 2 +- .../GlobalNamespace/GlobalNamespace.cpp | 2 + Src/Tests/Class.cpp | 2 +- Src/Tests/Method.cpp | 2 +- Src/Tests/Namespace.cpp | 2 +- Src/Tests/Plugin.cpp | 374 +++++ Xmake/rules/cct_cpp_reflect.lua | 20 + libllvm/constants.lua | 375 +++++ libllvm/xmake.lua | 214 +++ xmake.lua | 124 +- 28 files changed, 4758 insertions(+), 1305 deletions(-) create mode 100644 Docs/PluginSystem.md create mode 100644 Src/Concerto/CppPlugin/CppPlugin.c create mode 100644 Src/Concerto/HeaderPlugin/HeaderPlugin.c delete mode 100644 Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp delete mode 100644 Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp delete mode 100644 Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.cpp delete mode 100644 Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp delete mode 100644 Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.cpp delete mode 100644 Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.hpp create mode 100644 Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp create mode 100644 Src/Concerto/PackageGenerator/Plugin/PluginApi.h create mode 100644 Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp create mode 100644 Src/Concerto/PackageGenerator/Plugin/PluginRegistry.hpp create mode 100644 Src/Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp create mode 100644 Src/Tests/Plugin.cpp create mode 100644 libllvm/constants.lua create mode 100644 libllvm/xmake.lua diff --git a/Docs/PluginSystem.md b/Docs/PluginSystem.md new file mode 100644 index 0000000..a0959b2 --- /dev/null +++ b/Docs/PluginSystem.md @@ -0,0 +1,258 @@ +# Concerto Reflection Plugin System + +## Overview + +The Concerto Reflection plugin system allows you to extend the reflection code generator capabilities in a modular way. It provides a flexible architecture based on a stable C API to ensure ABI compatibility across different compilers and versions. + +## Architecture + +### Core Components + +1. **PluginApi.h/cpp** - Pure C API for cross-compiler compatibility +2. **PluginRegistry** - Plugin manager with dynamic loading +3. **ReflectionGeneratorPlugin** - Optional C++ interface to simplify development +4. **DefaultGeneratorsPlugin** - Built-in plugin providing Header and Cpp generators + +### Flow Diagram + +``` +PackageGenerator (main) + │ + ├─> PluginRegistry + │ ├─> LoadPlugin(path) + │ ├─> TransformPackage() + │ └─> CollectGenerators() + │ + └─> Plugins (DLL/SO) + ├─> DefaultGeneratorsPlugin (built-in) + └─> Custom Plugins (user-defined) +``` + +## Plugin API + +### Basic Structure + +Each plugin must export a function table via a standard entry point: + +```c +CRP_PLUGIN_EXPORT const CrpPluginFunctionTable* crpGetPluginFunctionTable(void); +``` + +### Function Table + +```c +typedef struct CrpPluginFunctionTable { + uint32_t apiVersion; // API version (required) + + // Metadata + void (*GetInfo)(CrpPluginInfo* outInfo); // Required + int32_t (*GetPriority)(void); // Optional (default: 0) + + // Lifecycle + int32_t (*OnLoad)(void); // Optional + void (*OnUnload)(void); // Optional + + // Transformation + void (*TransformPackage)(CrpPackage* package); // Optional + + // File generators + int32_t (*CreateGenerators)(...); // Optional + void (*DestroyGenerator)(...); // Optional + + // Generation hooks (all optional) + void (*BeforePackageGeneration)(CrpGenerationContext* ctx); + void (*AfterPackageGeneration)(CrpGenerationContext* ctx); + void (*BeforeNamespaceGeneration)(...); + void (*AfterNamespaceGeneration)(...); + void (*BeforeClassGeneration)(...); + void (*AfterClassGeneration)(...); + void (*BeforeMemberGeneration)(...); + void (*AfterMemberGeneration)(...); + void (*BeforeMethodGeneration)(...); + void (*AfterMethodGeneration)(...); + void (*BeforeEnumGeneration)(...); + void (*AfterEnumGeneration)(...); + void (*BeforeTemplateClassGeneration)(...); + void (*AfterTemplateClassGeneration)(...); + void (*BeforeGenericClassGeneration)(...); + void (*AfterGenericClassGeneration)(...); + + // Custom annotations + int32_t (*HandleAnnotation)(...); // Optional +} CrpPluginFunctionTable; +``` + +## Priority System + +Plugins are executed in **descending** priority order: +- High priority (e.g., 100) → executed first +- Default priority (0) +- Low priority (e.g., -100) → executed last + +The `DefaultGeneratorsPlugin` uses a priority of **-100** to execute first, allowing other plugins to add hooks after the base generators are created. + +## Generation Hooks + +Hooks allow you to inject code at different stages of the generation process: + +### Execution Order + +``` +BeforePackageGeneration + └─> BeforeNamespaceGeneration + ├─> BeforeClassGeneration + │ ├─> BeforeMemberGeneration + │ ├─> AfterMemberGeneration + │ ├─> BeforeMethodGeneration + │ └─> AfterMethodGeneration + └─> AfterClassGeneration + ├─> BeforeEnumGeneration + └─> AfterEnumGeneration + AfterNamespaceGeneration +AfterPackageGeneration +``` + +### GenerationContext + +The context provided to hooks allows writing to the output stream: + +```cpp +struct GenerationContext { + const Package* package; + const Namespace* currentNamespace; + const Class* currentClass; + const Enum* currentEnum; + std::ostringstream* outputStream; + std::size_t* indentLevel; + std::string_view apiMacro; + + // Utility methods + void Write(format_string, ...); + void NewLine(); + void EnterScope(); + void LeaveScope(suffix = ""); +}; +``` + +## C API Accessors + +The API provides accessors for all opaque types: + +### Package +- `crpPackageGetName()` +- `crpPackageGetVersion()` +- `crpPackageGetClassCount()` / `crpPackageGetClass()` +- `crpPackageGetNamespaceCount()` / `crpPackageGetNamespace()` +- `crpPackageGetEnumCount()` / `crpPackageGetEnum()` + +### Class +- `crpClassGetName()` / `crpClassGetBase()` / `crpClassGetScope()` +- `crpClassGetMethodCount()` / `crpClassGetMethod()` +- `crpClassGetMemberCount()` / `crpClassGetMember()` +- `crpClassIsTemplate()` / `crpClassIsGeneric()` + +### Enum +- `crpEnumGetName()` / `crpEnumGetBase()` +- `crpEnumGetElementCount()` + +### Namespace +- `crpNamespaceGetName()` +- `crpNamespaceGetClassCount()` / `crpNamespaceGetClass()` +- `crpNamespaceGetEnumCount()` / `crpNamespaceGetEnum()` + +### ClassMember +- `crpClassMemberGetName()` / `crpClassMemberGetType()` +- `crpClassMemberIsNative()` + +### ClassMethod +- `crpClassMethodGetName()` / `crpClassMethodGetBase()` +- `crpClassMethodGetReturnType()` +- `crpClassMethodHasCustomInvoker()` + +### GenerationContext Utilities (C API) +These functions allow pure C plugins to write to the generation context: + +- `crpGenerationContextWrite(ctx, format, ...)` - Write formatted text with automatic indentation +- `crpGenerationContextNewLine(ctx)` - Add a blank line +- `crpGenerationContextEnterScope(ctx)` - Open a scope `{` and increase indentation +- `crpGenerationContextLeaveScope(ctx, suffix)` - Close a scope `}` with optional suffix (e.g., `;`) and decrease indentation + +**Example:** +```c +crpGenerationContextWrite(ctx, "void MyFunction()"); +crpGenerationContextEnterScope(ctx); +crpGenerationContextWrite(ctx, "printf(\"Hello\\n\");"); +crpGenerationContextLeaveScope(ctx, NULL); +``` + +All accessors check for NULL pointers and return safe values (`nullptr`, `""`, or `0`). + +## Loading Plugins + +### Command Line + +```bash +concerto-pkg-generator output-dir \ + -P path/to/plugin1.dll \ + -P path/to/plugin2.so \ + --plugin-dir path/to/plugins/directory +``` + +### Xmake Integration + +The system automatically detects plugins in dependencies: + +```lua +target("my-custom-plugin") + set_kind("shared") + add_deps("concerto-plugin-api") + -- Name must contain "generator-plugin", "generators-plugin" or "generators" + +target("my-library") + add_deps("concerto-reflection", "my-custom-plugin") + add_rules("cct_cpp_reflect") +``` + +The `cct_cpp_reflect` rule automatically collects plugins from dependencies and passes them to the generator. + +## Validation and Security + +The `PluginRegistry` performs several validations during loading: + +1. **API Version**: Checks that `apiVersion == CRP_PLUGIN_API_VERSION` +2. **Entry Point**: Verifies the presence of `crpGetPluginFunctionTable` +3. **Required GetInfo**: The `GetInfo` function must be provided +4. **OnLoad**: If present, must return `true` for success + +Loading failure → plugin is rejected with an error message in logs. + +## Plugin Lifecycle + +1. **Loading**: `DynLib::Load()` loads the library +2. **Resolution**: Retrieval of entry point `crpGetPluginFunctionTable` +3. **Validation**: API version and required functions check +4. **Initialization**: Call to `OnLoad()` if present +5. **Registration**: Added to registry and sorted by priority +6. **Usage**: Hooks and generators called during generation +7. **Unloading**: Call to `OnUnload()` if present +8. **Release**: DLL unloaded via `DynLib` + +## Use Cases + +### Transformation Plugin +Modify the package before generation (rename classes, add metadata, etc.) + +### Code Generation Plugin +Create additional files (bindings, serialization, etc.) + +### Hooks Plugin +Inject code into generated files (logging, validation, etc.) + +### Annotations Plugin +Process custom annotations ([@serialize], [@network], etc.) + +## See Also + +- [Plugin Creation Guide](CreatingPlugins.md) +- [Plugin Examples](PluginExamples.md) +- [API Reference](PluginApiReference.md) diff --git a/Src/Concerto/CppPlugin/CppPlugin.c b/Src/Concerto/CppPlugin/CppPlugin.c new file mode 100644 index 0000000..aa8f9b6 --- /dev/null +++ b/Src/Concerto/CppPlugin/CppPlugin.c @@ -0,0 +1,1225 @@ +// +// CppPlugin.c - Generates .gen.cpp implementation files +// + +#include +#include +#include +#include +#include + +#include "Concerto/PackageGenerator/Plugin/PluginApi.h" + +typedef struct CppPluginData +{ + int reserved; +} CppPluginData; + +static void GetInfo(CrpPluginInfo* outInfo) +{ + outInfo->name = "CppPlugin"; + outInfo->version = "1.0.0"; + outInfo->author = "Concerto"; + outInfo->description = "Generates .gen.cpp implementation files for reflection"; + outInfo->outputFileExtension = ".gen.cpp"; +} + +static int32_t OnLoad(void** outPrivateData) +{ + CppPluginData* data = (CppPluginData*)calloc(1, sizeof(CppPluginData)); + if (!data) + return 0; + if (outPrivateData) + *outPrivateData = data; + return 1; +} + +static void OnUnload(void* privateData) +{ + free(privateData); +} + +static void Capitalize(const char* src, char* dest, size_t destSize) +{ + if (destSize == 0) + return; + + if (src[0] >= 'a' && src[0] <= 'z') + dest[0] = src[0] - 32; + else + dest[0] = src[0]; + + size_t i = 1; + while (src[i] && i < destSize - 1) + { + dest[i] = src[i]; + i++; + } + dest[i] = '\0'; +} + +static int IsAsciiAlphaNum(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +} + +static void SanitizeIdentifier(const char* src, char* dest, size_t destSize) +{ + if (!dest || destSize == 0) + return; + + if (!src || src[0] == '\0') + { + if (destSize >= 2) + { + dest[0] = '_'; + dest[1] = '\0'; + } + else + { + dest[0] = '\0'; + } + return; + } + + size_t i = 0; + while (src[i] && i < destSize - 1) + { + const char c = src[i]; + dest[i] = (IsAsciiAlphaNum(c) || c == '_') ? c : '_'; + ++i; + } + dest[i] = '\0'; + + if (dest[0] >= '0' && dest[0] <= '9') + { + if (i + 1 < destSize) + { + memmove(dest + 1, dest, i + 1); + dest[0] = 'T'; + } + else + { + dest[0] = 'T'; + } + } +} + +static void SanitizeSpecialization(const char* src, char* dest, size_t destSize) +{ + size_t i = 0; + while (src[i] && i < destSize - 1) + { + char c = src[i]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) + dest[i] = c; + else + dest[i] = '_'; + i++; + } + dest[i] = '\0'; +} + +static int AppendToBuffer(char* dest, size_t destSize, const char* src) +{ + if (!dest || !src || destSize == 0) + return 0; + + const size_t destLen = strlen(dest); + const size_t srcLen = strlen(src); + if (destLen + srcLen >= destSize) + return 0; + + memcpy(dest + destLen, src, srcLen + 1); + return 1; +} + +static const char* StripConst(const char* type, char* buf, size_t bufSize) +{ + if (strncmp(type, "const ", 6) == 0) + { + size_t len = strlen(type) - 6; + if (len >= bufSize) + len = bufSize - 1; + memcpy(buf, type + 6, len); + buf[len] = '\0'; + return buf; + } + return type; +} + +static void BeforeMethodGeneration(const CrpClassMethod* method, CrpGenerationContext* ctx) +{ + const CrpClass* cls = crpGenerationContextGetClass(ctx); + const char* className = crpClassGetName(cls); + const char* methodName = crpClassMethodGetName(method); + const char* base = crpClassMethodGetBase(method); + size_t paramCount = crpClassMethodGetParamCount(method); + + const char* baseClass = (base && base[0] != '\0') ? base : "cct::refl::Method"; + + crpGenerationContextWrite(ctx, "class %s%sMethod : public %s", className, methodName, baseClass); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "using BaseClass = %s;", baseClass); + crpGenerationContextWrite(ctx, "using BaseClass::BaseClass;"); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "void Initialize() override"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "cct::Result Invoke(cct::refl::Object& self, std::span parameters) const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "if (parameters.size() != %zu)", paramCount); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Invalid parameters size\");"); + crpGenerationContextWrite(ctx, "return {\"Invalid parameters size\"s};"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + for (size_t i = 0; i < paramCount; ++i) + { + const CrpClassMethodParam* param = crpClassMethodGetParam(method, i); + const char* paramType = crpClassMethodParamGetType(param); + const char* paramName = crpClassMethodParamGetName(param); + + char strippedType[512]; + const char* usedType = StripConst(paramType, strippedType, sizeof(strippedType)); + + crpGenerationContextWrite(ctx, "using Param%zu = %s;", i, usedType); + crpGenerationContextWrite(ctx, "if (parameters[%zu].Is() == false)", i, i); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Expected '%s' in argument %zu\");", paramType, i); + crpGenerationContextWrite(ctx, "return {\"Expected '%s' in argument %zu\"s};", paramType, i); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "Param%zu %s = parameters[%zu].As();", i, paramName, i, i); + crpGenerationContextNewLine(ctx); + } + + crpGenerationContextNewLine(ctx); + } + } +} + +static void BeforeEnumGeneration(const CrpEnum* enumeration, CrpGenerationContext* ctx) +{ + const char* enumName = crpEnumGetName(enumeration); + + crpGenerationContextWrite(ctx, "class Internal%sEnumerationClass : public cct::refl::EnumerationClass", enumName); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%sEnumerationClass()", enumName); + crpGenerationContextWrite(ctx, ": cct::refl::EnumerationClass(nullptr, \"%s\"s)", enumName); + crpGenerationContextEnterScope(ctx); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "~Internal%sEnumerationClass() override", enumName); + crpGenerationContextEnterScope(ctx); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void Initialize() override"); + crpGenerationContextEnterScope(ctx); + } +} + +static void OnEnumElementGeneration(const CrpEnumElement* element, CrpGenerationContext* ctx) +{ + const char* elemName = crpEnumElementGetName(element); + const char* elemValue = crpEnumElementGetValue(element); + crpGenerationContextWrite(ctx, "AddEnumValue(\"%s\", %s);", elemName, elemValue); +} + +static void AfterEnumGeneration(const CrpEnum* enumeration, CrpGenerationContext* ctx) +{ + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "cct::refl::Object* GetMemberVariable(std::size_t, const cct::refl::Object&) const override"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return nullptr;"); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void* GetNativeMemberVariable(std::size_t, const cct::refl::Object&) const override"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return nullptr;"); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, ";"); +} + +static void BeforeClassGeneration(const CrpClass* cls, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + const char* baseClass = crpClassGetBase(cls); + const char* nsPath = crpGenerationContextGetNamespacePath(ctx); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "class Internal%sClass : public cct::refl::Class", className); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%sClass() : cct::refl::Class(nullptr, \"%s\"s, nullptr)", className, className); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "if (%s::m_class != nullptr)", className); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Class already created\");"); + crpGenerationContextWrite(ctx, "return;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "%s::m_class = this;", className); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextWrite(ctx, "~Internal%sClass() override", className); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "%s::m_class = nullptr;", className); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void Initialize() override"); + crpGenerationContextEnterScope(ctx); + { + if (nsPath && nsPath[0] != '\0') + crpGenerationContextWrite(ctx, "SetNamespace(GlobalNamespace::Get().GetNamespaceByName(\"%s\"sv));", nsPath); + + if (baseClass && baseClass[0] != '\0') + { + crpGenerationContextWrite(ctx, "const Class* baseClass = GetClassByName(\"%s\"sv);", baseClass); + crpGenerationContextWrite(ctx, "CCT_ASSERT(baseClass != nullptr, \"Could not find class '%s'\");", baseClass); + crpGenerationContextWrite(ctx, "SetBaseClass(baseClass);"); + } + + crpGenerationContextNewLine(ctx); + } + } +} + +static void OnMemberGeneration(const CrpClassMember* member, CrpGenerationContext* ctx) +{ + const char* memberName = crpClassMemberGetName(member); + const char* memberType = crpClassMemberGetType(member); + int32_t isNative = crpClassMemberIsNative(member); + + if (isNative) + { + crpGenerationContextWrite(ctx, "AddNativeMemberVariable(\"%s\", cct::TypeId<%s>());", memberName, memberType); + } + else + { + const char* nsPath = crpGenerationContextGetNamespacePath(ctx); + + char classVar[512]; + SanitizeIdentifier(memberName, classVar, sizeof(classVar)); + + if (nsPath && nsPath[0] != '\0') + { + crpGenerationContextWrite(ctx, "const cct::refl::Class* %sClass = cct::refl::GetClassByName(\"%s::%s\"sv);", classVar, nsPath, memberType); + crpGenerationContextWrite(ctx, "if (%sClass == nullptr)", classVar); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "%sClass = cct::refl::GetClassByName(\"%s\"sv);", classVar, memberType); + } + crpGenerationContextLeaveScope(ctx, NULL); + } + else + { + crpGenerationContextWrite(ctx, "const cct::refl::Class* %sClass = cct::refl::GetClassByName(\"%s\"sv);", classVar, memberType); + } + crpGenerationContextWrite(ctx, "CCT_ASSERT(%sClass != nullptr, \"Could not find class '%s'\");", classVar, memberType); + crpGenerationContextWrite(ctx, "AddMemberVariable(\"%s\", %sClass);", memberName, classVar); + } +} + +static void OnMethodGeneration(const CrpClassMethod* method, CrpGenerationContext* ctx) +{ + const CrpClass* cls = crpGenerationContextGetClass(ctx); + const char* className = crpClassGetName(cls); + const char* methodName = crpClassMethodGetName(method); + const char* returnType = crpClassMethodGetReturnType(method); + size_t paramCount = crpClassMethodGetParamCount(method); + if (!returnType || returnType[0] == '\0') + returnType = "void"; + + // Build call args strings from method params + char callArgs[2048] = ""; + char callArgsTypes[2048] = ""; + int overflow = 0; + + for (size_t i = 0; i < paramCount; ++i) + { + const CrpClassMethodParam* param = crpClassMethodGetParam(method, i); + const char* paramType = crpClassMethodParamGetType(param); + const char* paramName = crpClassMethodParamGetName(param); + + if (i > 0) + { + if (!AppendToBuffer(callArgs, sizeof(callArgs), ", ")) + overflow = 1; + if (!AppendToBuffer(callArgsTypes, sizeof(callArgsTypes), ", ")) + overflow = 1; + } + if (!AppendToBuffer(callArgs, sizeof(callArgs), paramName ? paramName : "")) + overflow = 1; + if (!AppendToBuffer(callArgsTypes, sizeof(callArgsTypes), paramType ? paramType : "")) + overflow = 1; + } + + if (overflow) + { + fprintf(stderr, "CppPlugin: method signature too long while generating %s::%s\n", + className ? className : "", + methodName ? methodName : ""); + return; + } + + int hasDelegate = crpClassMethodHasDelegate(method); + int isBoolDelegate = crpClassMethodIsBooleanDelegate(method); + const char* delegateName = crpClassMethodGetDelegateName(method); + + if (strcmp(returnType, "void") == 0) + { + if (hasDelegate && isBoolDelegate) + { + crpGenerationContextWrite(ctx, "if (GetCustomDelegate() == nullptr)"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Missing Delegate\");"); + crpGenerationContextWrite(ctx, "return {\"Invalid delegate pointer\"s};"); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "auto func = reinterpret_cast<%s(*)(%s)>(GetCustomDelegate());", returnType, callArgsTypes); + crpGenerationContextWrite(ctx, "func(%s);", callArgs); + } + else if (hasDelegate && delegateName) + { + crpGenerationContextWrite(ctx, "%s(%s);", delegateName, callArgs); + } + else + { + crpGenerationContextWrite(ctx, "static_cast<%s&>(self).%s(%s);", className, methodName, callArgs); + } + crpGenerationContextWrite(ctx, "return Any{};"); + } + else + { + if (hasDelegate && isBoolDelegate) + { + crpGenerationContextWrite(ctx, "if (GetCustomDelegate() == nullptr)"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Missing Delegate\");"); + crpGenerationContextWrite(ctx, "return {\"Missing Delegate\"s};"); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "auto func = reinterpret_cast<%s(*)(%s)>(GetCustomDelegate());", returnType, callArgsTypes); + crpGenerationContextWrite(ctx, "return Any::Make<%s>(func(%s));", returnType, callArgs); + } + else if (hasDelegate && delegateName) + { + crpGenerationContextWrite(ctx, "return Any::Make<%s>(static_cast<%s&>(self).%s(%s));", returnType, className, methodName, callArgs); + } + else + { + crpGenerationContextWrite(ctx, "auto res = static_cast<%s&>(self).%s(%s);", className, methodName, callArgs); + crpGenerationContextWrite(ctx, "return cct::Any::Make<%s>(res);", returnType); + } + } +} + +static void AfterMethodGeneration(const CrpClassMethod* method, CrpGenerationContext* ctx) +{ + const CrpClass* cls = crpGenerationContextGetClass(ctx); + const char* className = crpClassGetName(cls); + const char* methodName = crpClassMethodGetName(method); + const char* returnType = crpClassMethodGetReturnType(method); + size_t paramCount = crpClassMethodGetParamCount(method); + if (!returnType || returnType[0] == '\0') + returnType = "void"; + + // Close Invoke() scope + crpGenerationContextLeaveScope(ctx, NULL); + // Close class scope + crpGenerationContextLeaveScope(ctx, ";"); + + // Generate delegate function body if needed + if (crpClassMethodHasDelegate(method)) + { + char methodParameterPrototype[2048] = ""; + char delegateCallArgs[2048] = ""; + char delegateCallArgsTypes[2048] = ""; + int overflow = 0; + + for (size_t i = 0; i < paramCount; ++i) + { + const CrpClassMethodParam* param = crpClassMethodGetParam(method, i); + const char* paramType = crpClassMethodParamGetType(param); + const char* paramName = crpClassMethodParamGetName(param); + + if (i > 0) + { + if (!AppendToBuffer(methodParameterPrototype, sizeof(methodParameterPrototype), ", ")) + overflow = 1; + if (!AppendToBuffer(delegateCallArgs, sizeof(delegateCallArgs), ", ")) + overflow = 1; + if (!AppendToBuffer(delegateCallArgsTypes, sizeof(delegateCallArgsTypes), ", ")) + overflow = 1; + } + + char paramDecl[512]; + const int written = snprintf(paramDecl, sizeof(paramDecl), "%s %s", paramType ? paramType : "", paramName ? paramName : ""); + if (written < 0 || (size_t)written >= sizeof(paramDecl)) + overflow = 1; + if (!AppendToBuffer(methodParameterPrototype, sizeof(methodParameterPrototype), paramDecl)) + overflow = 1; + if (!AppendToBuffer(delegateCallArgs, sizeof(delegateCallArgs), paramName ? paramName : "")) + overflow = 1; + if (!AppendToBuffer(delegateCallArgsTypes, sizeof(delegateCallArgsTypes), paramType ? paramType : "")) + overflow = 1; + } + + if (overflow) + { + fprintf(stderr, "CppPlugin: delegate signature too long while generating %s::%s\n", + className ? className : "", + methodName ? methodName : ""); + return; + } + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "%s %s::%s(%s)", returnType, className, methodName, methodParameterPrototype); + crpGenerationContextEnterScope(ctx); + + const char* delName = crpClassMethodGetDelegateName(method); + if (delName) + { + if (strcmp(returnType, "void") != 0) + crpGenerationContextWrite(ctx, "return"); + crpGenerationContextWrite(ctx, "%s(%s)", delName, delegateCallArgs); + crpGenerationContextWrite(ctx, ";"); + } + else if (crpClassMethodIsBooleanDelegate(method)) + { + crpGenerationContextWrite(ctx, "const auto* methodClass = GetClass()->GetMethod(\"%s\"sv);", methodName); + crpGenerationContextWrite(ctx, "if (methodClass == nullptr || methodClass->GetCustomDelegate() == nullptr)"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Missing Delegate\");"); + crpGenerationContextWrite(ctx, "return"); + if (strcmp(returnType, "void") != 0) + crpGenerationContextWrite(ctx, "%s{}", returnType); + crpGenerationContextWrite(ctx, ";"); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "auto func = reinterpret_cast<%s(*)(%s)>(methodClass->GetCustomDelegate());", returnType, delegateCallArgsTypes); + if (strcmp(returnType, "void") != 0) + crpGenerationContextWrite(ctx, "return"); + crpGenerationContextWrite(ctx, "func(%s)", delegateCallArgs); + crpGenerationContextWrite(ctx, ";"); + } + + crpGenerationContextLeaveScope(ctx, NULL); + } + + crpGenerationContextNewLine(ctx); +} + +static void AfterClassGeneration(const CrpClass* cls, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + + // Register methods inside Initialize() before closing it + size_t methodCount = crpClassGetMethodCount(cls); + for (size_t i = 0; i < methodCount; ++i) + { + const CrpClassMethod* method = crpClassGetMethod(cls, i); + const char* methodName = crpClassMethodGetName(method); + const char* returnType = crpClassMethodGetReturnType(method); + size_t paramCount = crpClassMethodGetParamCount(method); + if (!returnType || returnType[0] == '\0') + returnType = "void"; + + char paramsStr[4096] = ""; + int overflow = 0; + for (size_t j = 0; j < paramCount; ++j) + { + const CrpClassMethodParam* param = crpClassMethodGetParam(method, j); + const char* paramType = crpClassMethodParamGetType(param); + char paramEntry[512]; + const int written = snprintf(paramEntry, sizeof(paramEntry), "cct::refl::GetClassByName(\"\"sv, \"%s\"sv)", paramType ? paramType : ""); + if (written < 0 || (size_t)written >= sizeof(paramEntry)) + overflow = 1; + if (j > 0) + { + if (!AppendToBuffer(paramsStr, sizeof(paramsStr), ", ")) + overflow = 1; + } + if (!AppendToBuffer(paramsStr, sizeof(paramsStr), paramEntry)) + overflow = 1; + } + + if (overflow) + { + fprintf(stderr, "CppPlugin: parameter metadata too long while generating %s::%s\n", + className ? className : "", + methodName ? methodName : ""); + continue; + } + + crpGenerationContextWrite(ctx, "auto* %sMethod = new %s%sMethod(\"%s\"sv, cct::refl::GetClassByName(\"%s\"sv), { %s }, %zu);", + methodName, className, methodName, methodName, returnType, paramsStr, i); + crpGenerationContextWrite(ctx, "AddMemberFunction(std::unique_ptr(%sMethod));", methodName); + } + + // Close Initialize() + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "std::unique_ptr CreateDefaultObject() const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "auto object = std::unique_ptr(new %s);", className); + crpGenerationContextWrite(ctx, "if (object)"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "object->SetDynamicClass(this);"); + crpGenerationContextWrite(ctx, "object->InitializeMemberVariables();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "return object;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "cct::refl::Object* GetMemberVariable(std::size_t index, const cct::refl::Object& self) const override"); + crpGenerationContextEnterScope(ctx); + { + size_t nonNativeIndex = 0; + size_t memberCount = crpClassGetMemberCount(cls); + for (size_t i = 0; i < memberCount; ++i) + { + const CrpClassMember* member = crpClassGetMember(cls, i); + if (crpClassMemberIsNative(member)) + continue; + + const char* memberName = crpClassMemberGetName(member); + crpGenerationContextWrite(ctx, "if (index == %zu)", nonNativeIndex); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return &const_cast<%s&>(static_cast(self)).%s;", className, className, memberName); + crpGenerationContextLeaveScope(ctx, NULL); + nonNativeIndex++; + } + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Invalid index\");"); + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void* GetNativeMemberVariable(std::size_t index, const cct::refl::Object& self) const override"); + crpGenerationContextEnterScope(ctx); + { + size_t nativeIndex = 0; + size_t memberCount = crpClassGetMemberCount(cls); + for (size_t i = 0; i < memberCount; ++i) + { + const CrpClassMember* member = crpClassGetMember(cls, i); + if (!crpClassMemberIsNative(member)) + continue; + + const char* memberName = crpClassMemberGetName(member); + crpGenerationContextWrite(ctx, "if (index == %zu)", nativeIndex); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return &const_cast<%s&>(static_cast(self)).%s;", className, className, memberName); + crpGenerationContextLeaveScope(ctx, NULL); + nativeIndex++; + } + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Invalid index\");"); + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, ";"); + crpGenerationContextNewLine(ctx); +} + +static void BeforeGenericClassGeneration(const CrpClass* cls, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + const char* baseClass = crpClassGetBase(cls); + const char* nsPath = crpGenerationContextGetNamespacePath(ctx); + size_t genericFieldCount = crpClassGetGenericTypeParameterFieldCount(cls); + + crpGenerationContextWrite(ctx, "class Internal%sGenericClass : public cct::refl::GenericClass", className); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%sGenericClass() : cct::refl::GenericClass(nullptr, \"%s\"s, nullptr)", className, className); + crpGenerationContextEnterScope(ctx); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void Initialize() override"); + crpGenerationContextEnterScope(ctx); + { + if (nsPath && nsPath[0] != '\0') + crpGenerationContextWrite(ctx, "SetNamespace(GlobalNamespace::Get().GetNamespaceByName(\"%s\"sv));", nsPath); + + if (baseClass && baseClass[0] != '\0') + { + crpGenerationContextWrite(ctx, "const Class* baseClass = GetClassByName(\"%s\"sv);", baseClass); + crpGenerationContextWrite(ctx, "CCT_ASSERT(baseClass != nullptr, \"Could not find class '%s'\");", baseClass); + crpGenerationContextWrite(ctx, "SetBaseClass(baseClass);"); + } + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "SetTypeParameterCount(%zu);", genericFieldCount); + + for (size_t i = 0; i < genericFieldCount; ++i) + { + const char* fieldName = crpClassGetGenericTypeParameterField(cls, i); + crpGenerationContextWrite(ctx, "AddTypeParameter(\"%s\");", fieldName); + } + + crpGenerationContextNewLine(ctx); + size_t memberCount = crpClassGetMemberCount(cls); + for (size_t i = 0; i < memberCount; ++i) + { + const CrpClassMember* member = crpClassGetMember(cls, i); + const char* memberName = crpClassMemberGetName(member); + const char* memberType = crpClassMemberGetType(member); + int32_t isNative = crpClassMemberIsNative(member); + + if (isNative) + { + crpGenerationContextWrite(ctx, "AddNativeMemberVariable(\"%s\", cct::TypeId<%s>());", memberName, memberType); + } + else + { + crpGenerationContextWrite(ctx, "AddMemberVariable(\"%s\", cct::refl::GetClassByName(\"%s\"sv));", memberName, memberType); + } + } + } + crpGenerationContextLeaveScope(ctx, NULL); + } +} + +static void AfterGenericClassGeneration(const CrpClass* cls, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + size_t genericFieldCount = crpClassGetGenericTypeParameterFieldCount(cls); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "std::unique_ptr CreateDefaultObject() const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Cannot instantiate generic class directly. Use CreateDefaultObject(std::span)\");"); + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "std::unique_ptr CreateDefaultObject("); + crpGenerationContextWrite(ctx, " std::span typeArgs) const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "if (typeArgs.size() != %zu)", genericFieldCount); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Expected %zu type arguments, got %%zu\", typeArgs.size());", genericFieldCount); + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextWrite(ctx, "auto obj = std::make_unique<%s>();", className); + + for (size_t i = 0; i < genericFieldCount; ++i) + { + const char* fieldName = crpClassGetGenericTypeParameterField(cls, i); + crpGenerationContextWrite(ctx, "obj->%s = typeArgs[%zu];", fieldName, i); + } + + crpGenerationContextWrite(ctx, "obj->SetDynamicClass(this);"); + crpGenerationContextWrite(ctx, "obj->InitializeMemberVariables();"); + crpGenerationContextWrite(ctx, "return obj;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "cct::refl::Object* GetMemberVariable(std::size_t, const cct::refl::Object&) const override"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return nullptr;"); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void* GetNativeMemberVariable(std::size_t, const cct::refl::Object&) const override"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return nullptr;"); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, ";"); +} + +static void BeforeTemplateClassGeneration(const CrpClass* cls, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + size_t specCount = crpClassGetTemplateSpecializationCount(cls); + + crpGenerationContextWrite(ctx, "namespace"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "class Internal%sTemplateClass : public ::cct::refl::TemplateClass", className); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%sTemplateClass() : ::cct::refl::TemplateClass(nullptr, \"%s\"s, nullptr)", className, className); + crpGenerationContextEnterScope(ctx); + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void Initialize() override"); + crpGenerationContextEnterScope(ctx); + { + size_t paramCount = crpClassGetTemplateParameterCount(cls); + for (size_t i = 0; i < paramCount; ++i) + { + const CrpTemplateParameter* param = crpClassGetTemplateParameter(cls, i); + const char* paramName = crpTemplateParameterGetName(param); + crpGenerationContextWrite(ctx, "cct::refl::TemplateClass::AddTemplateParameter(\"%s\");", paramName); + } + + if (specCount > 0) + { + crpGenerationContextWrite(ctx, "std::vector typeArgs;"); + } + + for (size_t i = 0; i < specCount; ++i) + { + const char* spec = crpClassGetTemplateSpecialization(cls, i); + if (spec && spec[0] != '\0') + { + char sanitized[512]; + SanitizeSpecialization(spec, sanitized, sizeof(sanitized)); + + crpGenerationContextWrite(ctx, "typeArgs.clear();"); + crpGenerationContextWrite(ctx, "typeArgs.push_back(\"%s\");", spec); + crpGenerationContextWrite(ctx, "RegisterSpecialization(typeArgs, std::make_unique());", className, sanitized); + } + } + } + crpGenerationContextLeaveScope(ctx, NULL); + } + } +} + +static void AfterTemplateClassGeneration(const CrpClass* cls, CrpGenerationContext* ctx) +{ + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "std::unique_ptr<::cct::refl::Object> CreateDefaultObject() const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "CCT_ASSERT_FALSE(\"Cannot instantiate template class directly\");"); + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, ";"); + crpGenerationContextNewLine(ctx); + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); +} + +static void BeforeTemplateSpecializationGeneration(const CrpClass* cls, const char* spec, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + const char* nsPath = crpGenerationContextGetNamespacePath(ctx); + char sanitized[512]; + SanitizeSpecialization(spec, sanitized, sizeof(sanitized)); + + crpGenerationContextWrite(ctx, "class Internal%s_%s_Class : public cct::refl::Class", className, sanitized); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%s_%s_Class() : cct::refl::Class(nullptr, \"%s<%s>\"s, nullptr) {}", className, sanitized, className, spec); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "void Initialize() override"); + crpGenerationContextEnterScope(ctx); + { + if (nsPath && nsPath[0] != '\0') + crpGenerationContextWrite(ctx, "SetNamespace(GlobalNamespace::Get().GetNamespaceByName(\"%s\"sv));", nsPath); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + } +} + +static void AfterTemplateSpecializationGeneration(const CrpClass* cls, const char* spec, CrpGenerationContext* ctx) +{ + const char* className = crpClassGetName(cls); + + crpGenerationContextWrite(ctx, "std::unique_ptr<::cct::refl::Object> CreateDefaultObject() const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "auto obj = std::make_unique<%s<%s>>();", className, spec); + crpGenerationContextWrite(ctx, "obj->SetDynamicClass(this);"); + crpGenerationContextWrite(ctx, "obj->InitializeMemberVariables();"); + crpGenerationContextWrite(ctx, "return obj;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "cct::refl::Object* GetMemberVariable(std::size_t, const cct::refl::Object&) const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "void* GetNativeMemberVariable(std::size_t, const cct::refl::Object&) const override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "return nullptr;"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, ";"); + crpGenerationContextNewLine(ctx); +} + +static void BeforeNamespaceGeneration(const CrpNamespace* ns, CrpGenerationContext* ctx) +{ + const char* nsName = crpNamespaceGetName(ns); + crpGenerationContextWrite(ctx, "namespace %s", nsName); + crpGenerationContextEnterScope(ctx); +} + +static void AfterNamespaceGeneration(const CrpNamespace* ns, CrpGenerationContext* ctx) +{ + const char* nsName = crpNamespaceGetName(ns); + char capitalizedNsName[256]; + Capitalize(nsName, capitalizedNsName, sizeof(capitalizedNsName)); + + const CrpPackage* package = crpGenerationContextGetPackage(ctx); + const char* packageName = crpPackageGetName(package); + + size_t nestedCount = crpNamespaceGetNamespaceCount(ns); + size_t enumCount = crpNamespaceGetEnumCount(ns); + size_t classCount = crpNamespaceGetClassCount(ns); + + crpGenerationContextWrite(ctx, "class Internal%s_%s_Namespace : public cct::refl::Namespace", packageName, capitalizedNsName); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%s_%s_Namespace() : cct::refl::Namespace(\"%s\"s) {}", packageName, capitalizedNsName, nsName); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "~Internal%s_%s_Namespace() override", packageName, capitalizedNsName); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "m_classes.clear();"); + crpGenerationContextWrite(ctx, "m_namespaces.clear();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "void LoadNamespaces() override"); + crpGenerationContextEnterScope(ctx); + { + for (size_t i = 0; i < nestedCount; ++i) + { + const CrpNamespace* nested = crpNamespaceGetNamespace(ns, i); + const char* nestedName = crpNamespaceGetName(nested); + char capitalizedNestedName[256]; + Capitalize(nestedName, capitalizedNestedName, sizeof(capitalizedNestedName)); + crpGenerationContextWrite(ctx, "AddNamespace(%s::%s::Create%s_%s_NamespaceInstance());", + nsName, nestedName, packageName, capitalizedNestedName); + } + crpGenerationContextWrite(ctx, "for (auto& ns : m_namespaces)"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "ns->LoadNamespaces();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "void LoadClasses() override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "for(auto& ns : m_namespaces)"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "ns->LoadClasses();"); + crpGenerationContextLeaveScope(ctx, NULL); + + for (size_t i = 0; i < enumCount; ++i) + { + const CrpEnum* enm = crpNamespaceGetEnum(ns, i); + const char* enumName = crpEnumGetName(enm); + crpGenerationContextWrite(ctx, "AddClass(std::make_unique<%s::Internal%sEnumerationClass>());", nsName, enumName); + } + + for (size_t i = 0; i < classCount; ++i) + { + const CrpClass* cls = crpNamespaceGetClass(ns, i); + const char* className = crpClassGetName(cls); + if (crpClassIsGeneric(cls)) + { + crpGenerationContextWrite(ctx, "AddClass(std::make_unique<%s::Internal%sGenericClass>());", nsName, className); + } + else if (crpClassIsTemplate(cls)) + { + crpGenerationContextWrite(ctx, "AddClass(std::make_unique<%s::Internal%sTemplateClass>());", nsName, className); + + size_t specCount = crpClassGetTemplateSpecializationCount(cls); + for (size_t j = 0; j < specCount; ++j) + { + const char* spec = crpClassGetTemplateSpecialization(cls, j); + if (spec && spec[0] != '\0') + { + char sanitized[512]; + SanitizeSpecialization(spec, sanitized, sizeof(sanitized)); + crpGenerationContextWrite(ctx, "AddClass(std::make_unique<%s::Internal%s_%s_Class>());", nsName, className, sanitized); + } + } + } + else + { + crpGenerationContextWrite(ctx, "AddClass(std::make_unique<%s::Internal%sClass>());", nsName, className); + } + } + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "void InitializeClasses() override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "for (auto& ns : m_namespaces)"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "ns->InitializeClasses();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextWrite(ctx, "for (auto& klass : m_classes)"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "klass->Initialize();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + } + crpGenerationContextLeaveScope(ctx, NULL); + } + crpGenerationContextLeaveScope(ctx, ";"); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "std::unique_ptr Create%s_%s_NamespaceInstance()", packageName, capitalizedNsName); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "return std::make_unique();", packageName, capitalizedNsName); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, NULL); +} + +static void BeforePackageGeneration(const CrpPackage* package, CrpGenerationContext* ctx) +{ + const char* packageName = crpPackageGetName(package); + + crpGenerationContextWrite(ctx, "// This file was automatically generated, do not edit"); + crpGenerationContextWrite(ctx, "#include \"%sPackage.gen.hpp\"", packageName); + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextNewLine(ctx); + + size_t headerCount = crpGenerationContextGetHeaderCount(ctx); + for (size_t i = 0; i < headerCount; ++i) + { + const char* header = crpGenerationContextGetHeader(ctx, i); + crpGenerationContextWrite(ctx, "#include <%s>", header); + } + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "using namespace std::string_literals;"); + crpGenerationContextWrite(ctx, "using namespace std::string_view_literals;"); + crpGenerationContextWrite(ctx, "using namespace cct::refl;"); + crpGenerationContextNewLine(ctx); +} + +static void AfterPackageGeneration(const CrpPackage* package, CrpGenerationContext* ctx) +{ + const char* packageName = crpPackageGetName(package); + size_t classCount = crpPackageGetClassCount(package); + size_t nsCount = crpPackageGetNamespaceCount(package); + + crpGenerationContextWrite(ctx, "class Internal%sPackage : public cct::refl::Package", packageName); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "public:"); + crpGenerationContextWrite(ctx, "Internal%sPackage() : cct::refl::Package(\"%s\"s) {}", packageName, packageName); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "~Internal%sPackage() override", packageName); + crpGenerationContextEnterScope(ctx); + { + for (size_t i = 0; i < classCount; ++i) + { + const CrpClass* cls = crpPackageGetClass(package, i); + const char* className = crpClassGetName(cls); + crpGenerationContextWrite(ctx, "GlobalNamespace::Get().RemoveClass(\"%s\"sv);", className); + } + + for (size_t i = 0; i < nsCount; ++i) + { + const CrpNamespace* ns = crpPackageGetNamespace(package, i); + const char* nsName = crpNamespaceGetName(ns); + crpGenerationContextWrite(ctx, "GlobalNamespace::Get().RemoveNamespace(\"%s\"sv);", nsName); + } + crpGenerationContextWrite(ctx, "m_namespaces.clear();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void LoadNamespaces() override"); + crpGenerationContextEnterScope(ctx); + { + for (size_t i = 0; i < nsCount; ++i) + { + const CrpNamespace* ns = crpPackageGetNamespace(package, i); + const char* nsName = crpNamespaceGetName(ns); + char capitalizedNsName[256]; + Capitalize(nsName, capitalizedNsName, sizeof(capitalizedNsName)); + crpGenerationContextWrite(ctx, "AddNamespace(%s::Create%s_%s_NamespaceInstance());", nsName, packageName, capitalizedNsName); + } + crpGenerationContextWrite(ctx, "for (auto& ns : m_namespaces)"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "ns->LoadNamespaces();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void InitializeNamespaces() override"); + crpGenerationContextEnterScope(ctx); + { + for (size_t i = 0; i < classCount; ++i) + { + const CrpClass* cls = crpPackageGetClass(package, i); + const char* className = crpClassGetName(cls); + + if (crpClassIsGeneric(cls)) + crpGenerationContextWrite(ctx, "AddClass(std::make_unique());", className); + else if (crpClassIsTemplate(cls)) + { + crpGenerationContextWrite(ctx, "AddClass(std::make_unique());", className); + size_t specCount = crpClassGetTemplateSpecializationCount(cls); + for (size_t j = 0; j < specCount; ++j) + { + const char* spec = crpClassGetTemplateSpecialization(cls, j); + if (spec && spec[0] != '\0') + { + char sanitized[512]; + SanitizeSpecialization(spec, sanitized, sizeof(sanitized)); + crpGenerationContextWrite(ctx, "AddClass(std::make_unique());", className, sanitized); + } + } + } + else + crpGenerationContextWrite(ctx, "AddClass(std::make_unique());", className); + } + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "for (auto& ns : m_namespaces)"); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "ns->LoadClasses();"); + crpGenerationContextLeaveScope(ctx, NULL); + } + crpGenerationContextLeaveScope(ctx, NULL); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "void InitializeClasses() override"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "for (auto& ns : m_namespaces)"); + crpGenerationContextEnterScope(ctx); + { + crpGenerationContextWrite(ctx, "ns->InitializeClasses();"); + } + crpGenerationContextLeaveScope(ctx, NULL); + } + crpGenerationContextLeaveScope(ctx, NULL); + crpGenerationContextLeaveScope(ctx, ";"); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "std::unique_ptr Create%sPackage()", packageName); + crpGenerationContextEnterScope(ctx); + crpGenerationContextWrite(ctx, "return std::make_unique();", packageName); + } + crpGenerationContextLeaveScope(ctx, NULL); +} + +static const CrpPluginFunctionTable g_pluginTable = { + .apiVersion = CRP_PLUGIN_API_VERSION, + .GetInfo = GetInfo, + .GetPriority = NULL, + .OnLoad = OnLoad, + .OnUnload = OnUnload, + .TransformPackage = NULL, + + .BeforePackageGeneration = BeforePackageGeneration, + .OnPackageGeneration = NULL, + .AfterPackageGeneration = AfterPackageGeneration, + + .BeforeNamespaceGeneration = BeforeNamespaceGeneration, + .OnNamespaceGeneration = NULL, + .AfterNamespaceGeneration = AfterNamespaceGeneration, + + .BeforeClassGeneration = BeforeClassGeneration, + .OnClassGeneration = NULL, + .AfterClassGeneration = AfterClassGeneration, + + .BeforeMemberGeneration = NULL, + .OnMemberGeneration = OnMemberGeneration, + .AfterMemberGeneration = NULL, + + .BeforeMethodGeneration = BeforeMethodGeneration, + .OnMethodGeneration = OnMethodGeneration, + .AfterMethodGeneration = AfterMethodGeneration, + + .BeforeEnumGeneration = BeforeEnumGeneration, + .OnEnumGeneration = NULL, + .AfterEnumGeneration = AfterEnumGeneration, + + .BeforeEnumElementGeneration = NULL, + .OnEnumElementGeneration = OnEnumElementGeneration, + .AfterEnumElementGeneration = NULL, + + .BeforeTemplateClassGeneration = BeforeTemplateClassGeneration, + .OnTemplateClassGeneration = NULL, + .AfterTemplateClassGeneration = AfterTemplateClassGeneration, + + .BeforeGenericClassGeneration = BeforeGenericClassGeneration, + .OnGenericClassGeneration = NULL, + .AfterGenericClassGeneration = AfterGenericClassGeneration, + + .BeforeTemplateSpecializationGeneration = BeforeTemplateSpecializationGeneration, + .OnTemplateSpecializationGeneration = NULL, + .AfterTemplateSpecializationGeneration = AfterTemplateSpecializationGeneration, + + .HandleAnnotation = NULL, + .GenerateFile = NULL, +}; + +// Entry point +CRP_DECLARE_PLUGIN_ENTRY(g_pluginTable) diff --git a/Src/Concerto/HeaderPlugin/HeaderPlugin.c b/Src/Concerto/HeaderPlugin/HeaderPlugin.c new file mode 100644 index 0000000..f69a966 --- /dev/null +++ b/Src/Concerto/HeaderPlugin/HeaderPlugin.c @@ -0,0 +1,222 @@ +// +// HeaderPlugin.c - Generates .gen.hpp header files +// + +#include +#include +#include +#include +#include + +#include "Concerto/PackageGenerator/Plugin/PluginApi.h" + +typedef struct HeaderPluginData +{ + int reserved; +} HeaderPluginData; + +// Plugin info +static void GetInfo(CrpPluginInfo* outInfo) +{ + outInfo->name = "HeaderPlugin"; + outInfo->version = "1.0.0"; + outInfo->author = "Concerto"; + outInfo->description = "Generates .gen.hpp header files for reflection"; + outInfo->outputFileExtension = ".gen.hpp"; +} + +static int32_t OnLoad(void** outPrivateData) +{ + HeaderPluginData* data = (HeaderPluginData*)calloc(1, sizeof(HeaderPluginData)); + if (!data) + return 0; + if (outPrivateData) + *outPrivateData = data; + return 1; +} + +static void OnUnload(void* privateData) +{ + free(privateData); +} + +static void ToUpper(const char* src, char* dest, size_t destSize) +{ + size_t i = 0; + while (src[i] && i < destSize - 1) + { + if (src[i] >= 'a' && src[i] <= 'z') + dest[i] = src[i] - 32; + else + dest[i] = src[i]; + i++; + } + dest[i] = '\0'; +} + +static void Capitalize(const char* src, char* dest, size_t destSize) +{ + if (destSize == 0) + return; + + if (src[0] >= 'a' && src[0] <= 'z') + dest[0] = src[0] - 32; + else + dest[0] = src[0]; + + size_t i = 1; + while (src[i] && i < destSize - 1) + { + dest[i] = src[i]; + i++; + } + dest[i] = '\0'; +} + +static void GetApiMacro(CrpGenerationContext* ctx, char* upperName, size_t upperNameSize) +{ + const CrpPackage* package = crpGenerationContextGetPackage(ctx); + const char* packageName = crpPackageGetName(package); + ToUpper(packageName, upperName, upperNameSize); +} + +// --- Hooks --- + +static void BeforePackageGeneration(const CrpPackage* package, CrpGenerationContext* ctx) +{ + const char* packageName = crpPackageGetName(package); + + char upperName[256]; + ToUpper(packageName, upperName, sizeof(upperName)); + + // Header comment and pragma once + crpGenerationContextWrite(ctx, "//This file was automatically generated, do not edit"); + crpGenerationContextWrite(ctx, "#pragma once"); + crpGenerationContextNewLine(ctx); + + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + crpGenerationContextWrite(ctx, "#include "); + + // API macro definition + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "#ifdef %sPACKAGE_STATIC", upperName); + crpGenerationContextWrite(ctx, "\t#define %sPACKAGE_API", upperName); + crpGenerationContextWrite(ctx, "#else"); + crpGenerationContextWrite(ctx, "\t#ifdef %sPACKAGE_BUILD", upperName); + crpGenerationContextWrite(ctx, "\t\t#define %sPACKAGE_API CCT_EXPORT", upperName); + crpGenerationContextWrite(ctx, "\t#else"); + crpGenerationContextWrite(ctx, "\t\t#define %sPACKAGE_API CCT_IMPORT", upperName); + crpGenerationContextWrite(ctx, "\t#endif"); + crpGenerationContextWrite(ctx, "#endif"); + crpGenerationContextNewLine(ctx); +} + +static void AfterPackageGeneration(const CrpPackage* package, CrpGenerationContext* ctx) +{ + const char* packageName = crpPackageGetName(package); + + char upperName[256]; + ToUpper(packageName, upperName, sizeof(upperName)); + + crpGenerationContextNewLine(ctx); + crpGenerationContextWrite(ctx, "%sPACKAGE_API std::unique_ptr Create%sPackage();", + upperName, packageName); +} + +static void BeforeNamespaceGeneration(const CrpNamespace* ns, CrpGenerationContext* ctx) +{ + const char* namespaceName = crpNamespaceGetName(ns); + crpGenerationContextWrite(ctx, "namespace %s", namespaceName); + crpGenerationContextEnterScope(ctx); +} + +static void AfterNamespaceGeneration(const CrpNamespace* ns, CrpGenerationContext* ctx) +{ + crpGenerationContextNewLine(ctx); + crpGenerationContextLeaveScope(ctx, NULL); +} + +static void BeforeEnumGeneration(const CrpEnum* enm, CrpGenerationContext* ctx) +{ + const char* enumName = crpEnumGetName(enm); + const char* enumBase = crpEnumGetBase(enm); + + char upperName[256]; + GetApiMacro(ctx, upperName, sizeof(upperName)); + + char capitalizedName[256]; + Capitalize(enumName, capitalizedName, sizeof(capitalizedName)); + + crpGenerationContextWrite(ctx, "enum class %s : %s;", enumName, enumBase); + crpGenerationContextWrite(ctx, "%sPACKAGE_API std::string_view %sToString(%s value);", upperName, capitalizedName, enumName); + crpGenerationContextWrite(ctx, "%sPACKAGE_API %s %sFromString(std::string_view str);", upperName, enumName, capitalizedName); +} + +static int32_t GetPriority(void) +{ + // Header plugin should run with higher priority so .gen.hpp is generated first + return 100; +} + +// Function table +static const CrpPluginFunctionTable g_pluginTable = { + .apiVersion = CRP_PLUGIN_API_VERSION, + .GetInfo = GetInfo, + .GetPriority = GetPriority, + .OnLoad = OnLoad, + .OnUnload = OnUnload, + .TransformPackage = NULL, + + .BeforePackageGeneration = BeforePackageGeneration, + .OnPackageGeneration = NULL, + .AfterPackageGeneration = AfterPackageGeneration, + + .BeforeNamespaceGeneration = BeforeNamespaceGeneration, + .OnNamespaceGeneration = NULL, + .AfterNamespaceGeneration = AfterNamespaceGeneration, + + .BeforeClassGeneration = NULL, + .OnClassGeneration = NULL, + .AfterClassGeneration = NULL, + + .BeforeMemberGeneration = NULL, + .OnMemberGeneration = NULL, + .AfterMemberGeneration = NULL, + + .BeforeMethodGeneration = NULL, + .OnMethodGeneration = NULL, + .AfterMethodGeneration = NULL, + + .BeforeEnumGeneration = BeforeEnumGeneration, + .OnEnumGeneration = NULL, + .AfterEnumGeneration = NULL, + + .BeforeEnumElementGeneration = NULL, + .OnEnumElementGeneration = NULL, + .AfterEnumElementGeneration = NULL, + + .BeforeTemplateClassGeneration = NULL, + .OnTemplateClassGeneration = NULL, + .AfterTemplateClassGeneration = NULL, + + .BeforeGenericClassGeneration = NULL, + .OnGenericClassGeneration = NULL, + .AfterGenericClassGeneration = NULL, + + .BeforeTemplateSpecializationGeneration = NULL, + .OnTemplateSpecializationGeneration = NULL, + .AfterTemplateSpecializationGeneration = NULL, + + .HandleAnnotation = NULL, + .GenerateFile = NULL, +}; + +// Entry point +CRP_DECLARE_PLUGIN_ENTRY(g_pluginTable) diff --git a/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp b/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp index 97a6f57..2fc2199 100644 --- a/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp +++ b/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -67,7 +69,7 @@ namespace Namespace* EnsureNamespace(Package& pkg, const std::vector& chain) { Namespace* current = nullptr; - auto* level = &pkg.namepsaces; + auto* level = &pkg.namespaces; for (const auto& name : chain) { auto it = std::ranges::find_if(*level, [&](const Namespace& ns) @@ -147,33 +149,33 @@ namespace return true; } - void InsertClassIntoPackage(Package& pkg, const std::vector& nsChain, const Class& cls) + void InsertClassIntoPackage(Package& pkg, const std::vector& nsChain, const Class& klass) { if (nsChain.empty()) { - pkg.classes.push_back(cls); + pkg.classes.push_back(klass); return; } Namespace* leaf = EnsureNamespace(pkg, nsChain); if (leaf) - leaf->classes.push_back(cls); + leaf->classes.push_back(klass); } - void InsertEnumIntoPackage(Package& pkg, const std::vector& nsChain, const Enum& enm) + void InsertEnumIntoPackage(Package& pkg, const std::vector& nsChain, const Enum& enumeration) { if (nsChain.empty()) { - pkg.enums.push_back(enm); + pkg.enums.push_back(enumeration); return; } Namespace* leaf = EnsureNamespace(pkg, nsChain); if (leaf) { auto it = std::ranges::find_if(leaf->enums, [&](const Enum& e) - { return e.name == enm.name; }); + { return e.name == enumeration.name; }); if (it == leaf->enums.end()) - leaf->enums.push_back(enm); + leaf->enums.push_back(enumeration); } } } // namespace @@ -207,7 +209,20 @@ namespace cct for (auto& include : includeDirs) args.emplace_back("-I" + include); + auto diagOpts = llvm::IntrusiveRefCntPtr(new DiagnosticOptions()); + diagOpts->ShowColors = true; + TextDiagnosticPrinter diagPrinter(llvm::outs(), diagOpts.get()); + + std::unique_ptr AST; + { std::unique_ptr AST = buildASTFromCodeWithArgs(code, args); + AST = buildASTFromCodeWithArgs(code, args, "input.cc", "clang-tool", + std::make_shared(), + getClangStripDependencyFileAdjuster(), + FileContentMappings(), + &diagPrinter); + } + if (!AST) { Logger::Error("LibTooling: failed to parse"); @@ -222,7 +237,7 @@ namespace cct for (const auto* D : TU->decls()) ProcessDeclaration(D); - RemoveEmptyNamespaces(m_package.namepsaces); + RemoveEmptyNamespaces(m_package.namespaces); return &m_package; } @@ -484,8 +499,8 @@ namespace cct ProcessRecord(RD2); else if (const auto* ED2 = llvm::dyn_cast(SD)) ProcessEnum(ED2); - else if (const auto* ND = llvm::dyn_cast(declaration)) - ProcessNamespace(ND); + else if (const auto* ND2 = llvm::dyn_cast(SD)) + ProcessNamespace(ND2); } } } @@ -693,4 +708,4 @@ namespace cct ++it; } } -} // namespace cct \ No newline at end of file +} // namespace cct diff --git a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp b/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp deleted file mode 100644 index fdeac55..0000000 --- a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp +++ /dev/null @@ -1,825 +0,0 @@ -// -// Created by arthur on 09/12/2024. -// - -#include "Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp" - -#include - -namespace cct -{ - using namespace std::string_view_literals; - using namespace std::string_literals; - - bool CppGenerator::Generate(const Package& package, std::span args) - { - Write("//This file was automatically generated, do not edit"); - Write("#include "); - Write("#include "); - Write("#include \"Concerto/Reflection/GlobalNamespace/GlobalNamespace.hpp\""); - Write("#include "); - Write("#include "); - Write("#include \"{}Package.gen.hpp\"", package.name); - - for (auto& header : args) - Write("#include \"{}\"", header); - - Write("using namespace std::string_view_literals;"); - Write("using namespace std::string_literals;"); - Write("using namespace cct;"); - Write("using namespace cct::refl;"); - - for (auto& enum_ : package.enums) - GenerateEnum(enum_, ""); - for (auto& klass : package.classes) - { - if (klass.isGenericClass) - GenerateGenericClass({}, klass); - else if (klass.isTemplateClass) - GenerateTemplateClass({}, klass); - else - GenerateClass({}, klass); - } - for (auto& ns : package.namepsaces) - GenerateNamespace(ns); - GeneratePackage(package); - return true; - } - - void CppGenerator::GenerateNamespace(const Namespace& ns, const std::string& namespaceChain) - { - Write("namespace {}", ns.name); - EnterScope(); - { - for (auto& nestedNs : ns.namespaces) - GenerateNamespace(nestedNs, namespaceChain + "::"s + std::string(ns.name)); - for (auto& enum_ : ns.enums) - GenerateEnum(enum_, namespaceChain + "::"s + std::string(ns.name)); - for (auto& klass : ns.classes) - { - if (klass.isGenericClass) - GenerateGenericClass(namespaceChain + "::"s + std::string(ns.name), klass); - else if (klass.isTemplateClass) - GenerateTemplateClass(namespaceChain + "::"s + std::string(ns.name), klass); - else - GenerateClass(namespaceChain + "::"s + std::string(ns.name), klass); - } - } - LeaveScope(); - Write("namespace"); - EnterScope(); - { - Write("class Internal{}Namespace : public cct::refl::Namespace", ns.name); - EnterScope(); - { - Write("public:"); - Write("Internal{}Namespace() : cct::refl::Namespace(\"{}\"s) {{}}", ns.name, ns.name); - NewLine(); - Write("~Internal{}Namespace() override", ns.name); - EnterScope(); - { - Write("m_classes.clear();"); - Write("m_namespaces.clear();"); - } - LeaveScope(); - NewLine(); - Write("void LoadNamespaces() override"); - EnterScope(); - { - for (auto& nestedNs : ns.namespaces) - Write("AddNamespace({}::{}::Create{}NamespaceInstance());", namespaceChain, ns.name, nestedNs.name); - Write("for (auto& ns : m_namespaces)"); - EnterScope(); - { - Write("ns->LoadNamespaces();"); - } - LeaveScope(); - } - LeaveScope(); - NewLine(); - Write("void LoadClasses() override"); - EnterScope(); - { - Write("for(auto& ns : m_namespaces)"); - EnterScope(); - Write("ns->LoadClasses();"); - LeaveScope(); - for (auto& enum_ : ns.enums) - { - Write("AddClass(std::make_unique<{}::Internal{}EnumerationClass>());", ns.name, enum_.name); - } - for (auto& klass : ns.classes) - { - if (klass.isGenericClass) - Write("AddClass(std::make_unique<{}::Internal{}GenericClass>());", ns.name, klass.name); - else if (klass.isTemplateClass) - { - Write("AddClass(std::make_unique<{}::Internal{}TemplateClass>());", ns.name, klass.name); - for (const auto& specialization : klass.templateSpecializations) - { - if (specialization.empty()) - continue; - std::string specName = specialization; - for (auto& c : specName) - { - if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) - c = '_'; - } - Write("AddClass(std::make_unique<{}::Internal{}_{}_Class>());", ns.name, klass.name, specName); - } - } - else - Write("AddClass(std::make_unique<{}::Internal{}Class>());", ns.name, klass.name); - } - } - LeaveScope(); - NewLine(); - Write("void InitializeClasses() override"); - EnterScope(); - { - Write("for (auto& ns : m_namespaces)"); - EnterScope(); - Write("ns->InitializeClasses();"); - LeaveScope(); - Write("for (auto& klass : m_classes)"); - EnterScope(); - Write("klass->Initialize();"); - LeaveScope(); - } - LeaveScope(); - NewLine(); - } - LeaveScope(";"sv); - Write("std::unique_ptr Create{}NamespaceInstance(){{return std::make_unique();}}", ns.name, ns.name); - } - LeaveScope(); - } - - void CppGenerator::GenerateClass(std::string_view ns, const Class& klass) - { - std::size_t methodIndex = 0; - for (auto& method : klass.methods) - { - GenerateClassMethod(klass.name, method, ns, methodIndex); - ++methodIndex; - } - NewLine(); - Write("class Internal{}Class : public cct::refl::Class", klass.name); - EnterScope(); - { - Write("public:"); - Write("Internal{0}Class() : cct::refl::Class(nullptr, \"{0}\"s, nullptr)", klass.name); - EnterScope(); - { - Write("if ({}::m_class != nullptr)", klass.name); - EnterScope(); - { - Write("CCT_ASSERT_FALSE(\"Class already created\");"); - Write("return;"); - } - LeaveScope(); - Write("{}::m_class = this;", klass.name); - } - LeaveScope(); - Write("~Internal{}Class() override", klass.name); - EnterScope(); - { - Write("{}::m_class = nullptr;", klass.name); - } - LeaveScope(); - NewLine(); - Write("void Initialize() override"); - EnterScope(); - { - Write("SetNamespace(GlobalNamespace::Get().GetNamespaceByName(\"{}\"sv));", ns); - if (!klass.base.empty()) - { - Write("const Class* baseClass = GetClassByName(\"{}\"sv);", klass.base); - Write("CCT_ASSERT(baseClass != nullptr, \"Could not find class '{}'\");", klass.base); - Write("SetBaseClass(baseClass);"); - } - NewLine(); - for (auto& member : klass.members) - if (member.isNative) - Write(R"(AddNativeMemberVariable("{}", cct::TypeId<{}>());)", member.name, member.type); - else - { - auto removeAllColons = [](std::string_view s) - { - std::string result; - for (char c : s) - { - if (c != ':') - result += c; - } - return result; - }; - std::string type = removeAllColons(member.type); - Write(R"(const cct::refl::Class* {}Class = cct::refl::GetClassByName("{}::{}"sv);)", type, ns, member.type); - Write(R"(if ({}Class == nullptr))", type); - EnterScope(); - { - Write(R"({}Class = cct::refl::GetClassByName("{}"sv);)", type, member.type); - } - LeaveScope(); - Write(R"(CCT_ASSERT({}Class != nullptr, "Could not find class '{}'" );)", type, member.type); - Write(R"(AddMemberVariable("{}", {}Class);)", member.name, type); - } - NewLine(); - std::size_t i = 0; - for (auto& method : klass.methods) - { - std::string params; - for (auto& param : method.params) - params += std::format(R"(cct::refl::GetClassByName(""sv, "{}"sv), )", param.type); - Write(R"(auto* {}Method = new {}{}Method("{}"sv, cct::refl::GetClassByName("{}"sv), {{ {} }}, {});)", method.name, klass.name, method.name, method.name, method.returnValue, params, i); - Write("AddMemberFunction(std::unique_ptr({}Method));", method.name); - ++i; - } - - // for (auto& [name, value] : klass.attributes) - // Write(R"(AddAttribute("{}"s, "{}"s);)", name, value); - } - LeaveScope(); - NewLine(); - Write("std::unique_ptr CreateDefaultObject() const override"); - EnterScope(); - { - Write("auto object = std::unique_ptr(new {});", klass.name); - Write("if (object)"); - EnterScope(); - { - Write("object->SetDynamicClass(this);", klass.name); - Write("object->InitializeMemberVariables();"); - } - LeaveScope(); - Write(" return object;"); - LeaveScope(); - } - NewLine(); - Write("cct::refl::Object* GetMemberVariable(std::size_t index, const cct::refl::Object& self) const override"); - EnterScope(); - { - std::size_t i = 0; - for (auto& member : klass.members) - { - if (member.isNative) - continue; - Write("if (index == {})", i); - EnterScope(); - { - Write("return &const_cast<{0}&>(static_cast(self)).{1};", klass.name, member.name); - } - LeaveScope(); - ++i; - } - Write("CCT_ASSERT_FALSE(\"Invalid index\");"); - Write("return nullptr;"); - } - LeaveScope(); - NewLine(); - Write("void* GetNativeMemberVariable(std::size_t index, const cct::refl::Object& self) const override"); - EnterScope(); - { - std::size_t i = 0; - for (auto& member : klass.members) - { - if (member.isNative == false) - continue; - Write("if (index == {})", i); - EnterScope(); - { - Write("return &const_cast<{0}&>(static_cast(self)).{1};", klass.name, member.name); - } - LeaveScope(); - ++i; - } - Write("CCT_ASSERT_FALSE(\"Invalid index\");"); - Write("return nullptr;"); - } - LeaveScope(); - NewLine(); - } - LeaveScope(";"sv); - NewLine(); - } - - void CppGenerator::GenerateGenericClass(std::string_view ns, const Class& klass) - { - Write("class Internal{}GenericClass : public cct::refl::GenericClass", klass.name); - EnterScope(); - { - Write("public:"); - Write("Internal{}GenericClass() : cct::refl::GenericClass(nullptr, \"{}\"s, nullptr)", - klass.name, klass.name); - EnterScope(); - LeaveScope(); - - NewLine(); - Write("void Initialize() override"); - EnterScope(); - { - if (!ns.empty()) - Write("SetNamespace(GlobalNamespace::Get().GetNamespaceByName(\"{}\"sv));", ns); - - if (!klass.base.empty()) - { - Write("const Class* baseClass = GetClassByName(\"{}\"sv);", klass.base); - Write("CCT_ASSERT(baseClass != nullptr, \"Could not find class '{}'\");", klass.base); - Write("SetBaseClass(baseClass);"); - } - - NewLine(); - Write("SetTypeParameterCount({});", klass.genericTypeParameterFields.size()); - - for (const auto& fieldName : klass.genericTypeParameterFields) - Write("AddTypeParameter(\"{}\");", fieldName); - - NewLine(); - // Add members and methods as in regular class - for (auto& member : klass.members) - { - if (member.isNative) - Write(R"(AddNativeMemberVariable("{}", cct::TypeId<{}>());)", member.name, member.type); - else - Write(R"(AddMemberVariable("{}", cct::refl::GetClassByName("{}"sv));)", member.name, member.type); - } - } - LeaveScope(); - - NewLine(); - Write("std::unique_ptr CreateDefaultObject() const override"); - EnterScope(); - { - Write("CCT_ASSERT_FALSE(\"Cannot instantiate generic class directly. Use CreateDefaultObject(std::span)\");"); - Write("return nullptr;"); - } - LeaveScope(); - - NewLine(); - Write("std::unique_ptr CreateDefaultObject("); - Write(" std::span typeArgs) const override"); - EnterScope(); - { - Write("if (typeArgs.size() != {})", klass.genericTypeParameterFields.size()); - EnterScope(); - { - Write("CCT_ASSERT_FALSE(\"Expected {} type arguments, got {{}}\", typeArgs.size());", - klass.genericTypeParameterFields.size()); - Write("return nullptr;"); - } - LeaveScope(); - - if (ns.empty()) - Write("auto obj = std::make_unique<{}>();", klass.name); - else - Write("auto obj = std::make_unique<{}::{}>();", ns, klass.name); - - // Inject type parameters - for (std::size_t i = 0; i < klass.genericTypeParameterFields.size(); ++i) - Write("obj->{} = typeArgs[{}];", klass.genericTypeParameterFields[i], i); - - Write("obj->SetDynamicClass(this);"); - Write("obj->InitializeMemberVariables();"); - Write("return obj;"); - } - LeaveScope(); - - NewLine(); - Write("cct::refl::Object* GetMemberVariable(std::size_t, const cct::refl::Object&) const override"); - EnterScope(); - { - Write("return nullptr;"); - } - LeaveScope(); - - NewLine(); - Write("void* GetNativeMemberVariable(std::size_t, const cct::refl::Object&) const override"); - EnterScope(); - { - Write("return nullptr;"); - } - LeaveScope(); - } - LeaveScope(";"); - } - - void CppGenerator::GenerateTemplateClass(std::string_view ns, const Class& klass) - { - Write("namespace"); - EnterScope(); - { - for (const auto& specialization : klass.templateSpecializations) - { - if (specialization.empty()) - continue; - - // Create a sanitized name for the specialization class - std::string specName = specialization; - for (auto& c : specName) - { - if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) - c = '_'; - } - - Write("class Internal{}_{}_Class : public ::cct::refl::Class", klass.name, specName); - EnterScope(); - { - Write("public:"); - Write("Internal{}_{}_Class() : ::cct::refl::Class(nullptr, \"{}\"s, nullptr)", klass.name, specName, std::format("{}<{}>", klass.name, specialization)); - EnterScope(); - LeaveScope(); - NewLine(); - Write("void Initialize() override"); - EnterScope(); - { - // Specialization classes initialize their template instance - } - LeaveScope(); - NewLine(); - Write("::cct::refl::Object* GetMemberVariable(std::size_t index, const ::cct::refl::Object& self) const override"); - EnterScope(); - { - Write("return nullptr;"); - } - LeaveScope(); - NewLine(); - Write("void* GetNativeMemberVariable(std::size_t index, const ::cct::refl::Object& self) const override"); - EnterScope(); - { - Write("return nullptr;"); - } - LeaveScope(); - NewLine(); - Write("std::unique_ptr<::cct::refl::Object> CreateDefaultObject() const override"); - EnterScope(); - { - if (ns.empty()) - { - Write("auto obj = std::make_unique<{}{}>();", klass.name, std::format("<{}>", specialization)); - } - else - { - Write("auto obj = std::make_unique<{}::{}{}>();", ns, klass.name, std::format("<{}>", specialization)); - } - Write("return obj;"); - } - LeaveScope(); - } - LeaveScope(";"); - NewLine(); - } - - Write("class Internal{}TemplateClass : public ::cct::refl::TemplateClass", klass.name); - EnterScope(); - { - Write("public:"); - Write("Internal{}TemplateClass() : ::cct::refl::TemplateClass(nullptr, \"{}\"s, nullptr)", klass.name, klass.name); - EnterScope(); - LeaveScope(); - NewLine(); - Write("void Initialize() override"); - EnterScope(); - { - for (const auto& param : klass.templateParameters) - Write("cct::refl::TemplateClass::AddTemplateParameter(\"{}\");", param.name); - - if (!klass.templateSpecializations.empty()) - Write("std::vector typeArgs;"); - - for (const auto& specialization : klass.templateSpecializations) - { - if (specialization.empty()) - continue; - - // Create sanitized name - std::string specName = specialization; - for (auto& c : specName) - { - if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) - c = '_'; - } - - Write("typeArgs.clear();"); - Write("typeArgs.push_back(\"{}\");", specialization); - Write("RegisterSpecialization(typeArgs, std::make_unique({}));", - klass.name, specName, ""); - } - } - LeaveScope(); - NewLine(); - Write("std::unique_ptr<::cct::refl::Object> CreateDefaultObject() const override"); - EnterScope(); - { - Write("CCT_ASSERT_FALSE(\"Cannot instantiate template class directly\");"); - Write("return nullptr;"); - } - LeaveScope(); - } - LeaveScope(";"); - NewLine(); - } - LeaveScope(); - NewLine(); - } - - void CppGenerator::GenerateClassMethod(std::string_view className, const Class::Method& method, std::string_view ns, std::size_t methodIndex) - { - std::string base; - if (method.tomlAttributes.is_table() && method.tomlAttributes.as_table().contains("Base")) - { - auto it = method.tomlAttributes.as_table().find("Base"); - if (it->second.is_string()) - base = it->second.as_string(); - else - cct::Logger::Error("Invalid base class for method: {}::{}", className, method.name); - } - auto baseClass = base.empty() ? "cct::refl::Method"sv : base; - Write("class {}{}Method : public {}", className, method.name, baseClass); - EnterScope(); - { - Write("public:"); - Write("using BaseClass = {};", baseClass); - Write("using BaseClass::BaseClass;"); - NewLine(); - Write("void Initialize() override"); - EnterScope(); - // for (auto& [name, value] : method.attributes) - // Write(R"(AddAttribute("{}"s, "{}"s);)", name, value); - LeaveScope(); - NewLine(); - Write("cct::Result Invoke(cct::refl::Object& self, std::span parameters) const override"); - EnterScope(); - { - Write("if (parameters.size() != {})", method.params.size()); - EnterScope(); - { - Write("CCT_ASSERT_FALSE(\"Invalid parameters size\");"); - Write("return {{\"Invalid parameters size\"s}};"); - } - LeaveScope(); - std::size_t i = 0; - std::string callArgs; - std::string callArgsTypes; - for (auto& param : method.params) - { - if (i != 0 && i < method.params.size()) - { - callArgs += ", "; - callArgsTypes += ", "; - } - - std::string_view type = param.type; - for (auto& s : {"const"sv}) - if (type.starts_with(s)) - type = type.substr(s.size()); - - Write("using Param{0} = {1};", i, type); - Write("if (parameters[{0}].Is() == false)", i); - EnterScope(); - { - Write("CCT_ASSERT_FALSE(\"Expected '{}' in argument {}\");", param.type, i); - Write("return {{\"Expected '{}' in argument {}\"s}};", param.type, i); - } - LeaveScope(); - Write("Param{0} {1} = parameters[{0}].As();", i, param.name); - NewLine(); - callArgs += param.name; - callArgsTypes += param.type; - ++i; - } - NewLine(); - if (method.returnValue == "void") - { - if (method.tomlAttributes.is_table() && method.tomlAttributes.as_table().contains("Delegate")) - { - auto it = method.tomlAttributes.as_table().find("Delegate"); - if (it->second.is_boolean()) - { - Write("if (GetCustomDelegate() == nullptr)"); - EnterScope(); - Write("CCT_ASSERT_FALSE(\"Missing Delegate\");"); - Write("return {{\"Invalid delegate pointer\"s}};"); - LeaveScope(); - Write("auto func = reinterpret_cast<{}(*)({})>(GetCustomDelegate());", method.returnValue, callArgsTypes); - if (method.returnValue != "void"s) - Write("return"); - Write("func({});", callArgs); - } - else if (it->second.is_string()) - { - auto delegateName = it->second.as_string(); - Write("{}({});", delegateName, callArgs); - } - } - else - Write("static_cast<{}&>(self).{}({});", className, method.name, callArgs); - Write("return Any{{}};"); - } - else - { - if (method.tomlAttributes.is_table() && method.tomlAttributes.as_table().contains("Delegate")) - { - auto it = method.tomlAttributes.as_table().find("Delegate"); - if (it->second.is_boolean()) - { - Write("if (GetCustomDelegate() == nullptr)"); - EnterScope(); - Write("CCT_ASSERT_FALSE(\"Missing Delegate\");"); - Write("return {{\"Missing Delegate\"s}};"); - LeaveScope(); - Write("auto func = reinterpret_cast<{}(*)({})>(GetCustomDelegate());", method.returnValue, callArgsTypes); - Write("return Any::Make<{}>(func({}));", method.returnValue, callArgs); - } - else - { - Write("return Any::Make<{}>(static_cast<{}&>(self).{}({}));", method.returnValue, className, method.name, callArgs); - } - } - else - { - Write("auto res = static_cast<{}&>(self).{}({});", className, method.name, callArgs); - Write("return cct::Any::Make<{}>(res);", method.returnValue); - } - } - LeaveScope(); - } - } - LeaveScope(";"sv); - - if (method.tomlAttributes.as_table().contains("Delegate")) - { - std::string methodParameterPrototype; - std::string callArgs; - std::string callArgsTypes; - std::size_t i = 0; - for (auto& param : method.params) - { - if (i != 0 && i < method.params.size()) - { - methodParameterPrototype += ", "; - callArgs += ", "; - callArgsTypes += ", "; - } - methodParameterPrototype += param.type + ' ' + param.name; - callArgs += param.name; - callArgsTypes += param.type; - ++i; - } - if (ns.starts_with("::"sv)) - ns.remove_prefix(2); - Write("{} {}::{}::{}({})", method.returnValue, ns, className, method.name, methodParameterPrototype); - EnterScope(); - auto delegateIt = method.tomlAttributes.as_table().find("Delegate"); - if (delegateIt->second.is_string()) - { - auto functionName = method.tomlAttributes.as_table().find("Delegate")->second.as_string(); - if (method.returnValue != "void"s) - Write("return"); - Write("{}({})", functionName, callArgs); - Write(";"); - } - else if (delegateIt->second.is_boolean()) - { - Write("const auto* methodClass = GetClass()->GetMethod(\"{}\"sv);", method.name); - Write("if (methodClass == nullptr || methodClass->GetCustomDelegate() == nullptr)"); - EnterScope(), - Write("CCT_ASSERT_FALSE(\"Missing Delegate\");"); - Write("return"); - if (method.returnValue != "void"s) - Write("{}{{}}", method.returnValue); - Write(";"); - LeaveScope(); - Write("auto func = reinterpret_cast<{}(*)({})>(methodClass->GetCustomDelegate());", method.returnValue, callArgsTypes); - if (method.returnValue != "void"s) - Write("return"); - Write("func({})", callArgs); - Write(";"); - } - LeaveScope(); - } - } - - void CppGenerator::GenerateEnum(const Enum& enum_, std::string_view ns) - { - Write("class Internal{}EnumerationClass : public cct::refl::EnumerationClass", enum_.name); - EnterScope(); - { - Write("public:"); - Write("Internal{}EnumerationClass()", enum_.name); - Write(": cct::refl::EnumerationClass(nullptr, \"{}\"s)", enum_.name); - EnterScope(); - { - } - LeaveScope(); - - NewLine(); - Write("~Internal{}EnumerationClass() override", enum_.name); - EnterScope(); - { - } - LeaveScope(); - - NewLine(); - Write("void Initialize() override"); - EnterScope(); - { - for (const auto& elem : enum_.elements) - { - Write("AddEnumValue(\"{}\", {});", elem.name, elem.value); - } - } - LeaveScope(); - - NewLine(); - Write("cct::refl::Object* GetMemberVariable(std::size_t, const cct::refl::Object&) const override"); - EnterScope(); - { - Write("return nullptr;"); - } - LeaveScope(); - - NewLine(); - Write("void* GetNativeMemberVariable(std::size_t, const cct::refl::Object&) const override"); - EnterScope(); - { - Write("return nullptr;"); - } - LeaveScope(); - } - LeaveScope(";"); - } - - void CppGenerator::GeneratePackage(const Package& pkg) - { - if (pkg.name.empty()) - return; - Write("class Internal{}Package : public cct::refl::Package", pkg.name); - EnterScope(); - { - Write("public:"); - Write("Internal{0}Package() : cct::refl::Package(\"{0}\"s) {{}}", pkg.name); - NewLine(); - Write("~Internal{}Package() override", pkg.name); - EnterScope(); - { - for (auto& klass : pkg.classes) - Write("GlobalNamespace::Get().RemoveClass(\"{}\"sv);", klass.name); - for (auto& ns : pkg.namepsaces) - Write("GlobalNamespace::Get().RemoveNamespace(\"{}\"sv);", ns.name); - Write("m_namespaces.clear();"); - } - LeaveScope(); - - NewLine(); - Write("void LoadNamespaces() override"); - EnterScope(); - { - for (auto& ns : pkg.namepsaces) - Write("AddNamespace(Create{}NamespaceInstance());", ns.name); - Write("for (auto& ns : m_namespaces)"); - EnterScope(); - { - Write("ns->LoadNamespaces();"); - } - LeaveScope(); - } - LeaveScope(); - NewLine(); - Write("void InitializeNamespaces() override"); - EnterScope(); - { - for (auto& klass : pkg.classes) - { - EnterScope(); - Write("AddClass(std::make_unique());", klass.name); - LeaveScope(); - } - NewLine(); - Write("for (auto& ns : m_namespaces)"); - EnterScope(); - Write("ns->LoadClasses();"); - LeaveScope(); - } - LeaveScope(); - NewLine(); - Write("void InitializeClasses() override"); - EnterScope(); - { - Write("for (auto& ns : m_namespaces)"); - EnterScope(); - { - Write("ns->InitializeClasses();"); - } - LeaveScope(); - } - LeaveScope(); - } - LeaveScope(";"sv); - NewLine(); - Write("std::unique_ptr Create{}Package()", pkg.name); - EnterScope(); - Write("return std::make_unique();", pkg.name); - LeaveScope(); - } -} // namespace cct diff --git a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp b/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp deleted file mode 100644 index 1f4c397..0000000 --- a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// Created by arthur on 09/12/2024. -// - -#ifndef CONCERTO_PKGGENERATOR_CPPGENERATOR_HPP -#define CONCERTO_PKGGENERATOR_CPPGENERATOR_HPP - -#include "Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp" - -namespace cct -{ - class CppGenerator : public FileGenerator - { - public: - using FileGenerator::FileGenerator; - bool Generate(const Package& package, std::span args) override; - - private: - void GenerateNamespace(const Namespace& ns, const std::string& namespaceChain = ""); - void GenerateClass(std::string_view ns, const Class& klass); - void GenerateGenericClass(std::string_view ns, const Class& klass); - void GenerateTemplateClass(std::string_view ns, const Class& klass); - void GenerateClassMethod(std::string_view className, const Class::Method& method, std::string_view ns, std::size_t methodIndex); - void GenerateEnum(const Enum& enum_, std::string_view ns = ""); - void GeneratePackage(const Package& pkg); - }; -} // namespace cct - -#endif // CONCERTO_PKGGENERATOR_CPPGENERATOR_HPP diff --git a/Src/Concerto/PackageGenerator/Defines.hpp b/Src/Concerto/PackageGenerator/Defines.hpp index a8564b0..6aebbe7 100644 --- a/Src/Concerto/PackageGenerator/Defines.hpp +++ b/Src/Concerto/PackageGenerator/Defines.hpp @@ -101,7 +101,7 @@ struct Package std::string description; std::vector classes; - std::vector namepsaces; + std::vector namespaces; std::vector enums; }; diff --git a/Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.cpp b/Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.cpp deleted file mode 100644 index 48c4a42..0000000 --- a/Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// -// Created by arthur on 09/12/2024. -// - -#include "Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp" - -#include -#include - -#include - -namespace cct -{ - FileGenerator::FileGenerator(const std::string& path) : - m_indentLevel(0), - m_stream(), - m_path(path) - { - } - - namespace - { - static std::string NormalizeNewlines(std::string s) - { - // Convert CRLF to LF to allow platform-agnostic comparisons - std::string out; - out.reserve(s.size()); - for (std::size_t i = 0; i < s.size(); ++i) - { - char c = s[i]; - if (c == '\r') - { - // skip, will be normalized by following '\n' or ignored if standalone - continue; - } - out.push_back(c); - } - return out; - } - } // namespace - - FileGenerator::~FileGenerator() - { - try - { - std::string generated = m_stream.str(); - std::string existing; - { - std::ifstream in(m_path, std::ios::in | std::ios::binary); - if (in) - { - std::ostringstream buf; - buf << in.rdbuf(); - existing = buf.str(); - } - } - if (NormalizeNewlines(existing) == NormalizeNewlines(generated)) - return; - - std::ofstream out(m_path, std::ios::out | std::ios::trunc); - if (out) - { - out << generated; - } - } - catch (const std::exception& e) - { - cct::Logger::Error("Failed to write generated file {}: {}", m_path, e.what()); - } - } - - void FileGenerator::EnterScope() - { - Write("{{"); - ++m_indentLevel; - } - - void FileGenerator::LeaveScope(std::string_view str) - { - --m_indentLevel; - Write("}}{}", str); - } - - void FileGenerator::NewLine() - { - m_stream << '\n'; - } - - std::string FileGenerator::Capitalize(std::string_view str) - { - if (str.empty()) - return {}; - std::string s(str); - s[0] = toupper(s[0]); - return s; - } -} // namespace cct diff --git a/Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp b/Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp deleted file mode 100644 index 3d44a20..0000000 --- a/Src/Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// -// Created by arthur on 09/12/2024. -// - -#ifndef CONCERTO_PKGGENERATOR_FILEGENERATOR_HPP -#define CONCERTO_PKGGENERATOR_FILEGENERATOR_HPP - -#include -#include -#include -#include -#include - -#include "Concerto/PackageGenerator/Defines.hpp" - -namespace cct -{ - class FileGenerator - { - public: - virtual ~FileGenerator(); - explicit FileGenerator(const std::string& path); - - virtual bool Generate(const Package& package, std::span args) = 0; - - void EnterScope(); - void LeaveScope(std::string_view str = ""); - - void NewLine(); - - static std::string Capitalize(std::string_view str); - - template - void Write(const std::format_string fmt, T&&... args) - { - using namespace std::string_view_literals; - std::string data = std::vformat(fmt.get(), std::make_format_args(args...)); - auto lines = data | std::ranges::views::split("\n"sv); - - for (auto line : lines) - { - m_stream << std::string(m_indentLevel, '\t'); - m_stream << std::string_view(line.data(), line.size()); - } - m_stream << '\n'; - } - - private: - std::size_t m_indentLevel; - - protected: - std::ostringstream m_stream; - std::string m_path; - }; -} // namespace cct - -#endif // CONCERTO_PKGGENERATOR_FILEGENERATOR_HPP diff --git a/Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.cpp b/Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.cpp deleted file mode 100644 index f4bb93b..0000000 --- a/Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// -// Created by arthur on 09/12/2024. -// - -#include "Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.hpp" - -#include -#include - -namespace cct -{ - bool HeaderGenerator::Generate(const Package& package, std::span args) - { - std::string upperPackageName(package.name); - std::ranges::transform(upperPackageName, upperPackageName.begin(), [](char c) - { return std::toupper(c); }); - std::string api = upperPackageName + "PACKAGE_API"; - Write("//This file was automatically generated, do not edit\n"); - Write("#pragma once"); - Write("#include "); - Write("#include "); - NewLine(); - Write("#include "); - Write("#include "); - Write("#include "); - Write("#include "); - Write("#include "); - Write("#include "); - Write("#include "); - NewLine(); - - NewLine(); - Write("#ifdef {}PACKAGE_STATIC", upperPackageName); - { - Write(" #define {}PACKAGE_API", upperPackageName); - } - Write("#else"); - { - Write("#ifdef {}PACKAGE_BUILD", upperPackageName); - Write(" #define {}PACKAGE_API CCT_EXPORT", upperPackageName); - Write("#else"); - Write(" #define {}PACKAGE_API CCT_IMPORT\n", upperPackageName); - Write("#endif"); - } - Write("#endif"); - NewLine(); - - for (auto& enum_ : package.enums) - GenerateEnum(enum_, api); - - NewLine(); - - for (auto& klass : package.classes) - { - if (klass.isGenericClass) - GenerateGenericClass(klass, api); - else if (klass.isTemplateClass) - GenerateTemplateClass(klass, api); - else - GenerateClass(klass, api); - NewLine(); - } - - NewLine(); - - for (auto& ns : package.namepsaces) - GenerateNamespace(ns, api); - NewLine(); - Write("{} std::unique_ptr Create{}Package();", api, package.name); - return true; - } - - void HeaderGenerator::GenerateNamespace(const Namespace& ns, const std::string& api) - { - Write("namespace {}", ns.name); - EnterScope(); - for (auto& nestedNamespace : ns.namespaces) - GenerateNamespace(nestedNamespace, api); - for (auto& enum_ : ns.enums) - GenerateEnum(enum_, api); - NewLine(); - /*for (auto& klass : ns.classes) - GenerateClass(klass, api);*/ - LeaveScope(); - } - - void HeaderGenerator::GenerateClass(const Class& klass, const std::string& api) - { - if (klass.base.empty()) - Write("class {} {}", api, klass.name); - else - Write("class {} {} : public {}", api, klass.name, klass.base); - EnterScope(); - { - Write("public:"); - for (const auto& member : klass.members) - { - Write("const {}& Get{}() const", member.type, Capitalize(member.name)); - EnterScope(); - Write("return _{};", member.name); - LeaveScope(); - Write("{}& Get{}()", member.type, Capitalize(member.name)); - EnterScope(); - Write("return _{};", member.name); - LeaveScope(); - } - NewLine(); - Write("CCT_OBJECT({});", klass.name); - NewLine(); - Write("private:"); - for (const auto& member : klass.members) - { - Write("{} _{};", member.type, member.name); - } - } - LeaveScope(";"); - } - - void HeaderGenerator::GenerateTemplateClass(const Class& klass, const std::string& api) - { - Write("template<", ""); - for (std::size_t i = 0; i < klass.templateParameters.size(); ++i) - { - Write("typename {}", klass.templateParameters[i].name, ""); - if (i < klass.templateParameters.size() - 1) - Write(", ", ""); - } - Write(">"); - - if (klass.base.empty()) - Write("class {} {}", api, klass.name); - else - Write("class {} {} : public {}", api, klass.name, klass.base); - EnterScope(); - { - Write("public:"); - for (const auto& member : klass.members) - { - Write("const {}& Get{}() const", member.type, Capitalize(member.name)); - EnterScope(); - Write("return _{};", member.name); - LeaveScope(); - Write("{}& Get{}()", member.type, Capitalize(member.name)); - EnterScope(); - Write("return _{};", member.name); - LeaveScope(); - } - NewLine(); - Write("CCT_OBJECT({});", klass.name); - NewLine(); - Write("private:"); - for (const auto& member : klass.members) - { - Write("{} _{};", member.type, member.name); - } - } - LeaveScope(";"); - } - - void HeaderGenerator::GenerateGenericClass(const Class& klass, const std::string& api) - { - // For generic classes, we generate the same interface as regular classes - // The actual type parameters are handled at runtime - if (klass.base.empty()) - Write("class {} {}", api, klass.name); - else - Write("class {} {} : public {}", api, klass.name, klass.base); - EnterScope(); - { - Write("public:"); - for (const auto& member : klass.members) - { - Write("const {}& Get{}() const", member.type, Capitalize(member.name)); - EnterScope(); - Write("return _{};", member.name); - LeaveScope(); - Write("{}& Get{}()", member.type, Capitalize(member.name)); - EnterScope(); - Write("return _{};", member.name); - LeaveScope(); - } - NewLine(); - Write("CCT_OBJECT({});", klass.name); - NewLine(); - Write("private:"); - for (const auto& member : klass.members) - { - Write("{} _{};", member.type, member.name); - } - } - LeaveScope(";"); - } - - void HeaderGenerator::GenerateEnum(const Enum& enum_, const std::string& api) - { - Write("enum class {} : {};", enum_.name, enum_.base); - Write("{} std::string_view {}ToString({} value);", api, Capitalize(enum_.name), enum_.name); - Write("{} {} {}FromString(std::string_view str);", api, enum_.name, Capitalize(enum_.name)); - } -} // namespace cct diff --git a/Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.hpp b/Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.hpp deleted file mode 100644 index 37dca33..0000000 --- a/Src/Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by arthur on 09/12/2024. -// - -#ifndef CONCERTO_PKGGENERATOR_HEADERGENERATOR_HPP -#define CONCERTO_PKGGENERATOR_HEADERGENERATOR_HPP - -#include "Concerto/PackageGenerator/FileGenerator/FileGenerator.hpp" - -namespace cct -{ - class HeaderGenerator : public FileGenerator - { - public: - using FileGenerator::FileGenerator; - bool Generate(const Package& package, std::span args) override; - - private: - void GenerateNamespace(const Namespace& ns, const std::string& api); - void GenerateClass(const Class& klass, const std::string& api); - void GenerateGenericClass(const Class& klass, const std::string& api); - void GenerateTemplateClass(const Class& klass, const std::string& api); - void GenerateEnum(const Enum& enum_, const std::string& api); - }; -} // namespace cct - -#endif // CONCERTO_PKGGENERATOR_HEADERGENERATOR_HPP diff --git a/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp b/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp new file mode 100644 index 0000000..50ddfd9 --- /dev/null +++ b/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp @@ -0,0 +1,597 @@ +// +// Created by arthur +// + +#include "Concerto/PackageGenerator/Plugin/PluginApi.h" + +#include +#include +#include + +#include "Concerto/Core/Assert.hpp" +#include "Concerto/PackageGenerator/Defines.hpp" +#include "Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp" + +extern "C" +{ + const char* crpPackageGetName(const CrpPackage* package) + { + if (!package) + return nullptr; + return reinterpret_cast(package)->name.c_str(); + } + + const char* crpPackageGetVersion(const CrpPackage* package) + { + if (!package) + return nullptr; + return reinterpret_cast(package)->version.c_str(); + } + + const char* crpPackageGetDescription(const CrpPackage* package) + { + if (!package) + return nullptr; + return reinterpret_cast(package)->description.c_str(); + } + + size_t crpPackageGetClassCount(const CrpPackage* package) + { + if (!package) + return 0; + return reinterpret_cast(package)->classes.size(); + } + + const CrpClass* crpPackageGetClass(const CrpPackage* package, size_t index) + { + if (!package) + return nullptr; + const auto* pkg = reinterpret_cast(package); + if (index >= pkg->classes.size()) + return nullptr; + return reinterpret_cast(&pkg->classes[index]); + } + + size_t crpPackageGetNamespaceCount(const CrpPackage* package) + { + if (!package) + return 0; + return reinterpret_cast(package)->namespaces.size(); + } + + const CrpNamespace* crpPackageGetNamespace(const CrpPackage* package, size_t index) + { + if (!package) + return nullptr; + const auto* pkg = reinterpret_cast(package); + if (index >= pkg->namespaces.size()) + return nullptr; + return reinterpret_cast(&pkg->namespaces[index]); + } + + size_t crpPackageGetEnumCount(const CrpPackage* package) + { + if (!package) + return 0; + return reinterpret_cast(package)->enums.size(); + } + + const CrpEnum* crpPackageGetEnum(const CrpPackage* package, size_t index) + { + if (!package) + return nullptr; + const auto* pkg = reinterpret_cast(package); + if (index >= pkg->enums.size()) + return nullptr; + return reinterpret_cast(&pkg->enums[index]); + } + + const char* crpClassGetName(const CrpClass* cls) + { + if (!cls) + return nullptr; + if (reinterpret_cast(cls)->name.empty()) + return nullptr; + return reinterpret_cast(cls)->name.c_str(); + } + + const char* crpClassGetBase(const CrpClass* cls) + { + if (!cls) + return nullptr; + if (reinterpret_cast(cls)->base.empty()) + return nullptr; + return reinterpret_cast(cls)->base.c_str(); + } + + const char* crpClassGetScope(const CrpClass* cls) + { + if (!cls) + return nullptr; + if (reinterpret_cast(cls)->scope.empty()) + return nullptr; + return reinterpret_cast(cls)->scope.c_str(); + } + + size_t crpClassGetMethodCount(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->methods.size(); + } + + const CrpClassMethod* crpClassGetMethod(const CrpClass* cls, size_t index) + { + if (!cls) + return nullptr; + const auto* klass = reinterpret_cast(cls); + if (index >= klass->methods.size()) + return nullptr; + return reinterpret_cast(&klass->methods[index]); + } + + size_t crpClassGetMemberCount(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->members.size(); + } + + const CrpClassMember* crpClassGetMember(const CrpClass* cls, size_t index) + { + if (!cls) + return nullptr; + const auto* klass = reinterpret_cast(cls); + if (index >= klass->members.size()) + return nullptr; + return reinterpret_cast(&klass->members[index]); + } + + int32_t crpClassIsTemplate(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->isTemplateClass ? 1 : 0; + } + + int32_t crpClassIsGeneric(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->isGenericClass ? 1 : 0; + } + + size_t crpClassGetTemplateParameterCount(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->templateParameters.size(); + } + + const CrpTemplateParameter* crpClassGetTemplateParameter(const CrpClass* cls, size_t index) + { + if (!cls) + return nullptr; + const auto* klass = reinterpret_cast(cls); + if (index >= klass->templateParameters.size()) + return nullptr; + return reinterpret_cast(&klass->templateParameters[index]); + } + + size_t crpClassGetTemplateSpecializationCount(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->templateSpecializations.size(); + } + + const char* crpClassGetTemplateSpecialization(const CrpClass* cls, size_t index) + { + if (!cls) + return nullptr; + const auto* klass = reinterpret_cast(cls); + if (index >= klass->templateSpecializations.size()) + return nullptr; + if (klass->templateSpecializations[index].empty()) + return nullptr; + return klass->templateSpecializations[index].c_str(); + } + + size_t crpClassGetGenericTypeParameterFieldCount(const CrpClass* cls) + { + if (!cls) + return 0; + return reinterpret_cast(cls)->genericTypeParameterFields.size(); + } + + const char* crpClassGetGenericTypeParameterField(const CrpClass* cls, size_t index) + { + if (!cls) + return nullptr; + const auto* klass = reinterpret_cast(cls); + if (index >= klass->genericTypeParameterFields.size()) + return nullptr; + if (klass->genericTypeParameterFields[index].empty()) + return nullptr; + return klass->genericTypeParameterFields[index].c_str(); + } + + const char* crpClassMemberGetName(const CrpClassMember* member) + { + if (!member) + return nullptr; + if (reinterpret_cast(member)->name.empty()) + return nullptr; + return reinterpret_cast(member)->name.c_str(); + } + + const char* crpClassMemberGetType(const CrpClassMember* member) + { + if (!member) + return nullptr; + if (reinterpret_cast(member)->type.empty()) + return nullptr; + return reinterpret_cast(member)->type.c_str(); + } + + int32_t crpClassMemberIsNative(const CrpClassMember* member) + { + if (!member) + return 0; + return reinterpret_cast(member)->isNative ? 1 : 0; + } + + const char* crpClassMethodGetName(const CrpClassMethod* method) + { + if (!method) + return nullptr; + if (reinterpret_cast(method)->name.empty()) + return nullptr; + return reinterpret_cast(method)->name.c_str(); + } + + const char* crpClassMethodGetBase(const CrpClassMethod* method) + { + if (!method) + return nullptr; + if (reinterpret_cast(method)->base.empty()) + return nullptr; + return reinterpret_cast(method)->base.c_str(); + } + + const char* crpClassMethodGetReturnType(const CrpClassMethod* method) + { + if (!method) + return nullptr; + if (reinterpret_cast(method)->returnValue.empty()) + return nullptr; + return reinterpret_cast(method)->returnValue.c_str(); + } + + int32_t crpClassMethodHasCustomInvoker(const CrpClassMethod* method) + { + if (!method) + return 0; + return reinterpret_cast(method)->customInvoker ? 1 : 0; + } + + size_t crpClassMethodGetParamCount(const CrpClassMethod* method) + { + if (!method) + return 0; + return reinterpret_cast(method)->params.size(); + } + + const CrpClassMethodParam* crpClassMethodGetParam(const CrpClassMethod* method, size_t index) + { + if (!method) + return nullptr; + const auto* m = reinterpret_cast(method); + if (index >= m->params.size()) + return nullptr; + return reinterpret_cast(&m->params[index]); + } + + const char* crpClassMethodParamGetName(const CrpClassMethodParam* param) + { + if (!param) + return nullptr; + if (reinterpret_cast(param)->name.empty()) + return nullptr; + return reinterpret_cast(param)->name.c_str(); + } + + const char* crpClassMethodParamGetType(const CrpClassMethodParam* param) + { + if (!param) + return nullptr; + if (reinterpret_cast(param)->type.empty()) + return nullptr; + return reinterpret_cast(param)->type.c_str(); + } + + int32_t crpClassMethodHasDelegate(const CrpClassMethod* method) + { + if (!method) + return 0; + const auto* m = reinterpret_cast(method); + if (!m->tomlAttributes.is_table()) + return 0; + return m->tomlAttributes.as_table().contains("Delegate") ? 1 : 0; + } + + int32_t crpClassMethodIsBooleanDelegate(const CrpClassMethod* method) + { + if (!method) + return 0; + const auto* m = reinterpret_cast(method); + if (!m->tomlAttributes.is_table()) + return 0; + auto it = m->tomlAttributes.as_table().find("Delegate"); + if (it == m->tomlAttributes.as_table().end()) + return 0; + return it->second.is_boolean() ? 1 : 0; + } + + const char* crpClassMethodGetDelegateName(const CrpClassMethod* method) + { + if (!method) + return nullptr; + const auto* m = reinterpret_cast(method); + if (!m->tomlAttributes.is_table()) + return nullptr; + auto it = m->tomlAttributes.as_table().find("Delegate"); + if (it == m->tomlAttributes.as_table().end()) + return nullptr; + if (!it->second.is_string()) + return nullptr; + return it->second.as_string().c_str(); + } + + const char* crpEnumGetName(const CrpEnum* enm) + { + if (!enm) + return nullptr; + return reinterpret_cast(enm)->name.c_str(); + } + + const char* crpEnumGetBase(const CrpEnum* enm) + { + if (!enm) + return nullptr; + return reinterpret_cast(enm)->base.c_str(); + } + + size_t crpEnumGetElementCount(const CrpEnum* enm) + { + if (!enm) + return 0; + return reinterpret_cast(enm)->elements.size(); + } + + const CrpEnumElement* crpEnumGetElement(const CrpEnum* enm, size_t index) + { + if (!enm) + return nullptr; + const auto* enumType = reinterpret_cast(enm); + if (index >= enumType->elements.size()) + return nullptr; + return reinterpret_cast(&enumType->elements[index]); + } + + const char* crpEnumElementGetName(const CrpEnumElement* elem) + { + if (!elem) + return nullptr; + return reinterpret_cast(elem)->name.c_str(); + } + + const char* crpEnumElementGetValue(const CrpEnumElement* elem) + { + if (!elem) + return nullptr; + return reinterpret_cast(elem)->value.c_str(); + } + + const char* crpTemplateParameterGetName(const CrpTemplateParameter* param) + { + if (!param) + return nullptr; + return reinterpret_cast(param)->name.c_str(); + } + + const char* crpNamespaceGetName(const CrpNamespace* ns) + { + if (!ns) + return nullptr; + return reinterpret_cast(ns)->name.c_str(); + } + + size_t crpNamespaceGetClassCount(const CrpNamespace* ns) + { + if (!ns) + return 0; + return reinterpret_cast(ns)->classes.size(); + } + + const CrpClass* crpNamespaceGetClass(const CrpNamespace* ns, size_t index) + { + if (!ns) + return nullptr; + const auto* nspace = reinterpret_cast(ns); + if (index >= nspace->classes.size()) + return nullptr; + return reinterpret_cast(&nspace->classes[index]); + } + + size_t crpNamespaceGetEnumCount(const CrpNamespace* ns) + { + if (!ns) + return 0; + return reinterpret_cast(ns)->enums.size(); + } + + const CrpEnum* crpNamespaceGetEnum(const CrpNamespace* ns, size_t index) + { + if (!ns) + return nullptr; + const auto* nspace = reinterpret_cast(ns); + if (index >= nspace->enums.size()) + return nullptr; + return reinterpret_cast(&nspace->enums[index]); + } + + size_t crpNamespaceGetNamespaceCount(const CrpNamespace* ns) + { + if (!ns) + return 0; + return reinterpret_cast(ns)->namespaces.size(); + } + + const CrpNamespace* crpNamespaceGetNamespace(const CrpNamespace* ns, size_t index) + { + if (!ns) + return nullptr; + const auto* nspace = reinterpret_cast(ns); + if (index >= nspace->namespaces.size()) + return nullptr; + return reinterpret_cast(&nspace->namespaces[index]); + } + + void crpGenerationContextWrite(CrpGenerationContext* ctx, const char* format, ...) + { + if (!ctx || !format) + { + CCT_ASSERT_FALSE("crpGenerationContextWrite: ctx or format is null"); + return; + } + + auto* context = reinterpret_cast(ctx); + + va_list args; + va_start(args, format); + + va_list argsCopy; + va_copy(argsCopy, args); + int size = std::vsnprintf(nullptr, 0, format, argsCopy); + va_end(argsCopy); + + if (size < 0) + { + va_end(args); + return; + } + + std::string buffer(size + 1, '\0'); + std::vsnprintf(buffer.data(), buffer.size(), format, args); + va_end(args); + + buffer.resize(size); + context->Write(buffer); + } + + void crpGenerationContextNewLine(CrpGenerationContext* ctx) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextNewLine: ctx is null"); + return; + } + + auto* context = reinterpret_cast(ctx); + context->NewLine(); + } + + void crpGenerationContextEnterScope(CrpGenerationContext* ctx) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextEnterScope: ctx is null"); + return; + } + + auto* context = reinterpret_cast(ctx); + context->EnterScope(); + } + + void crpGenerationContextLeaveScope(CrpGenerationContext* ctx, const char* suffix) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextLeaveScope: ctx is null"); + return; + } + + auto* context = reinterpret_cast(ctx); + context->LeaveScope(suffix ? suffix : ""); + } + + const CrpPackage* crpGenerationContextGetPackage(CrpGenerationContext* ctx) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextGetPackage: ctx is null"); + return nullptr; + } + auto* context = reinterpret_cast(ctx); + return reinterpret_cast(context->package); + } + + const CrpClass* crpGenerationContextGetClass(CrpGenerationContext* ctx) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextGetClass: ctx is null"); + return nullptr; + } + auto* context = reinterpret_cast(ctx); + return reinterpret_cast(context->currentClass); + } + + const CrpNamespace* crpGenerationContextGetNamespace(CrpGenerationContext* ctx) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextGetNamespace: ctx is null"); + return nullptr; + } + auto* context = reinterpret_cast(ctx); + return reinterpret_cast(context->currentNamespace); + } + + const char* crpGenerationContextGetNamespacePath(CrpGenerationContext* ctx) + { + if (!ctx) + { + CCT_ASSERT_FALSE("crpGenerationContextGetNamespacePath: ctx is null"); + return nullptr; + } + auto* context = reinterpret_cast(ctx); + return context->namespacePath; + } + + void* crpGenerationContextGetPrivateData(CrpGenerationContext* ctx) + { + if (!ctx) + return nullptr; + auto* context = reinterpret_cast(ctx); + return context->pluginPrivateData; + } + size_t crpGenerationContextGetHeaderCount(CrpGenerationContext* ctx) + { + if (!ctx) + return 0; + auto* context = reinterpret_cast(ctx); + return context->headerCount; + } + + const char* crpGenerationContextGetHeader(CrpGenerationContext* ctx, size_t index) + { + if (!ctx) + return nullptr; + auto* context = reinterpret_cast(ctx); + if (index >= context->headerCount || !context->headers) + return nullptr; + return context->headers[index]; + } +} // extern "C" diff --git a/Src/Concerto/PackageGenerator/Plugin/PluginApi.h b/Src/Concerto/PackageGenerator/Plugin/PluginApi.h new file mode 100644 index 0000000..19871b2 --- /dev/null +++ b/Src/Concerto/PackageGenerator/Plugin/PluginApi.h @@ -0,0 +1,241 @@ +// +// Created by arthur +// + +#ifndef CONCERTO_PKGGENERATOR_PLUGINAPI_H +#define CONCERTO_PKGGENERATOR_PLUGINAPI_H + +#include +#include + +#ifdef _WIN32 +#define CRP_PLUGIN_EXPORT __declspec(dllexport) +#define CRP_PLUGIN_IMPORT __declspec(dllimport) +#else +#define CRP_PLUGIN_EXPORT __attribute__((visibility("default"))) +#define CRP_PLUGIN_IMPORT +#endif + +// API functions export/import +#ifdef CRP_PLUGIN_API_BUILD +#define CRP_PLUGIN_API CRP_PLUGIN_EXPORT +#else +#define CRP_PLUGIN_API CRP_PLUGIN_IMPORT +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif +#define CRP_PLUGIN_API_VERSION 1 + typedef struct CrpPackage CrpPackage; + typedef struct CrpGenerationContext CrpGenerationContext; + typedef struct CrpNamespace CrpNamespace; + typedef struct CrpClass CrpClass; + typedef struct CrpClassMember CrpClassMember; + typedef struct CrpClassMethod CrpClassMethod; + typedef struct CrpEnum CrpEnum; + typedef struct CrpEnumElement CrpEnumElement; + typedef struct CrpTemplateParameter CrpTemplateParameter; + typedef struct CrpClassMethodParam CrpClassMethodParam; + typedef struct CrpTomlAttributes CrpTomlAttributes; + + typedef struct CrpPluginInfo + { + const char* name; + const char* version; + const char* author; + const char* description; + const char* outputFileExtension; // ".gen.hpp", ".gen.cpp", etc. + } CrpPluginInfo; + + /** + * Function table exported by each plugin. + * All function pointers can be NULL if the plugin does not implement them. + * The loader will check for NULL before calling. + */ + typedef struct CrpPluginFunctionTable + { + uint32_t apiVersion; + + /** + * Get plugin information. + * @param outInfo Pointer to fill with plugin info + */ + void (*GetInfo)(CrpPluginInfo* outInfo); + + /** + * Get plugin priority for ordering. + * Higher priority plugins run first. + * @return Priority value (default: 0) + */ + int32_t (*GetPriority)(void); + + /** + * Called when the plugin is loaded. + * @param outPrivateData Optional pointer to store plugin-specific data (can be left NULL). + * @return 1 on success, 0 on failure. + */ + int32_t (*OnLoad)(void** outPrivateData); + void (*OnUnload)(void* privateData); + + /** + * Transform the package before generation. + * @param package Package to transform + */ + void (*TransformPackage)(CrpPackage* package); + + void (*BeforePackageGeneration)(const CrpPackage* package, CrpGenerationContext* ctx); + void (*OnPackageGeneration)(const CrpPackage* package, CrpGenerationContext* ctx); + void (*AfterPackageGeneration)(const CrpPackage* package, CrpGenerationContext* ctx); + + void (*BeforeNamespaceGeneration)(const CrpNamespace* ns, CrpGenerationContext* ctx); + void (*OnNamespaceGeneration)(const CrpNamespace* ns, CrpGenerationContext* ctx); + void (*AfterNamespaceGeneration)(const CrpNamespace* ns, CrpGenerationContext* ctx); + + void (*BeforeClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + void (*OnClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + void (*AfterClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + + void (*BeforeMemberGeneration)(const CrpClassMember* member, CrpGenerationContext* ctx); + void (*OnMemberGeneration)(const CrpClassMember* member, CrpGenerationContext* ctx); + void (*AfterMemberGeneration)(const CrpClassMember* member, CrpGenerationContext* ctx); + + void (*BeforeMethodGeneration)(const CrpClassMethod* method, CrpGenerationContext* ctx); + void (*OnMethodGeneration)(const CrpClassMethod* method, CrpGenerationContext* ctx); + void (*AfterMethodGeneration)(const CrpClassMethod* method, CrpGenerationContext* ctx); + + void (*BeforeEnumGeneration)(const CrpEnum* enumeration, CrpGenerationContext* ctx); + void (*OnEnumGeneration)(const CrpEnum* enumeration, CrpGenerationContext* ctx); + void (*AfterEnumGeneration)(const CrpEnum* enumeration, CrpGenerationContext* ctx); + + void (*BeforeEnumElementGeneration)(const CrpEnumElement* element, CrpGenerationContext* ctx); + void (*OnEnumElementGeneration)(const CrpEnumElement* element, CrpGenerationContext* ctx); + void (*AfterEnumElementGeneration)(const CrpEnumElement* element, CrpGenerationContext* ctx); + + void (*BeforeTemplateClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + void (*OnTemplateClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + void (*AfterTemplateClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + + void (*BeforeGenericClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + void (*OnGenericClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + void (*AfterGenericClassGeneration)(const CrpClass* cls, CrpGenerationContext* ctx); + + void (*BeforeTemplateSpecializationGeneration)(const CrpClass* cls, const char* specialization, CrpGenerationContext* ctx); + void (*OnTemplateSpecializationGeneration)(const CrpClass* cls, const char* specialization, CrpGenerationContext* ctx); + void (*AfterTemplateSpecializationGeneration)(const CrpClass* cls, const char* specialization, CrpGenerationContext* ctx); + + int32_t (*HandleAnnotation)( + const char* annotationType, + const char* entityName, + const CrpTomlAttributes* attributes, + CrpGenerationContext* ctx); + + /** + * Generate files for this plugin (optional). + * Plugin uses crpGenerationContextWrite() to write to ctx->outputStream. + * The host will write the stream contents to the appropriate file. + * @param package Package to generate files for + * @param ctx Generation context with outputStream + * @param outputFolder Folder where files should be generated + * @param headers Array of header file paths to include (can be NULL) + * @param headerCount Number of headers + * @return 1 if success, 0 if failure + */ + int32_t (*GenerateFile)( + const CrpPackage* package, + CrpGenerationContext* ctx, + const char* outputFolder, + const char** headers, + size_t headerCount); + + } CrpPluginFunctionTable; + + typedef const CrpPluginFunctionTable* (*CrpGetPluginFunctionTableFunc)(void); + +#define CRP_PLUGIN_ENTRY_POINT_NAME "crpGetPluginFunctionTable" + +#define CRP_DECLARE_PLUGIN_ENTRY(table) \ + CRP_PLUGIN_EXPORT const CrpPluginFunctionTable* crpGetPluginFunctionTable(void) \ + { \ + return &(table); \ + } + + CRP_PLUGIN_API const char* crpPackageGetName(const CrpPackage* package); + CRP_PLUGIN_API const char* crpPackageGetVersion(const CrpPackage* package); + CRP_PLUGIN_API const char* crpPackageGetDescription(const CrpPackage* package); + CRP_PLUGIN_API size_t crpPackageGetClassCount(const CrpPackage* package); + CRP_PLUGIN_API const CrpClass* crpPackageGetClass(const CrpPackage* package, size_t index); + CRP_PLUGIN_API size_t crpPackageGetNamespaceCount(const CrpPackage* package); + CRP_PLUGIN_API const CrpNamespace* crpPackageGetNamespace(const CrpPackage* package, size_t index); + CRP_PLUGIN_API size_t crpPackageGetEnumCount(const CrpPackage* package); + CRP_PLUGIN_API const CrpEnum* crpPackageGetEnum(const CrpPackage* package, size_t index); + + CRP_PLUGIN_API const char* crpClassGetName(const CrpClass* cls); + CRP_PLUGIN_API const char* crpClassGetBase(const CrpClass* cls); + CRP_PLUGIN_API const char* crpClassGetScope(const CrpClass* cls); + CRP_PLUGIN_API size_t crpClassGetMethodCount(const CrpClass* cls); + CRP_PLUGIN_API const CrpClassMethod* crpClassGetMethod(const CrpClass* cls, size_t index); + CRP_PLUGIN_API size_t crpClassGetMemberCount(const CrpClass* cls); + CRP_PLUGIN_API const CrpClassMember* crpClassGetMember(const CrpClass* cls, size_t index); + CRP_PLUGIN_API int32_t crpClassIsTemplate(const CrpClass* cls); + CRP_PLUGIN_API int32_t crpClassIsGeneric(const CrpClass* cls); + CRP_PLUGIN_API size_t crpClassGetTemplateParameterCount(const CrpClass* cls); + CRP_PLUGIN_API const CrpTemplateParameter* crpClassGetTemplateParameter(const CrpClass* cls, size_t index); + CRP_PLUGIN_API size_t crpClassGetTemplateSpecializationCount(const CrpClass* cls); + CRP_PLUGIN_API const char* crpClassGetTemplateSpecialization(const CrpClass* cls, size_t index); + + CRP_PLUGIN_API size_t crpClassGetGenericTypeParameterFieldCount(const CrpClass* cls); + CRP_PLUGIN_API const char* crpClassGetGenericTypeParameterField(const CrpClass* cls, size_t index); + + CRP_PLUGIN_API const char* crpClassMemberGetName(const CrpClassMember* member); + CRP_PLUGIN_API const char* crpClassMemberGetType(const CrpClassMember* member); + CRP_PLUGIN_API int32_t crpClassMemberIsNative(const CrpClassMember* member); + + CRP_PLUGIN_API const char* crpClassMethodGetName(const CrpClassMethod* method); + CRP_PLUGIN_API const char* crpClassMethodGetBase(const CrpClassMethod* method); + CRP_PLUGIN_API const char* crpClassMethodGetReturnType(const CrpClassMethod* method); + CRP_PLUGIN_API int32_t crpClassMethodHasCustomInvoker(const CrpClassMethod* method); + CRP_PLUGIN_API size_t crpClassMethodGetParamCount(const CrpClassMethod* method); + CRP_PLUGIN_API const CrpClassMethodParam* crpClassMethodGetParam(const CrpClassMethod* method, size_t index); + CRP_PLUGIN_API const char* crpClassMethodParamGetName(const CrpClassMethodParam* param); + CRP_PLUGIN_API const char* crpClassMethodParamGetType(const CrpClassMethodParam* param); + CRP_PLUGIN_API int32_t crpClassMethodHasDelegate(const CrpClassMethod* method); + CRP_PLUGIN_API int32_t crpClassMethodIsBooleanDelegate(const CrpClassMethod* method); + CRP_PLUGIN_API const char* crpClassMethodGetDelegateName(const CrpClassMethod* method); + + CRP_PLUGIN_API const char* crpEnumGetName(const CrpEnum* enm); + CRP_PLUGIN_API const char* crpEnumGetBase(const CrpEnum* enm); + CRP_PLUGIN_API size_t crpEnumGetElementCount(const CrpEnum* enm); + CRP_PLUGIN_API const CrpEnumElement* crpEnumGetElement(const CrpEnum* enm, size_t index); + + CRP_PLUGIN_API const char* crpEnumElementGetName(const CrpEnumElement* elem); + CRP_PLUGIN_API const char* crpEnumElementGetValue(const CrpEnumElement* elem); + + CRP_PLUGIN_API const char* crpTemplateParameterGetName(const CrpTemplateParameter* param); + + CRP_PLUGIN_API const char* crpNamespaceGetName(const CrpNamespace* ns); + CRP_PLUGIN_API size_t crpNamespaceGetClassCount(const CrpNamespace* ns); + CRP_PLUGIN_API const CrpClass* crpNamespaceGetClass(const CrpNamespace* ns, size_t index); + CRP_PLUGIN_API size_t crpNamespaceGetEnumCount(const CrpNamespace* ns); + CRP_PLUGIN_API const CrpEnum* crpNamespaceGetEnum(const CrpNamespace* ns, size_t index); + CRP_PLUGIN_API size_t crpNamespaceGetNamespaceCount(const CrpNamespace* ns); + CRP_PLUGIN_API const CrpNamespace* crpNamespaceGetNamespace(const CrpNamespace* ns, size_t index); + + CRP_PLUGIN_API void crpGenerationContextWrite(CrpGenerationContext* ctx, const char* format, ...); + CRP_PLUGIN_API void crpGenerationContextNewLine(CrpGenerationContext* ctx); + CRP_PLUGIN_API void crpGenerationContextEnterScope(CrpGenerationContext* ctx); + CRP_PLUGIN_API void crpGenerationContextLeaveScope(CrpGenerationContext* ctx, const char* suffix); + CRP_PLUGIN_API const CrpPackage* crpGenerationContextGetPackage(CrpGenerationContext* ctx); + CRP_PLUGIN_API const CrpClass* crpGenerationContextGetClass(CrpGenerationContext* ctx); + CRP_PLUGIN_API const CrpNamespace* crpGenerationContextGetNamespace(CrpGenerationContext* ctx); + CRP_PLUGIN_API const char* crpGenerationContextGetNamespacePath(CrpGenerationContext* ctx); + CRP_PLUGIN_API void* crpGenerationContextGetPrivateData(CrpGenerationContext* ctx); + CRP_PLUGIN_API size_t crpGenerationContextGetHeaderCount(CrpGenerationContext* ctx); + CRP_PLUGIN_API const char* crpGenerationContextGetHeader(CrpGenerationContext* ctx, size_t index); + +#ifdef __cplusplus +} +#endif + +#endif // CONCERTO_PKGGENERATOR_PLUGINAPI_H diff --git a/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp b/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp new file mode 100644 index 0000000..630b31a --- /dev/null +++ b/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp @@ -0,0 +1,600 @@ +// +// Created by arthur - Plugin Registry Implementation +// + +#include "Concerto/PackageGenerator/Plugin/PluginRegistry.hpp" + +#include +#include +#include +#include + +#include + +#include "Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp" + +namespace cct +{ + PluginRegistry::~PluginRegistry() + { + UnloadAll(); + } + + bool PluginRegistry::LoadPlugin(std::string_view path) + { + LoadedPlugin plugin; + + if (!plugin.library.Load(path)) + { + Logger::Error("Failed to load plugin: {}", path); + return false; + } + + // Get the plugin entry point + auto getTableFunc = plugin.library.GetFunction( + CRP_PLUGIN_ENTRY_POINT_NAME); + if (!getTableFunc) + { + Logger::Error("Plugin missing entry point '{}': {}", CRP_PLUGIN_ENTRY_POINT_NAME, path); + return false; + } + + plugin.functionTable = getTableFunc(); + if (!plugin.functionTable) + { + Logger::Error("Plugin entry point returned nullptr: {}", path); + return false; + } + + // Check API version + if (plugin.functionTable->apiVersion != CRP_PLUGIN_API_VERSION) + { + Logger::Error("Plugin API version mismatch: expected {}, got {} for plugin: {}", + CRP_PLUGIN_API_VERSION, plugin.functionTable->apiVersion, path); + return false; + } + + // GetInfo is required + if (!plugin.functionTable->GetInfo) + { + Logger::Error("Plugin missing required GetInfo function: {}", path); + return false; + } + + // Call OnLoad if provided + if (plugin.functionTable->OnLoad) + { + if (!plugin.functionTable->OnLoad(&plugin.privateData)) + { + Logger::Error("Plugin OnLoad failed: {}", path); + return false; + } + } + + // Log plugin info + CrpPluginInfo info{}; + plugin.functionTable->GetInfo(&info); + Logger::Info("Loaded plugin: {} v{} by {}", + info.name ? info.name : "unknown", + info.version ? info.version : "unknown", + info.author ? info.author : "unknown"); + + m_loadedPlugins.push_back(std::move(plugin)); + SortPluginsByPriority(); + + return true; + } + + bool PluginRegistry::LoadPluginsFromDirectory(std::string_view path) + { + namespace fs = std::filesystem; + + if (!fs::exists(path) || !fs::is_directory(path)) + { + Logger::Warning("Plugin directory does not exist: {}", path); + return false; + } + + bool allLoaded = true; + for (const auto& entry : fs::directory_iterator(path)) + { + if (!entry.is_regular_file()) + continue; + + auto ext = entry.path().extension(); + if (ext != CONCERTO_DYNLIB_EXTENSION) + continue; + + if (!LoadPlugin(entry.path().string())) + allLoaded = false; + } + + return allLoaded; + } + + std::span PluginRegistry::GetPlugins() const + { + return m_sortedPlugins; + } + + void PluginRegistry::UnloadAll() + { + // Call OnUnload for all plugins in reverse order + for (auto it = m_loadedPlugins.rbegin(); it != m_loadedPlugins.rend(); ++it) + { + if (it->functionTable && it->functionTable->OnUnload) + { + it->functionTable->OnUnload(it->privateData); + } + } + m_loadedPlugins.clear(); + m_sortedPlugins.clear(); + } + + void PluginRegistry::TransformPackage(Package& package) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->TransformPackage) + entry.functionTable->TransformPackage(reinterpret_cast(&package)); + } + } + + void PluginRegistry::InvokeBeforePackageGeneration(GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforePackageGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforePackageGeneration(reinterpret_cast(ctx.package), reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnPackageGeneration(GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnPackageGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnPackageGeneration(reinterpret_cast(ctx.package), reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterPackageGeneration(GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterPackageGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterPackageGeneration(reinterpret_cast(ctx.package), reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeNamespaceGeneration(const Namespace& ns, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeNamespaceGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeNamespaceGeneration( + reinterpret_cast(&ns), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnNamespaceGeneration(const Namespace& ns, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnNamespaceGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnNamespaceGeneration( + reinterpret_cast(&ns), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterNamespaceGeneration(const Namespace& ns, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterNamespaceGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterNamespaceGeneration( + reinterpret_cast(&ns), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeMemberGeneration(const Class::Member& member, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeMemberGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeMemberGeneration( + reinterpret_cast(&member), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnMemberGeneration(const Class::Member& member, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnMemberGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnMemberGeneration( + reinterpret_cast(&member), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterMemberGeneration(const Class::Member& member, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterMemberGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterMemberGeneration( + reinterpret_cast(&member), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeMethodGeneration(const Class::Method& method, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeMethodGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeMethodGeneration( + reinterpret_cast(&method), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnMethodGeneration(const Class::Method& method, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnMethodGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnMethodGeneration( + reinterpret_cast(&method), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterMethodGeneration(const Class::Method& method, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterMethodGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterMethodGeneration( + reinterpret_cast(&method), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeEnumGeneration(const Enum& enumeration, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeEnumGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeEnumGeneration( + reinterpret_cast(&enumeration), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnEnumGeneration(const Enum& enumeration, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnEnumGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnEnumGeneration( + reinterpret_cast(&enumeration), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterEnumGeneration(const Enum& enumeration, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterEnumGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterEnumGeneration( + reinterpret_cast(&enumeration), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeEnumElementGeneration(const Enum::Element& element, + GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeEnumElementGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeEnumElementGeneration( + reinterpret_cast(&element), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnEnumElementGeneration(const Enum::Element& element, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnEnumElementGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnEnumElementGeneration( + reinterpret_cast(&element), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterEnumElementGeneration(const Enum::Element& element, + GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterEnumElementGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterEnumElementGeneration( + reinterpret_cast(&element), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeTemplateClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeTemplateClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeTemplateClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnTemplateClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnTemplateClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnTemplateClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterTemplateClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterTemplateClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterTemplateClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeTemplateSpecializationGeneration(const Class& klass, const std::string& specialization, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeTemplateSpecializationGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeTemplateSpecializationGeneration( + reinterpret_cast(&klass), + specialization.c_str(), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnTemplateSpecializationGeneration(const Class& klass, const std::string& specialization, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnTemplateSpecializationGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnTemplateSpecializationGeneration( + reinterpret_cast(&klass), + specialization.c_str(), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterTemplateSpecializationGeneration(const Class& klass, const std::string& specialization, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterTemplateSpecializationGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterTemplateSpecializationGeneration( + reinterpret_cast(&klass), + specialization.c_str(), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeBeforeGenericClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->BeforeGenericClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->BeforeGenericClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeOnGenericClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->OnGenericClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->OnGenericClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + void PluginRegistry::InvokeAfterGenericClassGeneration(const Class& klass, GenerationContext& ctx) const + { + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->AfterGenericClassGeneration) + { + ctx.pluginPrivateData = entry.privateData; + entry.functionTable->AfterGenericClassGeneration( + reinterpret_cast(&klass), + reinterpret_cast(&ctx)); + } + } + } + + bool PluginRegistry::HandleAnnotation( + std::string_view annotationType, + std::string_view entityName, + const TomlAttributes& attributes, + GenerationContext& ctx) const + { + std::string typeStr(annotationType); + std::string nameStr(entityName); + + for (const auto& entry : m_sortedPlugins) + { + if (entry.functionTable->HandleAnnotation) + { + ctx.pluginPrivateData = entry.privateData; + if (entry.functionTable->HandleAnnotation( + typeStr.c_str(), + nameStr.c_str(), + reinterpret_cast(&attributes), + reinterpret_cast(&ctx))) + return true; + } + } + return false; + } + + void PluginRegistry::SortPluginsByPriority() + { + m_sortedPlugins.clear(); + for (auto& loaded : m_loadedPlugins) + m_sortedPlugins.push_back({loaded.functionTable, loaded.privateData}); + + std::ranges::sort(m_sortedPlugins, [](const auto& a, const auto& b) + { + int32_t priorityA = a.functionTable->GetPriority ? a.functionTable->GetPriority() : 0; + int32_t priorityB = b.functionTable->GetPriority ? b.functionTable->GetPriority() : 0; + return priorityA > priorityB; }); + } + +} // namespace cct diff --git a/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.hpp b/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.hpp new file mode 100644 index 0000000..6eba20e --- /dev/null +++ b/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.hpp @@ -0,0 +1,111 @@ +// +// Created by arthur - Plugin Registry for Concerto PackageGenerator +// + +#ifndef CONCERTO_PKGGENERATOR_PLUGINREGISTRY_HPP +#define CONCERTO_PKGGENERATOR_PLUGINREGISTRY_HPP + +#include +#include +#include +#include +#include +#include + +#include + +#include "Concerto/PackageGenerator/Defines.hpp" +#include "Concerto/PackageGenerator/Plugin/PluginApi.h" +#include "Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp" + +namespace cct +{ + class CCT_PKGGENERATOR_API PluginRegistry + { + public: + struct SortedPlugin + { + const CrpPluginFunctionTable* functionTable = nullptr; + void* privateData = nullptr; + }; + + PluginRegistry() = default; + ~PluginRegistry(); + + PluginRegistry(const PluginRegistry&) = delete; + PluginRegistry& operator=(const PluginRegistry&) = delete; + PluginRegistry(PluginRegistry&&) noexcept = default; + PluginRegistry& operator=(PluginRegistry&&) noexcept = default; + + bool LoadPlugin(std::string_view path); + bool LoadPluginsFromDirectory(std::string_view path); + + void UnloadAll(); + + [[nodiscard]] std::span GetPlugins() const; + + void TransformPackage(Package& package) const; + + void InvokeBeforePackageGeneration(GenerationContext& ctx) const; + void InvokeOnPackageGeneration(GenerationContext& ctx) const; + void InvokeAfterPackageGeneration(GenerationContext& ctx) const; + + void InvokeBeforeNamespaceGeneration(const Namespace& ns, GenerationContext& ctx) const; + void InvokeOnNamespaceGeneration(const Namespace& ns, GenerationContext& ctx) const; + void InvokeAfterNamespaceGeneration(const Namespace& ns, GenerationContext& ctx) const; + + void InvokeBeforeClassGeneration(const Class& klass, GenerationContext& ctx) const; + void InvokeOnClassGeneration(const Class& klass, GenerationContext& ctx) const; + void InvokeAfterClassGeneration(const Class& klass, GenerationContext& ctx) const; + + void InvokeBeforeMemberGeneration(const Class::Member& member, GenerationContext& ctx) const; + void InvokeOnMemberGeneration(const Class::Member& member, GenerationContext& ctx) const; + void InvokeAfterMemberGeneration(const Class::Member& member, GenerationContext& ctx) const; + + void InvokeBeforeMethodGeneration(const Class::Method& method, GenerationContext& ctx) const; + void InvokeOnMethodGeneration(const Class::Method& method, GenerationContext& ctx) const; + void InvokeAfterMethodGeneration(const Class::Method& method, GenerationContext& ctx) const; + + void InvokeBeforeEnumGeneration(const Enum& enumeration, GenerationContext& ctx) const; + void InvokeOnEnumGeneration(const Enum& enumeration, GenerationContext& ctx) const; + void InvokeAfterEnumGeneration(const Enum& enumeration, GenerationContext& ctx) const; + + void InvokeBeforeEnumElementGeneration(const Enum::Element& value, GenerationContext& ctx) const; + void InvokeOnEnumElementGeneration(const Enum::Element& value, GenerationContext& ctx) const; + void InvokeAfterEnumElementGeneration(const Enum::Element& value, GenerationContext& ctx) const; + + void InvokeBeforeTemplateClassGeneration(const Class& klass, GenerationContext& ctx) const; + void InvokeOnTemplateClassGeneration(const Class& klass, GenerationContext& ctx) const; + void InvokeAfterTemplateClassGeneration(const Class& klass, GenerationContext& ctx) const; + + void InvokeBeforeTemplateSpecializationGeneration(const Class& klass, const std::string& specialization, GenerationContext& ctx) const; + void InvokeOnTemplateSpecializationGeneration(const Class& klass, const std::string& specialization, GenerationContext& ctx) const; + void InvokeAfterTemplateSpecializationGeneration(const Class& klass, const std::string& specialization, GenerationContext& ctx) const; + + void InvokeBeforeGenericClassGeneration(const Class& klass, GenerationContext& ctx) const; + void InvokeOnGenericClassGeneration(const Class& klass, GenerationContext& ctx) const; + void InvokeAfterGenericClassGeneration(const Class& klass, GenerationContext& ctx) const; + + bool HandleAnnotation( + std::string_view annotationType, + std::string_view entityName, + const TomlAttributes& attributes, + GenerationContext& ctx) const; + + private: + struct LoadedPlugin + { + cct::DynLib library; + const CrpPluginFunctionTable* functionTable = nullptr; + void* privateData = nullptr; + }; + + std::vector m_loadedPlugins; + std::vector m_sortedPlugins; + + void SortPluginsByPriority(); + }; + +} // namespace cct + +#endif // CONCERTO_PKGGENERATOR_PLUGINREGISTRY_HPP diff --git a/Src/Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp b/Src/Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp new file mode 100644 index 0000000..c3cbdca --- /dev/null +++ b/Src/Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp @@ -0,0 +1,117 @@ +// +// Created by arthur +// + +#ifndef CONCERTO_PKGGENERATOR_REFLECTIONGENERATORPLUGIN_HPP +#define CONCERTO_PKGGENERATOR_REFLECTIONGENERATORPLUGIN_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "Concerto/PackageGenerator/Defines.hpp" + +namespace cct +{ + /** + * Context passed to generation hooks. + * Contains the current state of generation and output stream. + */ + struct GenerationContext + { + const Package* package = nullptr; + const Namespace* currentNamespace = nullptr; + const Class* currentClass = nullptr; + const Enum* currentEnum = nullptr; + const Enum::Element* currentEnumElement = nullptr; + const Class::Member* currentMember = nullptr; + const Class::Method* currentMethod = nullptr; + const char* namespacePath = nullptr; + std::ostream* outputStream = nullptr; + std::size_t* indentLevel = nullptr; + std::string_view apiMacro; + uint64_t Seed = 0; + void* pluginPrivateData = nullptr; + const char* const* headers = nullptr; + std::size_t headerCount = 0; + + template + void Write(const std::format_string formatStr, T&&... args) + { + if (outputStream == nullptr || indentLevel == nullptr) + { + return; + } + using namespace std::string_view_literals; + std::string data = std::vformat(formatStr.get(), std::make_format_args(args...)); + auto lines = data | std::ranges::views::split("\n"sv); + + for (auto line : lines) + { + *outputStream << std::string(*indentLevel, '\t'); + *outputStream << std::string_view(line.data(), line.size()); + } + *outputStream << '\n'; + } + + void Write(std::string_view str) + { + if (outputStream == nullptr || indentLevel == nullptr) + return; + using namespace std::string_view_literals; + auto lines = str | std::ranges::views::split("\n"sv); + + for (auto line : lines) + { + *outputStream << std::string(*indentLevel, '\t'); + *outputStream << std::string_view(line.data(), line.size()); + } + *outputStream << '\n'; + } + + void NewLine() const + { + if (outputStream != nullptr) + { + *outputStream << '\n'; + } + } + + void EnterScope() const + { + if (outputStream == nullptr || indentLevel == nullptr) + { + return; + } + *outputStream << std::string(*indentLevel, '\t') << "{\n"; + ++(*indentLevel); + } + + void LeaveScope(std::string_view str = "") const + { + if (outputStream == nullptr || indentLevel == nullptr) + { + return; + } + --(*indentLevel); + *outputStream << std::string(*indentLevel, '\t') << "}" << str << "\n"; + } + }; + + /** + * Plugin information structure. + */ + struct PluginInfo + { + std::string_view name; + std::string_view version; + std::string_view author; + std::string_view description; + }; +} // namespace cct + +#endif // CONCERTO_PKGGENERATOR_REFLECTIONGENERATORPLUGIN_HPP diff --git a/Src/Concerto/PackageGenerator/main.cpp b/Src/Concerto/PackageGenerator/main.cpp index 5f79d0d..f364876 100644 --- a/Src/Concerto/PackageGenerator/main.cpp +++ b/Src/Concerto/PackageGenerator/main.cpp @@ -1,15 +1,215 @@ #include #include #include +#include #include #include #include #include +#include "Concerto/Core/Defines.hpp" +#include "Concerto/PackageGenerator/ChunkedStreamBuf.hpp" #include "Concerto/PackageGenerator/ClangParser/ClangParser.hpp" -#include "Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp" -#include "Concerto/PackageGenerator/HeaderGenerator/HeaderGenerator.hpp" +#include "Concerto/PackageGenerator/Plugin/PluginRegistry.hpp" + +class PackageVisitor +{ +public: + PackageVisitor(const Package& package, const cct::PluginRegistry::SortedPlugin& plugin, std::ostream& outputStream, + const std::vector& headers) : + m_outputStream(outputStream), + m_package(&package), + m_functionTable(plugin.functionTable), + m_privateData(plugin.privateData) + { + // Build header pointers + std::vector headerPtrs; + headerPtrs.reserve(headers.size()); + for (const auto& h : headers) + headerPtrs.push_back(h.c_str()); + + ctx.package = &package; + ctx.outputStream = &m_outputStream; + ctx.indentLevel = &m_indentLevel; + ctx.pluginPrivateData = m_privateData; + ctx.headers = headerPtrs.data(); + ctx.headerCount = headerPtrs.size(); + + InvokeIfSet(m_functionTable->BeforePackageGeneration, reinterpret_cast(&package), reinterpret_cast(&ctx)); + InvokeIfSet(m_functionTable->OnPackageGeneration, reinterpret_cast(&package), reinterpret_cast(&ctx)); + { + InvokeOnPackageElements(package); + } + InvokeIfSet(m_functionTable->AfterPackageGeneration, reinterpret_cast(&package), reinterpret_cast(&ctx)); + } + +private: + std::ostream& m_outputStream; + std::size_t m_indentLevel = 0; + std::string m_namespacePath; + cct::GenerationContext ctx; + const Package* m_package; + const CrpPluginFunctionTable* m_functionTable; + void* m_privateData; + + template + void InvokeIfSet(Func func, Args&&... args) + { + if (func) + func(std::forward(args)...); + } + + void InvokeOnPackageElements(const Package& package) + { + for (const auto& enumeration : package.enums) + InvokeOnEnumGeneration(enumeration); + + for (const auto& klass : package.classes) + InvokeOnClassGeneration(klass); + + for (const auto& ns : package.namespaces) + InvokeOnNamespaceGeneration(ns); + } + + void InvokeOnEnumGeneration(const Enum& enumeration) + { + ctx.currentEnum = &enumeration; + auto* crpEnum = reinterpret_cast(&enumeration); + auto* crpCtx = reinterpret_cast(&ctx); + + InvokeIfSet(m_functionTable->BeforeEnumGeneration, crpEnum, crpCtx); + InvokeIfSet(m_functionTable->OnEnumGeneration, crpEnum, crpCtx); + for (const auto& value : enumeration.elements) + { + ctx.currentEnumElement = &value; + auto* crpElem = reinterpret_cast(&value); + InvokeIfSet(m_functionTable->BeforeEnumElementGeneration, crpElem, crpCtx); + InvokeIfSet(m_functionTable->OnEnumElementGeneration, crpElem, crpCtx); + InvokeIfSet(m_functionTable->AfterEnumElementGeneration, crpElem, crpCtx); + ctx.currentEnumElement = nullptr; + } + InvokeIfSet(m_functionTable->AfterEnumGeneration, crpEnum, crpCtx); + ctx.currentEnum = nullptr; + } + + void InvokeOnClassGeneration(const Class& klass) + { + ctx.currentClass = &klass; + auto* crpCls = reinterpret_cast(&klass); + auto* crpCtx = reinterpret_cast(&ctx); + + // Method wrapper classes must be generated BEFORE the class that references them + // Skip for template classes since methods reference unresolved template parameters + if (!klass.isTemplateClass) + { + InvokeOnClassMethods(klass); + } + + if (klass.isGenericClass) + { + InvokeIfSet(m_functionTable->BeforeGenericClassGeneration, crpCls, crpCtx); + InvokeIfSet(m_functionTable->OnGenericClassGeneration, crpCls, crpCtx); + } + else if (klass.isTemplateClass) + { + // Generate specialization classes before the template class that references them + for (const auto& spec : klass.templateSpecializations) + { + InvokeIfSet(m_functionTable->BeforeTemplateSpecializationGeneration, crpCls, spec.c_str(), crpCtx); + InvokeIfSet(m_functionTable->OnTemplateSpecializationGeneration, crpCls, spec.c_str(), crpCtx); + InvokeIfSet(m_functionTable->AfterTemplateSpecializationGeneration, crpCls, spec.c_str(), crpCtx); + } + InvokeIfSet(m_functionTable->BeforeTemplateClassGeneration, crpCls, crpCtx); + InvokeIfSet(m_functionTable->OnTemplateClassGeneration, crpCls, crpCtx); + } + else + { + InvokeIfSet(m_functionTable->BeforeClassGeneration, crpCls, crpCtx); + InvokeIfSet(m_functionTable->OnClassGeneration, crpCls, crpCtx); + } + + if (!klass.isGenericClass && !klass.isTemplateClass) + { + InvokeOnClassMembers(klass); + } + + if (klass.isGenericClass) + InvokeIfSet(m_functionTable->AfterGenericClassGeneration, crpCls, crpCtx); + else if (klass.isTemplateClass) + InvokeIfSet(m_functionTable->AfterTemplateClassGeneration, crpCls, crpCtx); + else + InvokeIfSet(m_functionTable->AfterClassGeneration, crpCls, crpCtx); + + ctx.currentClass = nullptr; + } + + void InvokeOnClassMembers(const Class& klass) + { + auto* crpCtx = reinterpret_cast(&ctx); + for (const auto& member : klass.members) + { + ctx.currentMember = &member; + auto* crpMember = reinterpret_cast(&member); + InvokeIfSet(m_functionTable->BeforeMemberGeneration, crpMember, crpCtx); + InvokeIfSet(m_functionTable->OnMemberGeneration, crpMember, crpCtx); + InvokeIfSet(m_functionTable->AfterMemberGeneration, crpMember, crpCtx); + ctx.currentMember = nullptr; + } + } + + void InvokeOnClassMethods(const Class& klass) + { + auto* crpCtx = reinterpret_cast(&ctx); + for (const auto& method : klass.methods) + { + ctx.currentMethod = &method; + auto* crpMethod = reinterpret_cast(&method); + InvokeIfSet(m_functionTable->BeforeMethodGeneration, crpMethod, crpCtx); + InvokeIfSet(m_functionTable->OnMethodGeneration, crpMethod, crpCtx); + InvokeIfSet(m_functionTable->AfterMethodGeneration, crpMethod, crpCtx); + ctx.currentMethod = nullptr; + } + } + + void InvokeOnNamespaceGeneration(const Namespace& ns) + { + ctx.currentNamespace = &ns; + std::string previousPath = m_namespacePath; + if (!m_namespacePath.empty()) + m_namespacePath += "::"; + m_namespacePath += ns.name; + ctx.namespacePath = m_namespacePath.c_str(); + + auto* crpNs = reinterpret_cast(&ns); + auto* crpCtx = reinterpret_cast(&ctx); + + InvokeIfSet(m_functionTable->BeforeNamespaceGeneration, crpNs, crpCtx); + InvokeIfSet(m_functionTable->OnNamespaceGeneration, crpNs, crpCtx); + { + for (const auto& nestedNs : ns.namespaces) + InvokeOnNamespaceGeneration(nestedNs); + + for (const auto& enumeration : ns.enums) + InvokeOnEnumGeneration(enumeration); + + for (const auto& klass : ns.classes) + InvokeOnClassGeneration(klass); + } + InvokeIfSet(m_functionTable->AfterNamespaceGeneration, crpNs, crpCtx); + m_namespacePath = previousPath; + ctx.namespacePath = m_namespacePath.empty() ? nullptr : m_namespacePath.c_str(); + ctx.currentNamespace = nullptr; + } +}; + +void WaitForDebugger() +{ + while (!cct::IsDebuggerAttached()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} int main(int argc, const char** argv) { @@ -20,13 +220,16 @@ int main(int argc, const char** argv) std::vector headers; std::string resourceDir; std::string sdk; + std::vector plugins; + std::string pluginDir; + bool noBuiltinGenerators = false; try { cxxopts::Options options("Concerto Reflection Generator", "Concerto Reflection Generator"); - options.add_options()("outputDir", "Output directory", cxxopts::value())("I,include", "Include directories (one or more)", cxxopts::value>())("D,define", "Preprocessor defines (one or more)", cxxopts::value>())("s,source", "Source files (one or more)", cxxopts::value>())("H,header", "Header file to include in the generated cpp", cxxopts::value>())("R,resource-dir", "Clang resource directory", cxxopts::value())("S,sdk", "SDK path", cxxopts::value())("v,version", "1.0.0")("h,help", "Show help"); + options.add_options()("outputDir", "Output directory", cxxopts::value())("I,include", "Include directories (one or more)", cxxopts::value>())("D,define", "Preprocessor defines (one or more)", cxxopts::value>())("s,source", "Source files (one or more)", cxxopts::value>())("H,header", "Header file to include in the generated cpp", cxxopts::value>())("R,resource-dir", "Clang resource directory", cxxopts::value())("S,sdk", "SDK path", cxxopts::value())("P,plugin", "Plugin paths (one or more)", cxxopts::value>())("plugin-dir", "Directory containing plugins to load", cxxopts::value())("v,version", "1.0.0")("h,help", "Show help"); options.parse_positional({"outputDir"}); @@ -64,24 +267,95 @@ int main(int argc, const char** argv) resourceDir = result["resource-dir"].as(); if (result.count("sdk")) sdk = result["sdk"].as(); + if (result.count("plugin")) + { + plugins = result["plugin"].as>(); + } + if (result.count("plugin-dir")) + { + pluginDir = result["plugin-dir"].as(); + } - // std::this_thread::sleep_for(std::chrono::seconds(5)); cct::ClangParser parser; Package* package = parser.Parse(includes, defines, sources, resourceDir, sdk); if (!package) + { return EXIT_FAILURE; + } + + cct::Logger::Info("Creating plugin registry..."); + cct::PluginRegistry pluginRegistry; + for (const auto& pluginPath : plugins) + { + cct::Logger::Info("Loading plugin: {}", pluginPath); + if (!pluginRegistry.LoadPlugin(pluginPath)) + { + cct::Logger::Error("Failed to load plugin: {}", pluginPath); + } + } + if (!pluginDir.empty()) + { + pluginRegistry.LoadPluginsFromDirectory(pluginDir); + } + + if (pluginRegistry.GetPlugins().empty()) + { + cct::Logger::Error("No plugins loaded. Provide at least one plugin with -P or --plugin-dir."); + return EXIT_FAILURE; + } - std::filesystem::path file(outputFolder); - std::filesystem::create_directories(file); + cct::Logger::Info("Transforming package..."); + pluginRegistry.TransformPackage(*package); + + cct::Logger::Info("Generating files via plugins..."); + + std::filesystem::path outputPath(outputFolder); + std::filesystem::create_directories(outputPath); + + for (const auto& plugin : pluginRegistry.GetPlugins()) + { + CrpPluginInfo info{}; + plugin.functionTable->GetInfo(&info); + + if (info.outputFileExtension == nullptr || std::strlen(info.outputFileExtension) == 0) + { + cct::Logger::Error("Plugin '{}' returned invalid output file extension.", info.name != nullptr ? info.name : "Unknown"); + continue; + } + + std::string extension = info.outputFileExtension; + std::filesystem::path outPath = outputPath / (package->name + "Package" + extension); + + std::ofstream outFile(outPath); + if (!outFile) + { + cct::Logger::Error("Failed to open file for writing: {}", outPath.string()); + continue; + } + + { + cct::AsyncChunkedStreamBuf asyncBuf(outFile); + std::ostream asyncStream(&asyncBuf); + + PackageVisitor visitor(*package, plugin, asyncStream, headers); + } + + if (outFile.tellp() <= 0) + { + outFile.close(); + std::filesystem::remove(outPath); + } + else + { + cct::Logger::Info("Generated file: {}", outPath.string()); + } + } - cct::HeaderGenerator headerGenerator((file / (package->name + "Package.gen.hpp")).string()); - headerGenerator.Generate(*package, headers); - cct::CppGenerator cppGenerator((file / (package->name + "Package.gen.cpp")).string()); - cppGenerator.Generate(*package, headers); + cct::Logger::Info("Done!"); } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << "\n"; + cct::Logger::Error("Error: {}", e.what()); return EXIT_FAILURE; } diff --git a/Src/Concerto/Reflection/Enumeration/Enumeration.cpp b/Src/Concerto/Reflection/Enumeration/Enumeration.cpp index 2cf9140..4e1351d 100644 --- a/Src/Concerto/Reflection/Enumeration/Enumeration.cpp +++ b/Src/Concerto/Reflection/Enumeration/Enumeration.cpp @@ -2,9 +2,8 @@ // Created by arthur on 31/12/2025 // -#include "Concerto/Reflection/Enumeration/Enumeration.refl.hpp" - #include "Concerto/Core/Assert.hpp" +#include "Concerto/Reflection/Enumeration/Enumeration.refl.hpp" #include "Concerto/Reflection/Enumeration/EnumerationClass.hpp" #include "Concerto/Reflection/Enumeration/EnumIterator.hpp" diff --git a/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp b/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp index 466d7b7..d30b86a 100644 --- a/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp +++ b/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp @@ -39,7 +39,7 @@ namespace cct::refl [[nodiscard]] EnumIterable Enumerate() const; [[nodiscard]] std::unique_ptr CreateDefaultObject() const override; - + template requires(std::is_base_of_v && std::is_polymorphic_v) [[nodiscard]] std::unique_ptr CreateDefaultObject() const diff --git a/Src/Concerto/Reflection/GlobalNamespace/GlobalNamespace.cpp b/Src/Concerto/Reflection/GlobalNamespace/GlobalNamespace.cpp index 7643bd2..3cb66a4 100644 --- a/Src/Concerto/Reflection/GlobalNamespace/GlobalNamespace.cpp +++ b/Src/Concerto/Reflection/GlobalNamespace/GlobalNamespace.cpp @@ -99,6 +99,8 @@ namespace cct::refl { if (ns->GetName() == names[0]) { + if (names.size() == 1) + return ns; Namespace* nestedNs = ns->GetNamespace(names.subspan(1)); if (nestedNs) return nestedNs; diff --git a/Src/Tests/Class.cpp b/Src/Tests/Class.cpp index 844a17c..1aa0351 100644 --- a/Src/Tests/Class.cpp +++ b/Src/Tests/Class.cpp @@ -22,7 +22,7 @@ SCENARIO("Class metadata verification") REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); packageLoader.LoadPackages(); - CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 12); + CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 13); CHECK(cct::refl::GlobalNamespace::Get().GetNamespaceCount() == 1); THEN("We are getting the class Object") diff --git a/Src/Tests/Method.cpp b/Src/Tests/Method.cpp index ccf0605..fe5275f 100644 --- a/Src/Tests/Method.cpp +++ b/Src/Tests/Method.cpp @@ -23,7 +23,7 @@ SCENARIO("Method") REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); packageLoader.LoadPackages(); - CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 12); + CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 13); CHECK(cct::refl::GlobalNamespace::Get().GetNamespaceCount() == 1); THEN("We are invoking a method named 'Bar'") diff --git a/Src/Tests/Namespace.cpp b/Src/Tests/Namespace.cpp index 28aca89..c680cac 100644 --- a/Src/Tests/Namespace.cpp +++ b/Src/Tests/Namespace.cpp @@ -22,7 +22,7 @@ SCENARIO("Namespace") REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); packageLoader.LoadPackages(); - CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 12); + CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 13); CHECK(cct::refl::GlobalNamespace::Get().GetNamespaceCount() == 1); THEN("We are getting the cct namespace") diff --git a/Src/Tests/Plugin.cpp b/Src/Tests/Plugin.cpp new file mode 100644 index 0000000..8f86a2d --- /dev/null +++ b/Src/Tests/Plugin.cpp @@ -0,0 +1,374 @@ +// +// Created by arthur +// + +#include + +#include "Concerto/PackageGenerator/Plugin/PluginApi.h" +#include "Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp" +#include + +SCENARIO("GenerationContext write operations") +{ + using namespace cct; + + GIVEN("A GenerationContext with a valid output stream") + { + std::ostringstream stream; + std::size_t indentLevel = 0; + + GenerationContext ctx; + ctx.outputStream = &stream; + ctx.indentLevel = &indentLevel; + + WHEN("Write is called with a simple string") + { + ctx.Write("Hello World"); + + THEN("The string is written with a newline") + { + CHECK(stream.str() == "Hello World\n"); + } + } + + WHEN("Write is called with format arguments") + { + ctx.Write("Value: {}", 42); + + THEN("The formatted string is written") + { + CHECK(stream.str() == "Value: 42\n"); + } + } + + WHEN("NewLine is called") + { + ctx.NewLine(); + + THEN("A newline is added") + { + CHECK(stream.str() == "\n"); + } + } + + WHEN("EnterScope is called") + { + ctx.EnterScope(); + + THEN("An opening brace is written and indent level increases") + { + CHECK(stream.str() == "{\n"); + CHECK(indentLevel == 1); + } + } + + WHEN("LeaveScope is called after EnterScope") + { + ctx.EnterScope(); + stream.str(""); + ctx.LeaveScope(); + + THEN("A closing brace is written and indent level decreases") + { + CHECK(stream.str() == "}\n"); + CHECK(indentLevel == 0); + } + } + + WHEN("LeaveScope is called with a suffix") + { + ctx.EnterScope(); + stream.str(""); + ctx.LeaveScope(";"); + + THEN("The closing brace has the suffix") + { + CHECK(stream.str() == "};\n"); + } + } + + WHEN("Writing with indentation") + { + ctx.EnterScope(); + stream.str(""); + ctx.Write("indented line"); + + THEN("The line has proper indentation") + { + CHECK(stream.str() == "\tindented line\n"); + } + } + + WHEN("Multiple nested scopes") + { + ctx.EnterScope(); + ctx.EnterScope(); + stream.str(""); + ctx.Write("double indented"); + + THEN("The line has double indentation") + { + CHECK(stream.str() == "\t\tdouble indented\n"); + CHECK(indentLevel == 2); + } + } + } + + GIVEN("A GenerationContext with null pointers") + { + GenerationContext ctx; + ctx.outputStream = nullptr; + ctx.indentLevel = nullptr; + + WHEN("Write is called") + { + THEN("It does not crash") + { + CHECK_NOTHROW(ctx.Write("test")); + } + } + + WHEN("NewLine is called") + { + THEN("It does not crash") + { + CHECK_NOTHROW(ctx.NewLine()); + } + } + + WHEN("EnterScope is called") + { + THEN("It does not crash") + { + CHECK_NOTHROW(ctx.EnterScope()); + } + } + + WHEN("LeaveScope is called") + { + THEN("It does not crash") + { + CHECK_NOTHROW(ctx.LeaveScope()); + } + } + } +} + +SCENARIO("C API GenerationContext functions") +{ + GIVEN("A GenerationContext cast to C API type") + { + std::ostringstream stream; + std::size_t indentLevel = 0; + + cct::GenerationContext ctx; + ctx.outputStream = &stream; + ctx.indentLevel = &indentLevel; + + auto* cCtx = reinterpret_cast(&ctx); + + WHEN("crpGenerationContextWrite is called with simple string") + { + crpGenerationContextWrite(cCtx, "Hello from C API"); + + THEN("The string is written with a newline") + { + CHECK(stream.str() == "Hello from C API\n"); + } + } + + WHEN("crpGenerationContextWrite is called with format arguments") + { + crpGenerationContextWrite(cCtx, "Value: %d, String: %s", 42, "test"); + + THEN("The formatted string is written") + { + CHECK(stream.str() == "Value: 42, String: test\n"); + } + } + + WHEN("crpGenerationContextNewLine is called") + { + crpGenerationContextNewLine(cCtx); + + THEN("A newline is added") + { + CHECK(stream.str() == "\n"); + } + } + + WHEN("crpGenerationContextEnterScope is called") + { + crpGenerationContextEnterScope(cCtx); + + THEN("An opening brace is written and indent level increases") + { + CHECK(stream.str() == "{\n"); + CHECK(indentLevel == 1); + } + } + + WHEN("crpGenerationContextLeaveScope is called after EnterScope") + { + crpGenerationContextEnterScope(cCtx); + stream.str(""); + crpGenerationContextLeaveScope(cCtx, nullptr); + + THEN("A closing brace is written and indent level decreases") + { + CHECK(stream.str() == "}\n"); + CHECK(indentLevel == 0); + } + } + + WHEN("crpGenerationContextLeaveScope is called with a suffix") + { + crpGenerationContextEnterScope(cCtx); + stream.str(""); + crpGenerationContextLeaveScope(cCtx, ";"); + + THEN("The closing brace has the suffix") + { + CHECK(stream.str() == "};\n"); + } + } + + WHEN("Writing with indentation using C API") + { + crpGenerationContextEnterScope(cCtx); + stream.str(""); + crpGenerationContextWrite(cCtx, "indented line"); + + THEN("The line has proper indentation") + { + CHECK(stream.str() == "\tindented line\n"); + } + } + + WHEN("Multiple nested scopes using C API") + { + crpGenerationContextEnterScope(cCtx); + crpGenerationContextEnterScope(cCtx); + stream.str(""); + crpGenerationContextWrite(cCtx, "double indented"); + + THEN("The line has double indentation") + { + CHECK(stream.str() == "\t\tdouble indented\n"); + CHECK(indentLevel == 2); + } + } + + WHEN("Building a complete function with C API") + { + crpGenerationContextWrite(cCtx, "void MyFunction()"); + crpGenerationContextEnterScope(cCtx); + crpGenerationContextWrite(cCtx, "int x = %d;", 42); + crpGenerationContextWrite(cCtx, "printf(\"Hello\\n\");"); + crpGenerationContextLeaveScope(cCtx, nullptr); + + THEN("The complete function is generated with proper formatting") + { + std::string expected = + "void MyFunction()\n" + "{\n" + "\tint x = 42;\n" + "\tprintf(\"Hello\\n\");\n" + "}\n"; + CHECK(stream.str() == expected); + } + } + } + + GIVEN("NULL context pointer") + { + WHEN("C API functions are called with NULL") + { + THEN("They do not crash") + { + CHECK_NOTHROW(crpGenerationContextWrite(nullptr, "test")); + CHECK_NOTHROW(crpGenerationContextNewLine(nullptr)); + CHECK_NOTHROW(crpGenerationContextEnterScope(nullptr)); + CHECK_NOTHROW(crpGenerationContextLeaveScope(nullptr, nullptr)); + } + } + } + + GIVEN("GenerationContext with null pointers using C API") + { + cct::GenerationContext ctx; + ctx.outputStream = nullptr; + ctx.indentLevel = nullptr; + + auto* cCtx = reinterpret_cast(&ctx); + + WHEN("C API functions are called") + { + THEN("They do not crash") + { + CHECK_NOTHROW(crpGenerationContextWrite(cCtx, "test")); + CHECK_NOTHROW(crpGenerationContextNewLine(cCtx)); + CHECK_NOTHROW(crpGenerationContextEnterScope(cCtx)); + CHECK_NOTHROW(crpGenerationContextLeaveScope(cCtx, nullptr)); + } + } + } +} + +SCENARIO("crpGenerationContextGetPrivateData") +{ + GIVEN("A GenerationContext with no private data") + { + cct::GenerationContext ctx; + ctx.outputStream = nullptr; + ctx.indentLevel = nullptr; + + auto* cCtx = reinterpret_cast(&ctx); + + WHEN("crpGenerationContextGetPrivateData is called") + { + void* result = crpGenerationContextGetPrivateData(cCtx); + + THEN("It returns nullptr") + { + CHECK(result == nullptr); + } + } + } + + GIVEN("A GenerationContext with private data set") + { + cct::GenerationContext ctx; + ctx.outputStream = nullptr; + ctx.indentLevel = nullptr; + + int dummyData = 42; + ctx.pluginPrivateData = &dummyData; + + auto* cCtx = reinterpret_cast(&ctx); + + WHEN("crpGenerationContextGetPrivateData is called") + { + void* result = crpGenerationContextGetPrivateData(cCtx); + + THEN("It returns the correct pointer") + { + CHECK(result == &dummyData); + CHECK(*static_cast(result) == 42); + } + } + } + + GIVEN("A NULL context") + { + WHEN("crpGenerationContextGetPrivateData is called with NULL") + { + void* result = crpGenerationContextGetPrivateData(nullptr); + + THEN("It returns nullptr") + { + CHECK(result == nullptr); + } + } + } +} diff --git a/Xmake/rules/cct_cpp_reflect.lua b/Xmake/rules/cct_cpp_reflect.lua index cddbc0b..0d61850 100644 --- a/Xmake/rules/cct_cpp_reflect.lua +++ b/Xmake/rules/cct_cpp_reflect.lua @@ -40,6 +40,26 @@ rule("cct_cpp_reflect") table.insert(args, "-s" .. header) end + local function collect_plugins(t, plugins) + for _, dep in ipairs(t:get("deps") or {}) do + local dep_target = project.target(dep) + if dep_target then + local plugin_type = t:extraconf("deps", dep, "plugin") + if plugin_type == "pkg-generator" and dep_target:kind() == "shared" then + local plugin_path = dep_target:targetfile() + if plugin_path and not plugins[plugin_path] then + plugins[plugin_path] = true + table.insert(args, "-P" .. plugin_path) + end + end + collect_plugins(dep_target, plugins) + end + end + end + + local plugins = {} + collect_plugins(target, plugins) + function process_target(t, args, is_root) for _, define in ipairs(t:get("defines")) do local conf = (t:extraconf("defines") or {})[define] diff --git a/libllvm/constants.lua b/libllvm/constants.lua new file mode 100644 index 0000000..7e31e3a --- /dev/null +++ b/libllvm/constants.lua @@ -0,0 +1,375 @@ +--- from llvm/CMakeLists.txt + +function get_llvm_all_projects() + -- @see https://llvm.org/docs/CMake.html + -- Some projects listed here can also go in LLVM_ENABLE_RUNTIMES. They should + -- only appear in one of the two lists. If a project is a valid possiblity for + -- both, prefer putting it in LLVM_ENABLE_RUNTIMES. + return { + "bolt", + "clang", + "clang-tools-extra", -- But we do not build clang tools. + "libclc", + "lld", + "lldb", + "mlir", -- TODO: incompleted. + "polly" + } +end + +function get_llvm_extra_projects() + return { + "flang" + } +end + +function get_llvm_known_projects() + return table.join(get_llvm_all_projects(), get_llvm_extra_projects()) +end + +function get_llvm_all_runtimes() + return { + "libc", + "libunwind", + "libcxxabi", + "pstl", + "libcxx", + "compiler-rt", + "openmp", + "llvm-libgcc", + "offload", + "flang-rt" + } +end + +--- from cmake/llvm/LLVMExports.cmake + +function get_llvm_shared_libraries() + return { + "LLVM", + "Remarks", + "LTO" + } +end + +function get_llvm_static_libraries() + return { + "LLVMExegesisMips", + "LLVMExegesisPowerPC", + "LLVMExegesisAArch64", + "LLVMExegesisX86", + "LLVMOptDriver", + "LLVMExegesis", + "LLVMOrcDebugging", + "LLVMBPFCodeGen", + "LLVMAMDGPUCodeGen", + "LLVMOrcJIT", + "LLVMLTO", + "LLVMPasses", + "LLVMX86CodeGen", + "LLVMRISCVCodeGen", + "LLVMPowerPCCodeGen", + "LLVMMipsCodeGen", + "LLVMARMCodeGen", + "LLVMAArch64CodeGen", + "LLVMNVPTXCodeGen", + "LLVMHexagonCodeGen", + "LLVMCoroutines", + "LLVMWebAssemblyCodeGen", + "LLVMDWARFLinkerParallel", + "LLVMDWARFLinkerClassic", + "LLVMXCoreCodeGen", + "LLVMVECodeGen", + "LLVMSystemZCodeGen", + "LLVMSparcCodeGen", + "LLVMMSP430CodeGen", + "LLVMLoongArchCodeGen", + "LLVMLanaiCodeGen", + "LLVMAVRCodeGen", + "LLVMGlobalISel", + "LLVMipo", + "LLVMWebAssemblyUtils", + "LLVMInterpreter", + "LLVMDWARFLinker", + "LLVMMIRParser", + "LLVMAsmPrinter", + "LLVMSelectionDAG", + "LLVMFrontendOpenMP", + "LLVMCodeGen", + "LLVMFuzzMutate", + "LLVMAMDGPUTargetMCA", + "LLVMAMDGPUDisassembler", + "LLVMAMDGPUAsmParser", + "LLVMMCJIT", + "LLVMScalarOpts", + "LLVMAMDGPUDesc", + "LLVMExecutionEngine", + "LLVMLinker", + "LLVMHipStdPar", + "LLVMObjCARCOpts", + "LLVMVectorize", + "LLVMInstCombine", + "LLVMAggressiveInstCombine", + "LLVMInstrumentation", + "LLVMFrontendOffloading", + "LLVMAMDGPUUtils", + "LLVMTarget", + "LLVMTransformUtils", + "LLVMFrontendDriver", + "LLVMBitWriter", + "LLVMIRPrinter", + "LLVMCoverage", + "LLVMAnalysis", + "LLVMCFIVerify", + "LLVMProfileData", + "LLVMDebuginfod", + "LLVMARMDisassembler", + "LLVMARMAsmParser", + "LLVMSymbolize", + "LLVMDebugInfoLogicalView", + "LLVMTextAPIBinaryReader", + "LLVMDWP", + "LLVMDebugInfoGSYM", + "LLVMXRay", + "LLVMLibDriver", + "LLVMDlltoolDriver", + "LLVMARMDesc", + "LLVMRuntimeDyld", + "LLVMJITLink", + "LLVMDebugInfoPDB", + "LLVMDebugInfoDWARF", + "LLVMObjectYAML", + "LLVMObjCopy", + "LLVMCodeGenData", + "LLVMInterfaceStub", + "LLVMX86TargetMCA", + "LLVMX86AsmParser", + "LLVMWebAssemblyDisassembler", + "LLVMWebAssemblyAsmParser", + "LLVMVEAsmParser", + "LLVMSystemZDisassembler", + "LLVMSystemZAsmParser", + "LLVMSparcAsmParser", + "LLVMRISCVTargetMCA", + "LLVMRISCVDisassembler", + "LLVMRISCVAsmParser", + "LLVMPowerPCAsmParser", + "LLVMMSP430AsmParser", + "LLVMMipsAsmParser", + "LLVMLoongArchDisassembler", + "LLVMLoongArchAsmParser", + "LLVMLanaiDisassembler", + "LLVMLanaiAsmParser", + "LLVMHexagonDisassembler", + "LLVMHexagonAsmParser", + "LLVMBPFAsmParser", + "LLVMAVRAsmParser", + "LLVMAArch64Disassembler", + "LLVMAArch64AsmParser", + "LLVMObject", + "LLVMXCoreDesc", + "LLVMXCoreDisassembler", + "LLVMX86Desc", + "LLVMX86Disassembler", + "LLVMWebAssemblyDesc", + "LLVMVEDesc", + "LLVMVEDisassembler", + "LLVMSystemZDesc", + "LLVMSparcDesc", + "LLVMSparcDisassembler", + "LLVMRISCVDesc", + "LLVMPowerPCDesc", + "LLVMPowerPCDisassembler", + "LLVMNVPTXDesc", + "LLVMMSP430Disassembler", + "LLVMMSP430Desc", + "LLVMMipsDesc", + "LLVMMipsDisassembler", + "LLVMLoongArchDesc", + "LLVMLanaiDesc", + "LLVMHexagonDesc", + "LLVMBPFDesc", + "LLVMBPFDisassembler", + "LLVMAVRDesc", + "LLVMAVRDisassembler", + "LLVMAArch64Desc", + "LLVMIRReader", + "LLVMXCoreInfo", + "LLVMX86Info", + "LLVMWebAssemblyInfo", + "LLVMVEInfo", + "LLVMSystemZInfo", + "LLVMSparcInfo", + "LLVMRISCVInfo", + "LLVMPowerPCInfo", + "LLVMNVPTXInfo", + "LLVMMSP430Info", + "LLVMMipsInfo", + "LLVMLoongArchInfo", + "LLVMLanaiInfo", + "LLVMHexagonInfo", + "LLVMBPFInfo", + "LLVMAVRInfo", + "LLVMARMInfo", + "LLVMAMDGPUInfo", + "LLVMAArch64Info", + "LLVMMCA", + "LLVMMCDisassembler", + "LLVMMCParser", + "LLVMDiff", + "LLVMAsmParser", + "LLVMSandboxIR", + "LLVMAArch64Utils", + "LLVMCFGuard", + "LLVMFrontendHLSL", + "LLVMBitReader", + "LLVMTextAPI", + "LLVMMC", + "LLVMCore", + "LLVMTableGenCommon", + "LLVMWindowsDriver", + "LLVMOrcTargetProcess", + "LLVMBinaryFormat", + "LLVMFuzzerCLI", + "LLVMRemarks", + "LLVMTableGenBasic", + "LLVMWindowsManifest", + "LLVMTargetParser", + "LLVMLineEditor", + "LLVMARMUtils", + "LLVMOrcShared", + "LLVMDebugInfoBTF", + "LLVMDebugInfoCodeView", + "LLVMDebugInfoMSF", + "LLVMOption", + "LLVMFrontendOpenACC", + "LLVMExtensions", + "LLVMBitstreamReader", + "LLVMCodeGenTypes", + "LLVMFileCheck", + "LLVMTableGen", + "LLVMSupport", + "LLVMDemangle", + "Remarks", -- shared + "LTO" -- shared + } +end + +function get_bolt_shared_libraries() + return {} -- TODO +end + +function get_bolt_static_libraries() + return { + "LLVMBOLTRewrite", + "LLVMBOLTRuntimeLibs", + "LLVMBOLTTargetRISCV", + "LLVMBOLTTargetX86", + "LLVMBOLTTargetAArch64", + "LLVMBOLTProfile", + "LLVMBOLTPasses", + "LLVMBOLTCore", + "LLVMBOLTUtils" + } +end + +function get_polly_shared_libraries() + return {} -- TODO +end + +function get_polly_static_libraries() + return { + "Polly", + "LLVMPolly", -- shared + "PollyISL", + } +end + +--- from cmake/clang/ClangTargets.cmake + +function get_clang_shared_libraries() + return { + "clang-cpp", + "clang" -- Clang's stable CAPI (shared) + } +end + +function get_clang_static_libraries() + return { + "clangInterpreter", + "clangFrontendTool", + "clangStaticAnalyzerFrontend", + "clangStaticAnalyzerCheckers", + "clangTransformer", + "clangStaticAnalyzerCore", + "clangToolingRefactoring", + "clangExtractAPI", + "clangCrossTU", + "clangHandleCXX", + "clangDependencyScanning", + "clangIndex", + "clangTooling", + "clangToolingSyntax", + "clangRewriteFrontend", + "clangARCMigrate", + "clangCodeGen", + "clangFrontend", + "clangAnalysisFlowSensitiveModels", + "clangSerialization", + "clangParse", + "clangFormat", + "clangAnalysisFlowSensitive", + "clangSema", + "clangToolingInclusions", + "clangAnalysis", + "clangDynamicASTMatchers", + "clangToolingCore", + "clangInstallAPI", + "clangToolingASTDiff", + "clangToolingInclusionsStdlib", + "clangEdit", + "clangASTMatchers", + "clangRewrite", + "clangAST", + "clangIndexSerialization", + "clangDriver", + "clangLex", + "clangAPINotes", + "clangHandleLLVM", + "clangSupport", + "clangDirectoryWatcher", + "clangBasic", + "clang", -- Clang's stable CAPI (shared) + } +end + +--- from cmake/lld/LLDTargets.cmake + +function get_lld_shared_libraries() + return {} -- TODO +end + +function get_lld_static_libraries() + return { + "lldMinGW", + "lldWasm", + "lldMachO", + "lldELF", + "lldCOFF", + "lldCommon", + } +end + +--- lldb + +function get_lldb_shared_libraries() + return { + "lldb" + } +end + +function get_lldb_static_libraries() + return { + "lldb" -- shared + } +end diff --git a/libllvm/xmake.lua b/libllvm/xmake.lua new file mode 100644 index 0000000..6a0b4b8 --- /dev/null +++ b/libllvm/xmake.lua @@ -0,0 +1,214 @@ +package("libllvm") + set_kind("library") + set_homepage("https://llvm.org/") + set_description("The LLVM Compiler Infrastructure.") + + add_configs("exception", {description = "Enable C++ exception support for LLVM.", default = true, type = "boolean"}) + add_configs("rtti", {description = "Enable C++ RTTI support for LLVM.", default = true, type = "boolean"}) + + add_configs("ms_dia", {description = "Enable DIA SDK to support non-native PDB parsing. (msvc only)", default = true, type = "boolean"}) + add_configs("libffi", {description = "Enable libffi to support the LLVM interpreter to call external functions.", default = false, type = "boolean"}) + add_configs("httplib", {description = "Enable cpp-httplib to support llvm-debuginfod serve debug information over HTTP.", default = false, type = "boolean"}) + add_configs("libcxx", {description = "Use libc++ as C++ standard library instead of libstdc++", default = false, type = "boolean"}) + + includes(path.join(os.scriptdir(), "constants.lua")) + for _, project in ipairs(get_llvm_known_projects()) do + add_configs(project:gsub("-", "_"), {description = "Build " .. project .. " project.", default = (project == "clang"), type = "boolean"}) + end + for _, runtime in ipairs(get_llvm_all_runtimes()) do + add_configs(runtime:gsub("-", "_"), {description = "Build " .. runtime .. " runtime.", default = false, type = "boolean"}) + end + + add_urls("https://github.com/llvm/llvm-project/releases/download/llvmorg-$(version)/llvm-project-$(version).src.tar.xz", {alias = "tarball"}) + add_urls("https://github.com/llvm/llvm-project.git", {alias = "git"}) + add_versions("tarball:19.1.7", "82401fea7b79d0078043f7598b835284d6650a75b93e64b6f761ea7b63097501") + add_versions("git:19.1.7", "llvmorg-19.1.7") + + add_deps("ninja") + add_deps("zlib", "zstd", {optional = true}) + set_policy("package.cmake_generator.ninja", true) + + + -- workaround to fix "error: undefined symbol: __mulodi4" (armeabi-v7a, r22, windows) + if is_plat("android") then + add_syslinks("compiler_rt-extras") + end + + -- error: undefined symbol: backtrace + if is_plat("bsd") then + add_syslinks("execinfo") + end + + add_deps("cmake") + on_load(function (package) + local constants = import('constants') + + -- add deps. + if not package:is_plat("windows") then -- not prebuilt + package:add("deps", "python 3.x", {kind = "binary", host = true}) + if package:config("libffi") then + package:add("deps", "libffi") + end + if package:config("httplib") then + package:add("deps", "cpp-httplib") + end + if package:config("libcxx") then + package:add("deps", "libc++") + end + end + + if package:is_plat("windows") and package:config("ms_dia") then + package:add("deps", "diasdk") + end + + -- add links + local linkable_projects = {"lldb", "lld", "clang", "polly", "bolt"} + table.insert(linkable_projects, "llvm") -- make sure that the base library is last. + for _, name in ipairs(linkable_projects) do + local cname = name:gsub("-", "_") + local ptype = package:config("shared") and "shared" or "static" + if cname == "llvm" or package:config(cname) then + package:add("links", constants[("get_%s_%s_libraries"):format(cname, ptype)]()) + end + end + if package:is_plat("windows") then + package:add("links", "LLVM-C") + package:add("syslinks", "version", "ntdll") + end + + end) + + -- on_install("windows|x64", function (package) + -- os.cp("*", package:installdir()) + -- end) + + on_install(function (package) + local constants = import('constants') + + local projects_enabled = {} + local runtimes_enabled = {} + for _, project in ipairs(constants.get_llvm_known_projects()) do + if package:config(project:gsub("-", "_")) then + table.insert(projects_enabled, project) + end + end + for _, runtime in ipairs(constants.get_llvm_all_runtimes()) do + if package:config(runtime:gsub("-", "_")) then + table.insert(runtimes_enabled, runtime) + end + end + + local configs = { + "-DBUILD_SHARED_LIBS=OFF", + + -- llvm + "-DLLVM_BUILD_UTILS=OFF", + "-DLLVM_INCLUDE_DOCS=OFF", + "-DLLVM_INCLUDE_EXAMPLES=OFF", + "-DLLVM_INCLUDE_TESTS=OFF", + "-DLLVM_INCLUDE_BENCHMARKS=OFF", + "-DLLVM_OPTIMIZED_TABLEGEN=ON", + "-DLLVM_ENABLE_PROJECTS=" .. table.concat(projects_enabled, ";"), + "-DLLVM_ENABLE_RUNTIMES=" .. table.concat(runtimes_enabled, ";"), + + -- disable tools build - to save link time + "-DLLVM_BUILD_TOOLS=OFF", + "-DCLANG_BUILD_TOOLS=OFF", + "-DCLANG_ENABLE_CLANGD=OFF", + "-DBOLT_BUILD_TOOLS=OFF", + "-DFLANG_BUILD_TOOLS=OFF", + "-DLLD_BUILD_TOOLS=OFF" + } + table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) + if package:is_plat("windows") then + table.insert(configs, "-DCMAKE_C_FLAGS=/FS") + table.insert(configs, "-DCMAKE_CXX_FLAGS=/FS") + end + table.insert(configs, "-DLLVM_BUILD_LLVM_DYLIB=" .. (package:config("shared") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_EH=" .. (package:config("exception") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_RTTI=" .. (package:config("rtti") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_DIA_SDK=" .. (package:config("ms_dia") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_LIBCXX=" .. (package:config("libcxx") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_LTO=" .. (package:config("lto") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_ZSTD=" .. (package:dep("zstd") and "ON" or "OFF")) + table.insert(configs, "-DLLVM_ENABLE_ZLIB=" .. (package:dep("zlib") and "ON" or "OFF")) + if package:config("libffi") then + table.insert(configs, "-DLLVM_ENABLE_FFI=ON") + table.insert(configs, "-DFFI_INCLUDE_DIR=" .. package:dep("libffi"):installdir("include")) + table.insert(configs, "-DFFI_LIBRARY_DIR=" .. package:dep("libffi"):installdir("lib")) + else + table.insert(configs, "-DLLVM_ENABLE_FFI=OFF") + end + if package:config("httplib") then + table.insert(configs, "-DLLVM_ENABLE_HTTPLIB=ON") + table.insert(configs, "-Dhttplib_ROOT=" .. package:dep("cpp-httplib"):installdir()) + else + table.insert(configs, "-DLLVM_ENABLE_HTTPLIB=OFF") + end + + for tooldir in string.gmatch(io.readfile("clang/tools/CMakeLists.txt"), "add_clang_subdirectory%((.-)%)") do + if tooldir ~= "libclang" and (tooldir ~= "clang-shlib" or not package:config("shared")) then + local tool = tooldir:upper():gsub("-", "_") + table.insert(configs, "-DCLANG_TOOL_" .. tool .. "_BUILD=OFF") + end + end + + if package:is_plat("android") then + local triple + if package:is_arch("arm64-v8a") then + triple = "aarch64-linux-android" + elseif package:arch():startswith("armeabi") then + triple = "armv7a-linux-androideabi" + elseif package:is_arch("x86") then + triple = "i686-linux-android" + elseif package:is_arch("x86_64") then + triple = "x86_64-linux-android" + else + raise("unsupported arch(%s) for android!", package:arch()) + end + table.insert(configs, "-DLLVM_HOST_TRIPLE=" .. triple) + end + if package:is_plat("iphoneos") then + local triple + if package:is_arch("arm64") then + triple = "aarch64-apple-ios" + else + raise("unsupported arch(%s) for iphoneos!", package:arch()) + end + table.insert(configs, "-DLLVM_HOST_TRIPLE=" .. triple) + + -- LLVM build systems mostly use "Darwin", not if(APPLE) + table.insert(configs, "-DCMAKE_SYSTEM_NAME=Darwin") + end + + function tryadd_dep(depname, varname) + varname = varname or depname + local dep = package:dep(depname) + if dep and not dep:is_system() then + local fetchinfo = dep:fetch({external = false}) + if fetchinfo then + local includedirs = fetchinfo.includedirs or fetchinfo.sysincludedirs + if includedirs and #includedirs > 0 then + local incs = {} + for _, p in ipairs(includedirs) do + table.insert(incs, (p:gsub("\\", "/"))) + end + table.insert(configs, "-D" .. varname .. "_INCLUDE_DIR=" .. "\"" .. table.concat(incs, ";") .. "\"") + end + local libfiles = fetchinfo.libfiles + if libfiles and #libfiles > 0 then + -- CMake attend typiquement un chemin de fichier unique ici + local libfile = libfiles[1]:gsub("\\", "/") + table.insert(configs, "-D" .. varname .. "_LIBRARY=" .. "\"" .. libfile .. "\"") + end + end + end + end + tryadd_dep("zlib", "ZLIB") + tryadd_dep("zstd", "ZSTD") + + os.cd("llvm") + import("package.tools.cmake").install(package, configs) + end) + + diff --git a/xmake.lua b/xmake.lua index daa8432..3e5f8ad 100644 --- a/xmake.lua +++ b/xmake.lua @@ -3,51 +3,23 @@ add_rules("plugin.vsxmake.autoupdate") add_repositories("concerto-xrepo https://github.com/ConcertoEngine/xmake-repo.git main") --- package("concerto-core") --- on_fetch(function (package) --- local concerto_core = "C:/Users/Arthur/Documents/Git/ConcertoEngine/ConcertoCore/install" --- return { --- linkdirs = { path.join(concerto_core, "lib") }, --- includedirs = { path.join(concerto_core, "include")}, --- links = { "concerto-core" } --- } --- end) - --- add_configs("asserts", {description = "Enable asserts.", default = false, type = "boolean"}) --- add_configs("enet", {description = "Enable ENet support.", default = false, type = "boolean"}) - --- on_load(function (package) --- if package:config("enet") then --- package:add("deps", "enet") --- end --- if package:config("asserts") then --- package:add("defines", "CCT_ENABLE_ASSERTS") --- end --- if package:is_plat("windows") then --- package:add("syslinks", "user32", "kernel32") --- end --- if package:has_tool("cxx", "cl", "clang_cl") then --- package:add("cxxflags", "/Zc:preprocessor", { public = true }) --- end --- if not package:config("shared") then --- package:add("defines", "CCT_CORE_LIB_STATIC") --- end --- end) --- package_end() - -add_requires("concerto-core", {configs = {asserts = true, shared = is_kind("static") and false or true }}) add_requires("toml11") add_requires("libllvm", {configs = {clang = true} }) add_requires("cxxopts") option("tests", { default = false, description = "Enable unit tests"}) add_defines("CCT_ENABLE_ASSERTS") + if has_config("tests") then add_requires("catch2") end if is_plat("windows") then + add_requires("concerto-core", {debug = true, configs = {asserts = true, shared = true, runtimes = is_mode("debug") and "MDd" or "MD" }}) set_runtimes(is_mode("debug") and "MDd" or "MD") + add_requires("concerto-core", {debug = true, configs = {asserts = true, shared = true, runtimes = "MT" }, alias = "concerto-core-mt"}) +else + add_requires("concerto-core", {debug = true, configs = {asserts = true, shared = true}}) end if is_mode("coverage") then @@ -77,19 +49,50 @@ function add_files_to_target(p) end end +target("concerto-plugin-api") + set_kind("shared") + set_languages("cxx20") + add_rpathdirs("$ORIGIN") + add_files("Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp") + add_headerfiles("Src/(Concerto/PackageGenerator/Plugin/PluginApi.h)") + add_headerfiles("Src/(Concerto/PackageGenerator/Defines.hpp)") + add_includedirs("Src/", { public = true }) + if is_plat("windows") then + add_packages("concerto-core-mt", { public = true }) + else + add_packages("concerto-core", { public = true }) + end + add_packages("toml11", { public = true }) + add_defines("CRP_PLUGIN_API_BUILD") + + if is_mode("debug") then + set_symbols("debug") + end + if is_plat("windows") then + set_runtimes("MT") + end + target("concerto-pkg-generator") set_kind("binary") set_languages("cxx20") add_rpathdirs("$ORIGIN") - local files = { ".", "ClangParser", "CppGenerator", "HeaderGenerator", "FileGenerator" } + local files = { ".", "ClangParser", "Plugin" } for _, dir in ipairs(files) do add_files_to_target("Src/Concerto/PackageGenerator/" .. dir, false) end - + remove_files("Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp") add_includedirs("Src/", { public = true }) - add_packages("concerto-core", "toml11", "libllvm", "cxxopts") + if is_plat("windows") then + add_packages("concerto-core-mt", { public = true }) + else + add_packages("concerto-core", { public = true }) + end + add_packages("toml11", { public = true }) + add_packages("libllvm", "cxxopts") + add_deps("concerto-plugin-api") set_policy("build.fence", true) - add_defines("CCT_PKG_GENERATOR_BUILD") + add_defines("CCT_PKGGENERATOR_BUILD") + if is_mode("debug") then set_symbols("debug") end @@ -103,12 +106,51 @@ target("concerto-pkg-generator") assert(llvm, "libllvm not found!") local llvm_lib = path.join(llvm:installdir(), "lib") - local concerto_core = project.required_package("concerto-core") + local concerto_core = project.required_package("concerto-core" .. (is_plat("windows") and "-mt" or "")) assert(concerto_core, "concerto-core not found!") local concerto_core_lib = path.join(concerto_core:installdir(), "lib") package:add("rpathdirs", llvm_lib, concerto_core_lib) end) +-- Header Plugin +target("concerto-header-plugin") + set_kind("shared") + set_languages("c11") + add_rpathdirs("$ORIGIN") + add_files("Src/Concerto/HeaderPlugin/*.c") + add_includedirs("Src/", { public = true }) + add_deps("concerto-plugin-api") + + if is_plat("windows") then + add_packages("concerto-core-mt", { public = true }) + set_runtimes("MT") + else + add_packages("concerto-core", { public = true }) + end + + if is_mode("debug") then + set_symbols("debug") + end + +-- Cpp Plugin +target("concerto-cpp-plugin") + set_kind("shared") + set_languages("c11") + add_rpathdirs("$ORIGIN") + add_files("Src/Concerto/CppPlugin/*.c") + add_includedirs("Src/", { public = true }) + add_deps("concerto-plugin-api") + + if is_plat("windows") then + add_packages("concerto-core-mt", { public = true }) + set_runtimes("MT") + else + add_packages("concerto-core", { public = true }) + end + + if is_mode("debug") then + set_symbols("debug") + end target("concerto-reflection") set_kind("$(kind)") @@ -142,6 +184,8 @@ target("concerto-reflection") add_files_to_target("Src/Concerto/Reflection/" .. dir, true) end add_deps("concerto-pkg-generator") + add_deps("concerto-header-plugin", {plugin = "pkg-generator"}) + add_deps("concerto-cpp-plugin", {plugin = "pkg-generator"}) add_packages("concerto-core", { public = true }) add_rules("cct_cpp_reflect") @@ -158,7 +202,7 @@ if has_config("tests") then set_languages("cxx20") add_rpathdirs("$ORIGIN") add_files("Src/Tests/*.cpp", "Src/Tests/*.refl.hpp") - add_packages("catch2") + add_packages("catch2", "toml11") add_deps("concerto-reflection") add_rules("cct_cpp_reflect") add_includedirs(".", { public = true }) -- temporary @@ -181,7 +225,7 @@ if has_config("tests") then assert(llvm, "libllvm not found!") local llvm_lib = path.join(llvm:installdir(), "lib") - local concerto_core = project.required_package("concerto-core") + local concerto_core = project.required_package("concerto-core" .. (is_plat("windows") and "-mt" or "")) assert(concerto_core, "concerto-core not found!") local concerto_core_lib = path.join(concerto_core:installdir(), "lib") package:add("rpathdirs", llvm_lib, concerto_core_lib) From ce7101644fba64e31c159c90a843c5d5a4bcef16 Mon Sep 17 00:00:00 2001 From: Arthur Vasseur Date: Thu, 19 Feb 2026 19:15:09 +0100 Subject: [PATCH 3/4] feat: add Tracy profiler integration --- .../ClangParser/ClangParser.cpp | 15 ++++- .../PackageGenerator/Plugin/PluginApi.cpp | 65 +++++++++++++++++++ .../Plugin/PluginRegistry.cpp | 2 + Src/Concerto/Profiler/Profiler.cpp | 12 ++++ Src/Concerto/Profiler/Profiler.hpp | 21 ++++++ xmake.lua | 35 +++++++++- 6 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 Src/Concerto/Profiler/Profiler.cpp create mode 100644 Src/Concerto/Profiler/Profiler.hpp diff --git a/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp b/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp index 2fc2199..9a5bbdc 100644 --- a/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp +++ b/Src/Concerto/PackageGenerator/ClangParser/ClangParser.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ namespace std::string QualTypeToString(const QualType& qt, const ASTContext& ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; PrintingPolicy policy(ctx.getLangOpts()); policy.SuppressTagKeyword = true; policy.Bool = true; @@ -52,6 +54,7 @@ namespace std::vector GetNamespaceChain(const DeclContext* DC) { + CCT_REFL_AUTO_PROFILER_SCOPE; std::vector chain; while (DC) { @@ -68,6 +71,7 @@ namespace Namespace* EnsureNamespace(Package& pkg, const std::vector& chain) { + CCT_REFL_AUTO_PROFILER_SCOPE; Namespace* current = nullptr; auto* level = &pkg.namespaces; for (const auto& name : chain) @@ -93,6 +97,7 @@ namespace bool ExtractCctAttribute(const Attr* A, ASTContext& astContext, const SourceManager& SM, const LangOptions& LO, std::string& outScope, TomlAttributes& outAttrs) { + CCT_REFL_AUTO_PROFILER_SCOPE; const IdentifierInfo* identifierInfo = A->getAttrName(); auto attributeName = identifierInfo ? identifierInfo->getName() : StringRef(); const auto* AnnotateAttribute = dyn_cast(A); @@ -151,6 +156,7 @@ namespace void InsertClassIntoPackage(Package& pkg, const std::vector& nsChain, const Class& klass) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (nsChain.empty()) { pkg.classes.push_back(klass); @@ -163,6 +169,7 @@ namespace void InsertEnumIntoPackage(Package& pkg, const std::vector& nsChain, const Enum& enumeration) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (nsChain.empty()) { pkg.enums.push_back(enumeration); @@ -215,7 +222,7 @@ namespace cct std::unique_ptr AST; { - std::unique_ptr AST = buildASTFromCodeWithArgs(code, args); + CCT_REFL_AUTO_PROFILER_SCOPE; AST = buildASTFromCodeWithArgs(code, args, "input.cc", "clang-tool", std::make_shared(), getClangStripDependencyFileAdjuster(), @@ -244,6 +251,7 @@ namespace cct void ClangParser::ProcessRecord(const CXXRecordDecl* recordDeclaration) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!recordDeclaration || !recordDeclaration->isThisDeclarationADefinition()) return; @@ -418,6 +426,7 @@ namespace cct void ClangParser::ProcessEnum(const EnumDecl* enumDeclaration) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!enumDeclaration || !enumDeclaration->isThisDeclarationADefinition()) return; bool hasAttr = false; @@ -463,12 +472,14 @@ namespace cct void ClangParser::ProcessNamespace(const NamespaceDecl* namespaceDeclaration) { + CCT_REFL_AUTO_PROFILER_SCOPE; for (const auto* D : namespaceDeclaration->decls()) ProcessDeclaration(D); } void ClangParser::ProcessDeclaration(const Decl* declaration) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (const auto* CTD = llvm::dyn_cast(declaration)) { ProcessTemplateRecord(CTD); @@ -507,6 +518,7 @@ namespace cct void ClangParser::ProcessTemplateRecord(const ClassTemplateDecl* templateDeclaration) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!templateDeclaration) return; @@ -644,6 +656,7 @@ namespace cct void ClangParser::ProcessTemplateSpecialization(const ClassTemplateSpecializationDecl* specializationDeclaration) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!specializationDeclaration || !specializationDeclaration->isThisDeclarationADefinition()) return; diff --git a/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp b/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp index 50ddfd9..083cdf9 100644 --- a/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp +++ b/Src/Concerto/PackageGenerator/Plugin/PluginApi.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "Concerto/Core/Assert.hpp" #include "Concerto/PackageGenerator/Defines.hpp" #include "Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp" @@ -16,6 +18,7 @@ extern "C" { const char* crpPackageGetName(const CrpPackage* package) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return nullptr; return reinterpret_cast(package)->name.c_str(); @@ -23,6 +26,7 @@ extern "C" const char* crpPackageGetVersion(const CrpPackage* package) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return nullptr; return reinterpret_cast(package)->version.c_str(); @@ -30,6 +34,7 @@ extern "C" const char* crpPackageGetDescription(const CrpPackage* package) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return nullptr; return reinterpret_cast(package)->description.c_str(); @@ -37,6 +42,7 @@ extern "C" size_t crpPackageGetClassCount(const CrpPackage* package) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return 0; return reinterpret_cast(package)->classes.size(); @@ -44,6 +50,7 @@ extern "C" const CrpClass* crpPackageGetClass(const CrpPackage* package, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return nullptr; const auto* pkg = reinterpret_cast(package); @@ -54,6 +61,7 @@ extern "C" size_t crpPackageGetNamespaceCount(const CrpPackage* package) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return 0; return reinterpret_cast(package)->namespaces.size(); @@ -61,6 +69,7 @@ extern "C" const CrpNamespace* crpPackageGetNamespace(const CrpPackage* package, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return nullptr; const auto* pkg = reinterpret_cast(package); @@ -71,6 +80,7 @@ extern "C" size_t crpPackageGetEnumCount(const CrpPackage* package) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return 0; return reinterpret_cast(package)->enums.size(); @@ -78,6 +88,7 @@ extern "C" const CrpEnum* crpPackageGetEnum(const CrpPackage* package, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!package) return nullptr; const auto* pkg = reinterpret_cast(package); @@ -88,6 +99,7 @@ extern "C" const char* crpClassGetName(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; if (reinterpret_cast(cls)->name.empty()) @@ -97,6 +109,7 @@ extern "C" const char* crpClassGetBase(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; if (reinterpret_cast(cls)->base.empty()) @@ -106,6 +119,7 @@ extern "C" const char* crpClassGetScope(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; if (reinterpret_cast(cls)->scope.empty()) @@ -115,6 +129,7 @@ extern "C" size_t crpClassGetMethodCount(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->methods.size(); @@ -122,6 +137,7 @@ extern "C" const CrpClassMethod* crpClassGetMethod(const CrpClass* cls, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; const auto* klass = reinterpret_cast(cls); @@ -132,6 +148,7 @@ extern "C" size_t crpClassGetMemberCount(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->members.size(); @@ -139,6 +156,7 @@ extern "C" const CrpClassMember* crpClassGetMember(const CrpClass* cls, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; const auto* klass = reinterpret_cast(cls); @@ -149,6 +167,7 @@ extern "C" int32_t crpClassIsTemplate(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->isTemplateClass ? 1 : 0; @@ -156,6 +175,7 @@ extern "C" int32_t crpClassIsGeneric(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->isGenericClass ? 1 : 0; @@ -163,6 +183,7 @@ extern "C" size_t crpClassGetTemplateParameterCount(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->templateParameters.size(); @@ -170,6 +191,7 @@ extern "C" const CrpTemplateParameter* crpClassGetTemplateParameter(const CrpClass* cls, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; const auto* klass = reinterpret_cast(cls); @@ -180,6 +202,7 @@ extern "C" size_t crpClassGetTemplateSpecializationCount(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->templateSpecializations.size(); @@ -187,6 +210,7 @@ extern "C" const char* crpClassGetTemplateSpecialization(const CrpClass* cls, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; const auto* klass = reinterpret_cast(cls); @@ -199,6 +223,7 @@ extern "C" size_t crpClassGetGenericTypeParameterFieldCount(const CrpClass* cls) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return 0; return reinterpret_cast(cls)->genericTypeParameterFields.size(); @@ -206,6 +231,7 @@ extern "C" const char* crpClassGetGenericTypeParameterField(const CrpClass* cls, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!cls) return nullptr; const auto* klass = reinterpret_cast(cls); @@ -218,6 +244,7 @@ extern "C" const char* crpClassMemberGetName(const CrpClassMember* member) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!member) return nullptr; if (reinterpret_cast(member)->name.empty()) @@ -227,6 +254,7 @@ extern "C" const char* crpClassMemberGetType(const CrpClassMember* member) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!member) return nullptr; if (reinterpret_cast(member)->type.empty()) @@ -236,6 +264,7 @@ extern "C" int32_t crpClassMemberIsNative(const CrpClassMember* member) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!member) return 0; return reinterpret_cast(member)->isNative ? 1 : 0; @@ -243,6 +272,7 @@ extern "C" const char* crpClassMethodGetName(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return nullptr; if (reinterpret_cast(method)->name.empty()) @@ -252,6 +282,7 @@ extern "C" const char* crpClassMethodGetBase(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return nullptr; if (reinterpret_cast(method)->base.empty()) @@ -261,6 +292,7 @@ extern "C" const char* crpClassMethodGetReturnType(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return nullptr; if (reinterpret_cast(method)->returnValue.empty()) @@ -270,6 +302,7 @@ extern "C" int32_t crpClassMethodHasCustomInvoker(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return 0; return reinterpret_cast(method)->customInvoker ? 1 : 0; @@ -277,6 +310,7 @@ extern "C" size_t crpClassMethodGetParamCount(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return 0; return reinterpret_cast(method)->params.size(); @@ -284,6 +318,7 @@ extern "C" const CrpClassMethodParam* crpClassMethodGetParam(const CrpClassMethod* method, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return nullptr; const auto* m = reinterpret_cast(method); @@ -294,6 +329,7 @@ extern "C" const char* crpClassMethodParamGetName(const CrpClassMethodParam* param) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!param) return nullptr; if (reinterpret_cast(param)->name.empty()) @@ -303,6 +339,7 @@ extern "C" const char* crpClassMethodParamGetType(const CrpClassMethodParam* param) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!param) return nullptr; if (reinterpret_cast(param)->type.empty()) @@ -312,6 +349,7 @@ extern "C" int32_t crpClassMethodHasDelegate(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return 0; const auto* m = reinterpret_cast(method); @@ -322,6 +360,7 @@ extern "C" int32_t crpClassMethodIsBooleanDelegate(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return 0; const auto* m = reinterpret_cast(method); @@ -335,6 +374,7 @@ extern "C" const char* crpClassMethodGetDelegateName(const CrpClassMethod* method) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!method) return nullptr; const auto* m = reinterpret_cast(method); @@ -350,6 +390,7 @@ extern "C" const char* crpEnumGetName(const CrpEnum* enm) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!enm) return nullptr; return reinterpret_cast(enm)->name.c_str(); @@ -357,6 +398,7 @@ extern "C" const char* crpEnumGetBase(const CrpEnum* enm) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!enm) return nullptr; return reinterpret_cast(enm)->base.c_str(); @@ -364,6 +406,7 @@ extern "C" size_t crpEnumGetElementCount(const CrpEnum* enm) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!enm) return 0; return reinterpret_cast(enm)->elements.size(); @@ -371,6 +414,7 @@ extern "C" const CrpEnumElement* crpEnumGetElement(const CrpEnum* enm, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!enm) return nullptr; const auto* enumType = reinterpret_cast(enm); @@ -381,6 +425,7 @@ extern "C" const char* crpEnumElementGetName(const CrpEnumElement* elem) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!elem) return nullptr; return reinterpret_cast(elem)->name.c_str(); @@ -388,6 +433,7 @@ extern "C" const char* crpEnumElementGetValue(const CrpEnumElement* elem) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!elem) return nullptr; return reinterpret_cast(elem)->value.c_str(); @@ -395,6 +441,7 @@ extern "C" const char* crpTemplateParameterGetName(const CrpTemplateParameter* param) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!param) return nullptr; return reinterpret_cast(param)->name.c_str(); @@ -402,6 +449,7 @@ extern "C" const char* crpNamespaceGetName(const CrpNamespace* ns) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return nullptr; return reinterpret_cast(ns)->name.c_str(); @@ -409,6 +457,7 @@ extern "C" size_t crpNamespaceGetClassCount(const CrpNamespace* ns) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return 0; return reinterpret_cast(ns)->classes.size(); @@ -416,6 +465,7 @@ extern "C" const CrpClass* crpNamespaceGetClass(const CrpNamespace* ns, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return nullptr; const auto* nspace = reinterpret_cast(ns); @@ -426,6 +476,7 @@ extern "C" size_t crpNamespaceGetEnumCount(const CrpNamespace* ns) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return 0; return reinterpret_cast(ns)->enums.size(); @@ -433,6 +484,7 @@ extern "C" const CrpEnum* crpNamespaceGetEnum(const CrpNamespace* ns, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return nullptr; const auto* nspace = reinterpret_cast(ns); @@ -443,6 +495,7 @@ extern "C" size_t crpNamespaceGetNamespaceCount(const CrpNamespace* ns) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return 0; return reinterpret_cast(ns)->namespaces.size(); @@ -450,6 +503,7 @@ extern "C" const CrpNamespace* crpNamespaceGetNamespace(const CrpNamespace* ns, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ns) return nullptr; const auto* nspace = reinterpret_cast(ns); @@ -460,6 +514,7 @@ extern "C" void crpGenerationContextWrite(CrpGenerationContext* ctx, const char* format, ...) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx || !format) { CCT_ASSERT_FALSE("crpGenerationContextWrite: ctx or format is null"); @@ -492,6 +547,7 @@ extern "C" void crpGenerationContextNewLine(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextNewLine: ctx is null"); @@ -504,6 +560,7 @@ extern "C" void crpGenerationContextEnterScope(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextEnterScope: ctx is null"); @@ -516,6 +573,7 @@ extern "C" void crpGenerationContextLeaveScope(CrpGenerationContext* ctx, const char* suffix) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextLeaveScope: ctx is null"); @@ -528,6 +586,7 @@ extern "C" const CrpPackage* crpGenerationContextGetPackage(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextGetPackage: ctx is null"); @@ -539,6 +598,7 @@ extern "C" const CrpClass* crpGenerationContextGetClass(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextGetClass: ctx is null"); @@ -550,6 +610,7 @@ extern "C" const CrpNamespace* crpGenerationContextGetNamespace(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextGetNamespace: ctx is null"); @@ -561,6 +622,7 @@ extern "C" const char* crpGenerationContextGetNamespacePath(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) { CCT_ASSERT_FALSE("crpGenerationContextGetNamespacePath: ctx is null"); @@ -572,6 +634,7 @@ extern "C" void* crpGenerationContextGetPrivateData(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) return nullptr; auto* context = reinterpret_cast(ctx); @@ -579,6 +642,7 @@ extern "C" } size_t crpGenerationContextGetHeaderCount(CrpGenerationContext* ctx) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) return 0; auto* context = reinterpret_cast(ctx); @@ -587,6 +651,7 @@ extern "C" const char* crpGenerationContextGetHeader(CrpGenerationContext* ctx, size_t index) { + CCT_REFL_AUTO_PROFILER_SCOPE; if (!ctx) return nullptr; auto* context = reinterpret_cast(ctx); diff --git a/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp b/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp index 630b31a..9288d7d 100644 --- a/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp +++ b/Src/Concerto/PackageGenerator/Plugin/PluginRegistry.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "Concerto/PackageGenerator/Plugin/ReflectionGeneratorPlugin.hpp" @@ -22,6 +23,7 @@ namespace cct bool PluginRegistry::LoadPlugin(std::string_view path) { + CCT_REFL_AUTO_PROFILER_SCOPE; LoadedPlugin plugin; if (!plugin.library.Load(path)) diff --git a/Src/Concerto/Profiler/Profiler.cpp b/Src/Concerto/Profiler/Profiler.cpp new file mode 100644 index 0000000..16f5b9d --- /dev/null +++ b/Src/Concerto/Profiler/Profiler.cpp @@ -0,0 +1,12 @@ +// +// Created by arthur on 20/06/2025. +// + +#include "tracy/Tracy.hpp" +#include +#include + +CCT_EXPORT cct::Int32 MakeMsvcHappy() +{ + return 0; +} diff --git a/Src/Concerto/Profiler/Profiler.hpp b/Src/Concerto/Profiler/Profiler.hpp new file mode 100644 index 0000000..14b14e5 --- /dev/null +++ b/Src/Concerto/Profiler/Profiler.hpp @@ -0,0 +1,21 @@ +// +// Created by arthur on 24/08/2023. +// +#ifndef CONCERTO_REFLECTION_PROFILER_PROFILER_HPP +#define CONCERTO_REFLECTION_PROFILER_PROFILER_HPP + + +#ifdef CCT_REFL_PROFILING +#include +#include +#define CCT_REFL_PROFILER_SCOPE(name) ZoneScopedN(name) +#define CCT_REFL_AUTO_PROFILER_SCOPE ZoneScoped +#define CCT_REFL_FRAME_MARK FrameMark +#else +#define CCT_REFL_PROFILER_SCOPE(name) +#define CCT_REFL_AUTO_PROFILER_SCOPE +#define CCT_REFL_FRAME_MARK +#endif + + +#endif //CONCERTO_REFLECTION_PROFILER_PROFILER_HPP \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 3e5f8ad..f55ef0e 100644 --- a/xmake.lua +++ b/xmake.lua @@ -8,12 +8,33 @@ add_requires("libllvm", {configs = {clang = true} }) add_requires("cxxopts") option("tests", { default = false, description = "Enable unit tests"}) -add_defines("CCT_ENABLE_ASSERTS") +option("profiling", { description = "Build with tracy profiler", default = false }) if has_config("tests") then add_requires("catch2") end +if has_config("profiling") then + add_requires("tracy", {configs = {shared = true, cmake = false, debug = true, vs_runtime = is_mode("debug") and "MDd" or "MD"}}) + + target("concerto-reflection-profiler") + set_kind("shared") + add_includedirs("Src/", { public = true }) + add_headerfiles("Src/(Concerto/Profiler/*.hpp)") + add_packages("tracy", {public = true}) + add_packages("concerto-core", {public = false}) + add_defines("CCT_REFL_PROFILING", {public = true}) + add_files("Src/Concerto/Profiler/**.cpp") + add_rpathdirs("$ORIGIN") + if is_plat("windows") then + add_packages("concerto-core-mt", { public = true }) + set_runtimes("MT") + else + add_packages("concerto-core", { public = true }) + end + target_end() +end + if is_plat("windows") then add_requires("concerto-core", {debug = true, configs = {asserts = true, shared = true, runtimes = is_mode("debug") and "MDd" or "MD" }}) set_runtimes(is_mode("debug") and "MDd" or "MD") @@ -71,6 +92,9 @@ target("concerto-plugin-api") if is_plat("windows") then set_runtimes("MT") end + if has_config("profiling") then + add_deps("concerto-reflection-profiler", { public = true }) + end target("concerto-pkg-generator") set_kind("binary") @@ -99,6 +123,9 @@ target("concerto-pkg-generator") if is_plat("windows") then set_runtimes("MT") end + -- if has_config("profiling") then + -- add_deps("concerto-reflection-profiler") + -- end on_config(function(package) import("core.project.project") @@ -131,6 +158,9 @@ target("concerto-header-plugin") if is_mode("debug") then set_symbols("debug") end + if has_config("profiling") then + add_deps("concerto-reflection-profiler") + end -- Cpp Plugin target("concerto-cpp-plugin") @@ -151,6 +181,9 @@ target("concerto-cpp-plugin") if is_mode("debug") then set_symbols("debug") end + if has_config("profiling") then + add_deps("concerto-reflection-profiler") + end target("concerto-reflection") set_kind("$(kind)") From 12b0655ee80ad942a4c432b10ac47791465bcce2 Mon Sep 17 00:00:00 2001 From: Arthur Vasseur Date: Fri, 20 Feb 2026 15:56:43 +0100 Subject: [PATCH 4/4] feat: update macOS runner version to 15 and install clang 20 --- .github/workflows/build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4ceba1..bf2fbdc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,8 +32,8 @@ jobs: - { name: Linux, runner: ubuntu-latest, shell: bash, plat: linux, arch: x86_64, mode: releasedbg, package: true, test: true, } - { name: macOS, runner: macos-latest, shell: bash, plat: macosx, arch: x86_64, mode: debug, package: true, test: true, } - { name: macOS, runner: macos-latest, shell: bash, plat: macosx, arch: x86_64, mode: releasedbg, package: true, test: true, } - - { name: macOS, runner: macos-14, shell: bash, plat: macosx, arch: arm64, mode: debug, package: true, test: true, } - - { name: macOS, runner: macos-14, shell: bash, plat: macosx, arch: arm64, mode: releasedbg, package: true, test: true, } + - { name: macOS, runner: macos-15, shell: bash, plat: macosx, arch: arm64, mode: debug, package: true, test: true, } + - { name: macOS, runner: macos-15, shell: bash, plat: macosx, arch: arm64, mode: releasedbg, package: true, test: true, } # - { name: iOS, runner: macos-latest, shell: bash, plat: iphoneos, arch: arm64, mode: debug, package: true, test: false, } # - { name: iOS, runner: macos-latest, shell: bash, plat: iphoneos, arch: arm64, mode: releasedbg, package: true, test: false, } kind: [shared, static] @@ -57,6 +57,10 @@ jobs: sudo apt-get update sudo apt-get -y install mesa-common-dev + - name: Update clang to 20 + if: ${{ matrix.confs.plat == 'macosx' }} + run: brew install llvm@20 + # Setup Emsdk - name: Setup Emscripten if: ${{ matrix.confs.plat == 'wasm' }}