From 4b2bb6998af026747b90b4e29c775a2bf7aa82c9 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Mon, 6 Apr 2026 23:46:36 +0200 Subject: [PATCH] feat: add C++ support with ImGui/ImPlot framework and vendor submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Añade soporte C++ al registry: vendor submodules (glfw, imgui, implot, tracy), sistema de build con CMake y toolchains cross-platform, runner C++ en fn CLI, parser de tests Google Test, y funciones bash para build Linux/Windows. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitmodules | 13 +++ bash/functions/infra/build_cpp_linux.md | 36 ++++++ bash/functions/infra/build_cpp_linux.sh | 24 ++++ bash/functions/infra/build_cpp_windows.md | 38 +++++++ bash/functions/infra/build_cpp_windows.sh | 34 ++++++ bash/functions/infra/install_cpp_deps.md | 37 +++++++ bash/functions/infra/install_cpp_deps.sh | 49 +++++++++ cmd/fn/cpprunner.go | 128 ++++++++++++++++++++++ cmd/fn/run.go | 2 + cpp/CMakeLists.txt | 100 +++++++++++++++++ cpp/apps/chart_demo/CMakeLists.txt | 8 ++ cpp/apps/chart_demo/main.cpp | 79 +++++++++++++ cpp/framework/app_base.cpp | 105 ++++++++++++++++++ cpp/framework/app_base.h | 25 +++++ cpp/functions/core/fps_overlay.cpp | 33 ++++++ cpp/functions/core/fps_overlay.h | 5 + cpp/functions/core/fps_overlay.md | 30 +++++ cpp/functions/viz/bar_chart.cpp | 26 +++++ cpp/functions/viz/bar_chart.h | 6 + cpp/functions/viz/bar_chart.md | 40 +++++++ cpp/functions/viz/heatmap.cpp | 24 ++++ cpp/functions/viz/heatmap.h | 9 ++ cpp/functions/viz/heatmap.md | 42 +++++++ cpp/functions/viz/line_plot.cpp | 16 +++ cpp/functions/viz/line_plot.h | 8 ++ cpp/functions/viz/line_plot.md | 40 +++++++ cpp/functions/viz/scatter_plot.cpp | 16 +++ cpp/functions/viz/scatter_plot.h | 6 + cpp/functions/viz/scatter_plot.md | 38 +++++++ cpp/toolchains/linux-x86_64.cmake | 8 ++ cpp/toolchains/mingw-w64.cmake | 17 +++ cpp/vendor/glfw | 1 + cpp/vendor/imgui | 1 + cpp/vendor/implot | 1 + cpp/vendor/tracy | 1 + registry/test_parser.go | 19 ++++ 36 files changed, 1065 insertions(+) create mode 100644 .gitmodules create mode 100644 bash/functions/infra/build_cpp_linux.md create mode 100644 bash/functions/infra/build_cpp_linux.sh create mode 100644 bash/functions/infra/build_cpp_windows.md create mode 100644 bash/functions/infra/build_cpp_windows.sh create mode 100644 bash/functions/infra/install_cpp_deps.md create mode 100644 bash/functions/infra/install_cpp_deps.sh create mode 100644 cmd/fn/cpprunner.go create mode 100644 cpp/CMakeLists.txt create mode 100644 cpp/apps/chart_demo/CMakeLists.txt create mode 100644 cpp/apps/chart_demo/main.cpp create mode 100644 cpp/framework/app_base.cpp create mode 100644 cpp/framework/app_base.h create mode 100644 cpp/functions/core/fps_overlay.cpp create mode 100644 cpp/functions/core/fps_overlay.h create mode 100644 cpp/functions/core/fps_overlay.md create mode 100644 cpp/functions/viz/bar_chart.cpp create mode 100644 cpp/functions/viz/bar_chart.h create mode 100644 cpp/functions/viz/bar_chart.md create mode 100644 cpp/functions/viz/heatmap.cpp create mode 100644 cpp/functions/viz/heatmap.h create mode 100644 cpp/functions/viz/heatmap.md create mode 100644 cpp/functions/viz/line_plot.cpp create mode 100644 cpp/functions/viz/line_plot.h create mode 100644 cpp/functions/viz/line_plot.md create mode 100644 cpp/functions/viz/scatter_plot.cpp create mode 100644 cpp/functions/viz/scatter_plot.h create mode 100644 cpp/functions/viz/scatter_plot.md create mode 100644 cpp/toolchains/linux-x86_64.cmake create mode 100644 cpp/toolchains/mingw-w64.cmake create mode 160000 cpp/vendor/glfw create mode 160000 cpp/vendor/imgui create mode 160000 cpp/vendor/implot create mode 160000 cpp/vendor/tracy diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..6adba50d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,13 @@ +[submodule "cpp/vendor/imgui"] + path = cpp/vendor/imgui + url = https://github.com/ocornut/imgui.git + branch = docking +[submodule "cpp/vendor/implot"] + path = cpp/vendor/implot + url = https://github.com/epezent/implot.git +[submodule "cpp/vendor/tracy"] + path = cpp/vendor/tracy + url = https://github.com/wolfpld/tracy.git +[submodule "/home/lucas/fn_registry/cpp/vendor/glfw"] + path = /home/lucas/fn_registry/cpp/vendor/glfw + url = https://github.com/glfw/glfw.git diff --git a/bash/functions/infra/build_cpp_linux.md b/bash/functions/infra/build_cpp_linux.md new file mode 100644 index 00000000..4685129e --- /dev/null +++ b/bash/functions/infra/build_cpp_linux.md @@ -0,0 +1,36 @@ +--- +name: build_cpp_linux +kind: function +lang: bash +domain: infra +version: "1.0.0" +purity: impure +signature: "build_cpp_linux(target?: string) -> void" +description: "Compila las funciones y apps C++ del registry para Linux nativo usando cmake" +tags: [cpp, build, cmake, linux, imgui] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/infra/build_cpp_linux.sh" +params: + - name: target + desc: "Nombre del target cmake a compilar (opcional, sin argumento compila todo)" +output: "Compila los binarios en cpp/build/linux/" +--- + +# build_cpp_linux + +Configura y compila el proyecto C++ (ImGui/ImPlot) para Linux nativo. + +Usa cmake con compilacion paralela (`-j$(nproc)`). Si no se ha configurado antes, ejecuta `cmake -B` automaticamente. + +```bash +fn run build_cpp_linux # Compilar todo +fn run build_cpp_linux chart_demo # Compilar solo chart_demo +``` diff --git a/bash/functions/infra/build_cpp_linux.sh b/bash/functions/infra/build_cpp_linux.sh new file mode 100644 index 00000000..7a3028ab --- /dev/null +++ b/bash/functions/infra/build_cpp_linux.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}" +CPP_ROOT="$REGISTRY_ROOT/cpp" +BUILD_DIR="$CPP_ROOT/build/linux" +TARGET="${1:-}" + +# Configure if needed +if [ ! -f "$BUILD_DIR/CMakeCache.txt" ]; then + echo "[build_cpp_linux] Configuring cmake..." + cmake -B "$BUILD_DIR" -S "$CPP_ROOT" +fi + +# Build +if [ -n "$TARGET" ]; then + echo "[build_cpp_linux] Building target: $TARGET" + cmake --build "$BUILD_DIR" --target "$TARGET" -- -j"$(nproc)" +else + echo "[build_cpp_linux] Building all targets..." + cmake --build "$BUILD_DIR" -- -j"$(nproc)" +fi + +echo "[build_cpp_linux] Done. Binaries in $BUILD_DIR" diff --git a/bash/functions/infra/build_cpp_windows.md b/bash/functions/infra/build_cpp_windows.md new file mode 100644 index 00000000..0d300da8 --- /dev/null +++ b/bash/functions/infra/build_cpp_windows.md @@ -0,0 +1,38 @@ +--- +name: build_cpp_windows +kind: function +lang: bash +domain: infra +version: "1.0.0" +purity: impure +signature: "build_cpp_windows(target?: string) -> void" +description: "Cross-compila las funciones y apps C++ del registry para Windows usando mingw-w64" +tags: [cpp, build, cmake, windows, cross-compile, mingw, imgui] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/infra/build_cpp_windows.sh" +params: + - name: target + desc: "Nombre del target cmake a compilar (opcional, sin argumento compila todo)" +output: "Produce binarios .exe de Windows en cpp/build/windows/" +--- + +# build_cpp_windows + +Cross-compila el proyecto C++ para Windows desde Linux usando el toolchain mingw-w64. + +Los .exe resultantes incluyen runtime linkado estaticamente (self-contained). + +```bash +fn run build_cpp_windows # Compilar todo +fn run build_cpp_windows chart_demo # Compilar solo chart_demo +``` + +Requiere `mingw-w64`: `sudo apt install mingw-w64` diff --git a/bash/functions/infra/build_cpp_windows.sh b/bash/functions/infra/build_cpp_windows.sh new file mode 100644 index 00000000..9d52db97 --- /dev/null +++ b/bash/functions/infra/build_cpp_windows.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}" +CPP_ROOT="$REGISTRY_ROOT/cpp" +BUILD_DIR="$CPP_ROOT/build/windows" +TOOLCHAIN="$CPP_ROOT/toolchains/mingw-w64.cmake" +TARGET="${1:-}" + +# Check mingw is available +if ! command -v x86_64-w64-mingw32-g++ &>/dev/null; then + echo "[build_cpp_windows] Error: mingw-w64 not found. Install with: sudo apt install mingw-w64" + exit 1 +fi + +# Configure if needed +if [ ! -f "$BUILD_DIR/CMakeCache.txt" ]; then + echo "[build_cpp_windows] Configuring cmake with mingw-w64 toolchain..." + cmake -B "$BUILD_DIR" -S "$CPP_ROOT" -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" +fi + +# Build +if [ -n "$TARGET" ]; then + echo "[build_cpp_windows] Cross-compiling target: $TARGET" + cmake --build "$BUILD_DIR" --target "$TARGET" -- -j"$(nproc)" +else + echo "[build_cpp_windows] Cross-compiling all targets..." + cmake --build "$BUILD_DIR" -- -j"$(nproc)" +fi + +echo "[build_cpp_windows] Done. Windows binaries in $BUILD_DIR" +if [ -n "$TARGET" ]; then + file "$BUILD_DIR"/**/"$TARGET".exe 2>/dev/null || file "$BUILD_DIR/$TARGET".exe 2>/dev/null || true +fi diff --git a/bash/functions/infra/install_cpp_deps.md b/bash/functions/infra/install_cpp_deps.md new file mode 100644 index 00000000..2f48e230 --- /dev/null +++ b/bash/functions/infra/install_cpp_deps.md @@ -0,0 +1,37 @@ +--- +name: install_cpp_deps +kind: function +lang: bash +domain: infra +version: "1.0.0" +purity: impure +signature: "install_cpp_deps() -> void" +description: "Verifica e instala las dependencias de sistema necesarias para compilar C++ con ImGui (cmake, g++, glfw, mesa)" +tags: [cpp, dependencies, setup, cmake, imgui] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/infra/install_cpp_deps.sh" +params: [] +output: "Instala paquetes faltantes via apt o confirma que todo esta instalado" +--- + +# install_cpp_deps + +Verifica las dependencias necesarias para el build C++: +- `cmake` — sistema de build +- `g++` / `build-essential` — compilador +- `libglfw3-dev` — windowing (GLFW) +- `libgl1-mesa-dev` — OpenGL headers + +Tambien reporta si `mingw-w64` esta disponible para cross-compile a Windows. + +```bash +fn run install_cpp_deps +``` diff --git a/bash/functions/infra/install_cpp_deps.sh b/bash/functions/infra/install_cpp_deps.sh new file mode 100644 index 00000000..1ce25129 --- /dev/null +++ b/bash/functions/infra/install_cpp_deps.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "[install_cpp_deps] Checking C++ build dependencies..." + +MISSING=() + +if ! command -v cmake &>/dev/null; then + MISSING+=(cmake) +else + echo " cmake: $(cmake --version | head -1)" +fi + +if ! command -v g++ &>/dev/null; then + MISSING+=(g++ build-essential) +else + echo " g++: $(g++ --version | head -1)" +fi + +if ! dpkg -s libglfw3-dev &>/dev/null 2>&1; then + MISSING+=(libglfw3-dev) +else + echo " libglfw3-dev: installed" +fi + +if ! dpkg -s libgl1-mesa-dev &>/dev/null 2>&1; then + MISSING+=(libgl1-mesa-dev) +else + echo " libgl1-mesa-dev: installed" +fi + +# Optional: mingw for cross-compile +if command -v x86_64-w64-mingw32-g++ &>/dev/null; then + echo " mingw-w64: $(x86_64-w64-mingw32-g++ --version | head -1)" +else + echo " mingw-w64: not installed (optional, for Windows cross-compile)" +fi + +if [ ${#MISSING[@]} -eq 0 ]; then + echo "[install_cpp_deps] All dependencies satisfied." + exit 0 +fi + +echo "" +echo "[install_cpp_deps] Missing packages: ${MISSING[*]}" +echo "[install_cpp_deps] Installing..." +sudo apt-get update -qq +sudo apt-get install -y -qq "${MISSING[@]}" +echo "[install_cpp_deps] Done." diff --git a/cmd/fn/cpprunner.go b/cmd/fn/cpprunner.go new file mode 100644 index 00000000..74f61dd0 --- /dev/null +++ b/cmd/fn/cpprunner.go @@ -0,0 +1,128 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "fn-registry/registry" +) + +func buildCppCommand(fn *registry.Function, registryRoot, absPath string, args []string) (*exec.Cmd, error) { + cppRoot := filepath.Join(registryRoot, "cpp") + buildDir := filepath.Join(cppRoot, "build", "linux") + + // Ensure build directory exists and cmake is configured + if _, err := os.Stat(filepath.Join(buildDir, "CMakeCache.txt")); os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "[fn run] configuring cmake for cpp...\n") + configure := exec.Command("cmake", "-B", buildDir, "-S", cppRoot) + configure.Dir = registryRoot + configure.Stdout = os.Stderr + configure.Stderr = os.Stderr + if err := configure.Run(); err != nil { + return nil, fmt.Errorf("cmake configure failed: %w", err) + } + } + + dir := filepath.Dir(absPath) + + // Check if the function's directory has its own CMakeLists.txt (app with main) + localCMake := filepath.Join(dir, "CMakeLists.txt") + hasMain := false + if _, err := os.Stat(localCMake); err == nil { + hasMain = true + } + // Also check for main.cpp in the same directory + mainCpp := filepath.Join(dir, "main.cpp") + if _, err := os.Stat(mainCpp); err == nil { + hasMain = true + } + + if hasMain { + // Build and run the app binary + targetName := filepath.Base(dir) + build := exec.Command("cmake", "--build", buildDir, "--target", targetName) + build.Dir = registryRoot + build.Stdout = os.Stderr + build.Stderr = os.Stderr + fmt.Fprintf(os.Stderr, "[fn run] building target %s...\n", targetName) + if err := build.Run(); err != nil { + return nil, fmt.Errorf("cmake build failed: %w", err) + } + + // Find the built binary + binaryPath := findBinary(buildDir, targetName) + if binaryPath == "" { + return nil, fmt.Errorf("built binary %q not found in %s", targetName, buildDir) + } + + cmd := exec.Command(binaryPath, args...) + cmd.Dir = dir + return cmd, nil + } + + // Library code: compile-check only (like go vet) + build := exec.Command("cmake", "--build", buildDir) + build.Dir = registryRoot + build.Stdout = os.Stderr + build.Stderr = os.Stderr + fmt.Fprintf(os.Stderr, "[fn run] %s is library code — running compile check\n", fn.ID) + + if err := build.Run(); err != nil { + return nil, fmt.Errorf("compile check failed: %w", err) + } + + // Return a no-op command that just prints success + cmd := exec.Command("echo", fmt.Sprintf("[fn run] %s compiled successfully", fn.ID)) + return cmd, nil +} + +// findBinary searches for an executable in the build tree. +func findBinary(buildDir, name string) string { + // Common locations cmake puts binaries + candidates := []string{ + filepath.Join(buildDir, name), + filepath.Join(buildDir, "apps", name, name), + } + + for _, c := range candidates { + if info, err := os.Stat(c); err == nil && !info.IsDir() { + // Check if executable + if info.Mode()&0111 != 0 { + return c + } + } + } + + // Walk the build directory as fallback + var found string + filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() { + return nil + } + if info.Name() == name && info.Mode()&0111 != 0 { + found = path + return filepath.SkipAll + } + return nil + }) + + // Also try without extension match for paths with subdirectories + if found == "" { + filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() { + return nil + } + base := strings.TrimSuffix(info.Name(), filepath.Ext(info.Name())) + if base == name && info.Mode()&0111 != 0 { + found = path + return filepath.SkipAll + } + return nil + }) + } + + return found +} diff --git a/cmd/fn/run.go b/cmd/fn/run.go index 89206e42..9932a0ae 100644 --- a/cmd/fn/run.go +++ b/cmd/fn/run.go @@ -103,6 +103,8 @@ func buildCommand(fn *registry.Function, db *registry.DB, registryRoot, absPath return buildBashCommand(absPath, args) case "ts": return buildTsCommand(registryRoot, absPath, args) + case "cpp": + return buildCppCommand(fn, registryRoot, absPath, args) default: return nil, fmt.Errorf("unsupported lang %q for execution", fn.Lang) } diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 00000000..8079b01c --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.16) +project(fn_registry_cpp LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# --- Options --- +option(TRACY_ENABLE "Enable Tracy profiling" OFF) + +# --- Vendor: Dear ImGui --- +set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/imgui) +add_library(imgui STATIC + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_tables.cpp + ${IMGUI_DIR}/imgui_widgets.cpp + ${IMGUI_DIR}/imgui_demo.cpp + ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp + ${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp +) +target_include_directories(imgui PUBLIC + ${IMGUI_DIR} + ${IMGUI_DIR}/backends +) + +# --- Vendor: ImPlot --- +set(IMPLOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/implot) +add_library(implot STATIC + ${IMPLOT_DIR}/implot.cpp + ${IMPLOT_DIR}/implot_items.cpp +) +target_include_directories(implot PUBLIC ${IMPLOT_DIR}) +target_link_libraries(implot PUBLIC imgui) + +# --- Vendor: Tracy (optional) --- +if(TRACY_ENABLE) + set(TRACY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/tracy) + add_library(tracy STATIC + ${TRACY_DIR}/public/TracyClient.cpp + ) + target_include_directories(tracy PUBLIC ${TRACY_DIR}/public) + target_compile_definitions(tracy PUBLIC TRACY_ENABLE) +endif() + +# --- Platform dependencies --- +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + # Cross-compile: use vendored or system GLFW, link opengl32/gdi32 + find_package(glfw3 QUIET) + if(NOT glfw3_FOUND) + # Build GLFW from source if available + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/vendor/glfw/CMakeLists.txt) + set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + add_subdirectory(vendor/glfw) + else() + message(FATAL_ERROR "GLFW not found. For Windows cross-compile, add GLFW source to cpp/vendor/glfw/") + endif() + endif() + set(PLATFORM_LIBS glfw opengl32 gdi32 imm32) +else() + # Linux native + find_package(glfw3 REQUIRED) + find_package(OpenGL REQUIRED) + set(PLATFORM_LIBS glfw OpenGL::GL ${CMAKE_DL_LIBS}) +endif() + +target_link_libraries(imgui PUBLIC ${PLATFORM_LIBS}) + +# --- Framework --- +add_library(fn_framework STATIC + framework/app_base.cpp +) +target_include_directories(fn_framework PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/framework + ${CMAKE_CURRENT_SOURCE_DIR}/functions +) +target_link_libraries(fn_framework PUBLIC imgui implot) +if(TRACY_ENABLE) + target_link_libraries(fn_framework PUBLIC tracy) +endif() + +# --- Macro for creating ImGui apps --- +function(add_imgui_app target) + add_executable(${target} ${ARGN}) + target_link_libraries(${target} PRIVATE fn_framework) + target_include_directories(${target} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/functions + ) +endfunction() + +# --- Function libraries (headers for composition) --- +# Functions are compiled as part of apps that use them via add_imgui_app. +# Each function is a .h/.cpp pair included by the app's CMakeLists.txt. + +# --- Demo app --- +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/apps/chart_demo/CMakeLists.txt) + add_subdirectory(apps/chart_demo) +endif() diff --git a/cpp/apps/chart_demo/CMakeLists.txt b/cpp/apps/chart_demo/CMakeLists.txt new file mode 100644 index 00000000..ed04084f --- /dev/null +++ b/cpp/apps/chart_demo/CMakeLists.txt @@ -0,0 +1,8 @@ +add_imgui_app(chart_demo + main.cpp + ${CMAKE_SOURCE_DIR}/functions/viz/line_plot.cpp + ${CMAKE_SOURCE_DIR}/functions/viz/scatter_plot.cpp + ${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp + ${CMAKE_SOURCE_DIR}/functions/viz/heatmap.cpp + ${CMAKE_SOURCE_DIR}/functions/core/fps_overlay.cpp +) diff --git a/cpp/apps/chart_demo/main.cpp b/cpp/apps/chart_demo/main.cpp new file mode 100644 index 00000000..1ec73628 --- /dev/null +++ b/cpp/apps/chart_demo/main.cpp @@ -0,0 +1,79 @@ +#include "app_base.h" +#include "imgui.h" +#include "implot.h" + +#include "viz/line_plot.h" +#include "viz/scatter_plot.h" +#include "viz/bar_chart.h" +#include "viz/heatmap.h" +#include "core/fps_overlay.h" + +#include +#include + +// Generate sample data +static constexpr int N = 500; +static float xs[N], ys_sin[N], ys_cos[N]; +static float scatter_x[200], scatter_y[200]; +static const char* bar_labels[] = {"Go", "Python", "Bash", "TypeScript", "C++"}; +static float bar_values[] = {201.0f, 202.0f, 38.0f, 80.0f, 5.0f}; +static float heat_data[10 * 10]; + +static bool data_initialized = false; + +static void init_data() { + if (data_initialized) return; + for (int i = 0; i < N; i++) { + xs[i] = static_cast(i) * 0.02f; + ys_sin[i] = sinf(xs[i]); + ys_cos[i] = cosf(xs[i]); + } + for (int i = 0; i < 200; i++) { + scatter_x[i] = static_cast(rand()) / RAND_MAX * 10.0f; + scatter_y[i] = scatter_x[i] * 0.5f + (static_cast(rand()) / RAND_MAX - 0.5f) * 3.0f; + } + for (int i = 0; i < 100; i++) { + int r = i / 10, c = i % 10; + heat_data[i] = sinf(r * 0.5f) * cosf(c * 0.5f); + } + data_initialized = true; +} + +static void render() { + init_data(); + fps_overlay(); + + // Full-window dockspace + ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport()); + + if (ImGui::Begin("fn_registry — Chart Demo")) { + if (ImGui::BeginTabBar("##charts")) { + if (ImGui::BeginTabItem("Line Plot")) { + ImGui::Text("sin(x) — %d points", N); + line_plot("Sine Wave", xs, ys_sin, N); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Scatter Plot")) { + ImGui::Text("y = 0.5x + noise — 200 points"); + scatter_plot("Scatter Data", scatter_x, scatter_y, 200); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Bar Chart")) { + ImGui::Text("Functions per language in fn_registry"); + bar_chart("Registry Languages", bar_labels, bar_values, 5); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Heatmap")) { + ImGui::Text("sin(r) * cos(c) — 10x10 matrix"); + heatmap("Correlation Matrix", heat_data, 10, 10, -1.0f, 1.0f); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + } + ImGui::End(); +} + +int main() { + return fn::run_app({.title = "fn_registry — Chart Demo", .width = 1400, .height = 900}, render); +} diff --git a/cpp/framework/app_base.cpp b/cpp/framework/app_base.cpp new file mode 100644 index 00000000..a4c0c328 --- /dev/null +++ b/cpp/framework/app_base.cpp @@ -0,0 +1,105 @@ +#include "app_base.h" + +#include "imgui.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_opengl3.h" +#include "implot.h" + +#include +#include + +#ifdef TRACY_ENABLE +#include "tracy/Tracy.hpp" +#endif + +static void glfw_error_callback(int error, const char* description) { + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} + +namespace fn { + +int run_app(AppConfig config, std::function render_fn) { + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) { + fprintf(stderr, "Failed to initialize GLFW\n"); + return 1; + } + + // OpenGL 3.3 core + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + GLFWwindow* window = glfwCreateWindow(config.width, config.height, config.title, nullptr, nullptr); + if (!window) { + fprintf(stderr, "Failed to create GLFW window\n"); + glfwTerminate(); + return 1; + } + + glfwMakeContextCurrent(window); + glfwSwapInterval(config.vsync ? 1 : 0); + + // Setup ImGui + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImPlot::CreateContext(); + + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + ImGui::StyleColorsDark(); + + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init("#version 330"); + + // Main loop + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { + glfwWaitEvents(); + continue; + } + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + render_fn(); + + ImGui::Render(); + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(config.bg_r, config.bg_g, config.bg_b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(window); + +#ifdef TRACY_ENABLE + FrameMark; +#endif + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImPlot::DestroyContext(); + ImGui::DestroyContext(); + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} + +int run_app(std::function render_fn) { + return run_app(AppConfig{}, render_fn); +} + +} // namespace fn diff --git a/cpp/framework/app_base.h b/cpp/framework/app_base.h new file mode 100644 index 00000000..8e3b92d8 --- /dev/null +++ b/cpp/framework/app_base.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace fn { + +struct AppConfig { + const char* title = "fn_registry"; + int width = 1280; + int height = 720; + bool vsync = true; + float bg_r = 0.1f; + float bg_g = 0.1f; + float bg_b = 0.1f; +}; + +// Run an ImGui application. The render_fn is called every frame +// between ImGui::NewFrame() and ImGui::Render(). +// Returns 0 on clean exit, 1 on error. +int run_app(AppConfig config, std::function render_fn); + +// Convenience: run with default config +int run_app(std::function render_fn); + +} // namespace fn diff --git a/cpp/functions/core/fps_overlay.cpp b/cpp/functions/core/fps_overlay.cpp new file mode 100644 index 00000000..268698f5 --- /dev/null +++ b/cpp/functions/core/fps_overlay.cpp @@ -0,0 +1,33 @@ +#include "core/fps_overlay.h" +#include "imgui.h" + +#ifdef TRACY_ENABLE +#include "tracy/Tracy.hpp" +#endif + +void fps_overlay() { +#ifdef TRACY_ENABLE + ZoneScoped; +#endif + + ImGuiIO& io = ImGui::GetIO(); + ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration + | ImGuiWindowFlags_AlwaysAutoResize + | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoMove; + + const float pad = 10.0f; + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImVec2 pos(viewport->WorkPos.x + viewport->WorkSize.x - pad, + viewport->WorkPos.y + pad); + ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowBgAlpha(0.65f); + + if (ImGui::Begin("##fps_overlay", nullptr, flags)) { + ImGui::Text("%.1f FPS", io.Framerate); + ImGui::Text("%.3f ms", 1000.0f / io.Framerate); + } + ImGui::End(); +} diff --git a/cpp/functions/core/fps_overlay.h b/cpp/functions/core/fps_overlay.h new file mode 100644 index 00000000..abe124e4 --- /dev/null +++ b/cpp/functions/core/fps_overlay.h @@ -0,0 +1,5 @@ +#pragma once + +// Renders an FPS counter overlay in the top-right corner. +// Call within an ImGui frame. +void fps_overlay(); diff --git a/cpp/functions/core/fps_overlay.md b/cpp/functions/core/fps_overlay.md new file mode 100644 index 00000000..7d16e8a5 --- /dev/null +++ b/cpp/functions/core/fps_overlay.md @@ -0,0 +1,30 @@ +--- +name: fps_overlay +kind: component +lang: cpp +domain: core +version: "1.0.0" +purity: pure +signature: "void fps_overlay()" +description: "Renderiza un overlay de FPS y frametime en la esquina superior derecha, con soporte opcional de Tracy" +tags: [imgui, fps, overlay, profiling, debug] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [imgui] +tested: false +tests: [] +test_file_path: "" +file_path: "cpp/functions/core/fps_overlay.cpp" +framework: imgui +params: [] +output: "Renderiza el overlay de FPS en el frame ImGui actual" +--- + +# fps_overlay + +Muestra FPS y frametime (ms) en una ventana semi-transparente en la esquina superior derecha. + +Si se compila con `TRACY_ENABLE`, incluye un `ZoneScoped` para profiling con Tracy. diff --git a/cpp/functions/viz/bar_chart.cpp b/cpp/functions/viz/bar_chart.cpp new file mode 100644 index 00000000..75bb83d7 --- /dev/null +++ b/cpp/functions/viz/bar_chart.cpp @@ -0,0 +1,26 @@ +#include "viz/bar_chart.h" +#include "implot.h" + +#include + +void bar_chart(const char* title, const char* const* labels, const float* values, int count, float bar_width) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) { + std::vector positions(count); + for (int i = 0; i < count; i++) positions[i] = i; + + ImPlot::SetupAxisTicks(ImAxis_X1, positions.data(), count, labels); + ImPlot::PlotBars("##data", values, count, bar_width); + ImPlot::EndPlot(); + } +} + +void bar_chart(const char* title, const char* const* labels, const double* values, int count, double bar_width) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) { + std::vector positions(count); + for (int i = 0; i < count; i++) positions[i] = i; + + ImPlot::SetupAxisTicks(ImAxis_X1, positions.data(), count, labels); + ImPlot::PlotBars("##data", values, count, bar_width); + ImPlot::EndPlot(); + } +} diff --git a/cpp/functions/viz/bar_chart.h b/cpp/functions/viz/bar_chart.h new file mode 100644 index 00000000..693e5973 --- /dev/null +++ b/cpp/functions/viz/bar_chart.h @@ -0,0 +1,6 @@ +#pragma once + +// Renders a vertical bar chart using ImPlot. +// Call within an ImGui frame. +void bar_chart(const char* title, const char* const* labels, const float* values, int count, float bar_width = 0.67f); +void bar_chart(const char* title, const char* const* labels, const double* values, int count, double bar_width = 0.67); diff --git a/cpp/functions/viz/bar_chart.md b/cpp/functions/viz/bar_chart.md new file mode 100644 index 00000000..b374434e --- /dev/null +++ b/cpp/functions/viz/bar_chart.md @@ -0,0 +1,40 @@ +--- +name: bar_chart +kind: component +lang: cpp +domain: viz +version: "1.0.0" +purity: pure +signature: "void bar_chart(const char* title, const char* const* labels, const float* values, int count, float bar_width)" +description: "Renderiza un grafico de barras verticales usando ImPlot dentro de un frame ImGui" +tags: [implot, chart, visualization, gpu, bar] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [implot] +tested: false +tests: [] +test_file_path: "" +file_path: "cpp/functions/viz/bar_chart.cpp" +framework: imgui +params: + - name: title + desc: "Titulo del grafico de barras" + - name: labels + desc: "Array de etiquetas para el eje X, una por barra" + - name: values + desc: "Array de valores numericos para la altura de cada barra" + - name: count + desc: "Numero de barras (longitud de labels y values)" + - name: bar_width + desc: "Ancho de cada barra como fraccion del espacio disponible (default 0.67)" +output: "Renderiza el grafico de barras en el frame ImGui actual" +--- + +# bar_chart + +Wrapper atomico sobre `ImPlot::PlotBars` con configuracion automatica de etiquetas en el eje X. + +Debe llamarse dentro del render callback de `fn::run_app`. diff --git a/cpp/functions/viz/heatmap.cpp b/cpp/functions/viz/heatmap.cpp new file mode 100644 index 00000000..3f6b155c --- /dev/null +++ b/cpp/functions/viz/heatmap.cpp @@ -0,0 +1,24 @@ +#include "viz/heatmap.h" +#include "implot.h" + +void heatmap(const char* title, const float* values, int rows, int cols, + float scale_min, float scale_max) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0), ImPlotFlags_NoLegend)) { + ImPlot::SetupAxes(nullptr, nullptr, + ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); + ImPlot::PlotHeatmap("##data", values, rows, cols, + scale_min, scale_max); + ImPlot::EndPlot(); + } +} + +void heatmap(const char* title, const double* values, int rows, int cols, + double scale_min, double scale_max) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0), ImPlotFlags_NoLegend)) { + ImPlot::SetupAxes(nullptr, nullptr, + ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); + ImPlot::PlotHeatmap("##data", values, rows, cols, + scale_min, scale_max); + ImPlot::EndPlot(); + } +} diff --git a/cpp/functions/viz/heatmap.h b/cpp/functions/viz/heatmap.h new file mode 100644 index 00000000..9d5496af --- /dev/null +++ b/cpp/functions/viz/heatmap.h @@ -0,0 +1,9 @@ +#pragma once + +// Renders a heatmap using ImPlot. +// Data is row-major: values[row * cols + col]. +// Call within an ImGui frame. +void heatmap(const char* title, const float* values, int rows, int cols, + float scale_min = 0.0f, float scale_max = 0.0f); +void heatmap(const char* title, const double* values, int rows, int cols, + double scale_min = 0.0, double scale_max = 0.0); diff --git a/cpp/functions/viz/heatmap.md b/cpp/functions/viz/heatmap.md new file mode 100644 index 00000000..85ddda03 --- /dev/null +++ b/cpp/functions/viz/heatmap.md @@ -0,0 +1,42 @@ +--- +name: heatmap +kind: component +lang: cpp +domain: viz +version: "1.0.0" +purity: pure +signature: "void heatmap(const char* title, const float* values, int rows, int cols, float scale_min, float scale_max)" +description: "Renderiza un mapa de calor 2D usando ImPlot dentro de un frame ImGui" +tags: [implot, chart, visualization, gpu, heatmap, matrix] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [implot] +tested: false +tests: [] +test_file_path: "" +file_path: "cpp/functions/viz/heatmap.cpp" +framework: imgui +params: + - name: title + desc: "Titulo del heatmap" + - name: values + desc: "Array de valores en orden row-major (values[row * cols + col])" + - name: rows + desc: "Numero de filas de la matriz" + - name: cols + desc: "Numero de columnas de la matriz" + - name: scale_min + desc: "Valor minimo de la escala de color (0 para autodetectar)" + - name: scale_max + desc: "Valor maximo de la escala de color (0 para autodetectar)" +output: "Renderiza el heatmap en el frame ImGui actual" +--- + +# heatmap + +Wrapper atomico sobre `ImPlot::PlotHeatmap`. Renderiza una matriz de valores como mapa de calor con escala de color. + +Los datos deben estar en formato row-major. Si `scale_min` y `scale_max` son ambos 0, ImPlot autodetecta el rango. diff --git a/cpp/functions/viz/line_plot.cpp b/cpp/functions/viz/line_plot.cpp new file mode 100644 index 00000000..4ee7646d --- /dev/null +++ b/cpp/functions/viz/line_plot.cpp @@ -0,0 +1,16 @@ +#include "viz/line_plot.h" +#include "implot.h" + +void line_plot(const char* title, const float* xs, const float* ys, int count) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) { + ImPlot::PlotLine("##data", xs, ys, count); + ImPlot::EndPlot(); + } +} + +void line_plot(const char* title, const double* xs, const double* ys, int count) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) { + ImPlot::PlotLine("##data", xs, ys, count); + ImPlot::EndPlot(); + } +} diff --git a/cpp/functions/viz/line_plot.h b/cpp/functions/viz/line_plot.h new file mode 100644 index 00000000..50e31929 --- /dev/null +++ b/cpp/functions/viz/line_plot.h @@ -0,0 +1,8 @@ +#pragma once + +// Renders a 2D line plot using ImPlot. +// Call within an ImGui frame (inside fn::run_app render callback). +void line_plot(const char* title, const float* xs, const float* ys, int count); + +// Overload with double precision. +void line_plot(const char* title, const double* xs, const double* ys, int count); diff --git a/cpp/functions/viz/line_plot.md b/cpp/functions/viz/line_plot.md new file mode 100644 index 00000000..4b436717 --- /dev/null +++ b/cpp/functions/viz/line_plot.md @@ -0,0 +1,40 @@ +--- +name: line_plot +kind: component +lang: cpp +domain: viz +version: "1.0.0" +purity: pure +signature: "void line_plot(const char* title, const float* xs, const float* ys, int count)" +description: "Renderiza un grafico de lineas 2D usando ImPlot dentro de un frame ImGui" +tags: [implot, chart, visualization, gpu, line] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [implot] +tested: false +tests: [] +test_file_path: "" +file_path: "cpp/functions/viz/line_plot.cpp" +framework: imgui +params: + - name: title + desc: "Titulo del grafico, se muestra como header del plot" + - name: xs + desc: "Array de coordenadas X" + - name: ys + desc: "Array de coordenadas Y" + - name: count + desc: "Numero de puntos en los arrays xs/ys" +output: "Renderiza el grafico de lineas en el frame ImGui actual" +--- + +# line_plot + +Wrapper atomico sobre `ImPlot::PlotLine`. Renderiza un grafico de lineas 2D con los datos proporcionados. + +Debe llamarse dentro del render callback de `fn::run_app` (o cualquier contexto con un frame ImGui activo). + +Soporta `float` y `double` precision. diff --git a/cpp/functions/viz/scatter_plot.cpp b/cpp/functions/viz/scatter_plot.cpp new file mode 100644 index 00000000..f2eca52a --- /dev/null +++ b/cpp/functions/viz/scatter_plot.cpp @@ -0,0 +1,16 @@ +#include "viz/scatter_plot.h" +#include "implot.h" + +void scatter_plot(const char* title, const float* xs, const float* ys, int count) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) { + ImPlot::PlotScatter("##data", xs, ys, count); + ImPlot::EndPlot(); + } +} + +void scatter_plot(const char* title, const double* xs, const double* ys, int count) { + if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) { + ImPlot::PlotScatter("##data", xs, ys, count); + ImPlot::EndPlot(); + } +} diff --git a/cpp/functions/viz/scatter_plot.h b/cpp/functions/viz/scatter_plot.h new file mode 100644 index 00000000..fbefac5d --- /dev/null +++ b/cpp/functions/viz/scatter_plot.h @@ -0,0 +1,6 @@ +#pragma once + +// Renders a scatter plot using ImPlot. +// Call within an ImGui frame. +void scatter_plot(const char* title, const float* xs, const float* ys, int count); +void scatter_plot(const char* title, const double* xs, const double* ys, int count); diff --git a/cpp/functions/viz/scatter_plot.md b/cpp/functions/viz/scatter_plot.md new file mode 100644 index 00000000..2073f0fe --- /dev/null +++ b/cpp/functions/viz/scatter_plot.md @@ -0,0 +1,38 @@ +--- +name: scatter_plot +kind: component +lang: cpp +domain: viz +version: "1.0.0" +purity: pure +signature: "void scatter_plot(const char* title, const float* xs, const float* ys, int count)" +description: "Renderiza un grafico de dispersion usando ImPlot dentro de un frame ImGui" +tags: [implot, chart, visualization, gpu, scatter] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [implot] +tested: false +tests: [] +test_file_path: "" +file_path: "cpp/functions/viz/scatter_plot.cpp" +framework: imgui +params: + - name: title + desc: "Titulo del grafico scatter" + - name: xs + desc: "Array de coordenadas X" + - name: ys + desc: "Array de coordenadas Y" + - name: count + desc: "Numero de puntos en los arrays xs/ys" +output: "Renderiza el grafico de dispersion en el frame ImGui actual" +--- + +# scatter_plot + +Wrapper atomico sobre `ImPlot::PlotScatter`. Renderiza un grafico de dispersion 2D. + +Debe llamarse dentro del render callback de `fn::run_app`. diff --git a/cpp/toolchains/linux-x86_64.cmake b/cpp/toolchains/linux-x86_64.cmake new file mode 100644 index 00000000..c44e2250 --- /dev/null +++ b/cpp/toolchains/linux-x86_64.cmake @@ -0,0 +1,8 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(CMAKE_C_COMPILER gcc) +set(CMAKE_CXX_COMPILER g++) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/cpp/toolchains/mingw-w64.cmake b/cpp/toolchains/mingw-w64.cmake new file mode 100644 index 00000000..8fa78619 --- /dev/null +++ b/cpp/toolchains/mingw-w64.cmake @@ -0,0 +1,17 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Static link runtime so .exe is self-contained +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") diff --git a/cpp/vendor/glfw b/cpp/vendor/glfw new file mode 160000 index 00000000..b00e6a8a --- /dev/null +++ b/cpp/vendor/glfw @@ -0,0 +1 @@ +Subproject commit b00e6a8a88ad1b60c0a045e696301deb92c9a13e diff --git a/cpp/vendor/imgui b/cpp/vendor/imgui new file mode 160000 index 00000000..f5f6ca07 --- /dev/null +++ b/cpp/vendor/imgui @@ -0,0 +1 @@ +Subproject commit f5f6ca07be7ce0ea9eed6c04d55833bac3f6b50b diff --git a/cpp/vendor/implot b/cpp/vendor/implot new file mode 160000 index 00000000..524f9fcd --- /dev/null +++ b/cpp/vendor/implot @@ -0,0 +1 @@ +Subproject commit 524f9fcd48d76c13fdf94c5ffbba8787a1ff7e39 diff --git a/cpp/vendor/tracy b/cpp/vendor/tracy new file mode 160000 index 00000000..00a069d6 --- /dev/null +++ b/cpp/vendor/tracy @@ -0,0 +1 @@ +Subproject commit 00a069d6088ff8d93304eaac4d925cece0e9081c diff --git a/registry/test_parser.go b/registry/test_parser.go index 3abf75d0..81cad386 100644 --- a/registry/test_parser.go +++ b/registry/test_parser.go @@ -35,6 +35,8 @@ func parseTestFile(path, lang string) ([]testCase, error) { return parsePythonTests(content), nil case "bash": return parseBashTests(content), nil + case "cpp": + return parseCppTests(content), nil default: return nil, nil } @@ -115,6 +117,23 @@ func parseBashTests(content string) []testCase { return extractBlocks(lines, positions) } +// parseCppTests extracts C++ test functions (Google Test TEST/TEST_F macros). +var cppTestRe = regexp.MustCompile(`(?m)^(?:TEST|TEST_F|TEST_P)\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)`) + +func parseCppTests(content string) []testCase { + lines := strings.Split(content, "\n") + var positions []testPos + + for i, line := range lines { + if m := cppTestRe.FindStringSubmatch(line); m != nil { + name := m[1] + "." + m[2] // Suite.TestName + positions = append(positions, testPos{name: name, startLine: i}) + } + } + + return extractBlocks(lines, positions) +} + // extractBlocks splits lines into code blocks based on test positions. func extractBlocks(lines []string, positions []testPos) []testCase { var tests []testCase