From 5343dc28bff9323a7adcbccdbacb25f379c2cb8e Mon Sep 17 00:00:00 2001 From: funworks Date: Tue, 30 Sep 2025 12:33:20 +0000 Subject: [PATCH] feat: add evaluation of few etlcpp features --- .github/workflows/cmake-multi-platform.yml | 3 + CMakeLists.txt | 1 + etlcpp/CMakeLists.txt | 56 ++++++++++ etlcpp/etl_assert_demo.cpp | 124 +++++++++++++++++++++ etlcpp/etl_assert_demo.h | 13 +++ etlcpp/etl_string_view_demo.cpp | 35 ++++++ etlcpp/etl_string_view_demo.h | 18 +++ etlcpp/etl_task_scheduler_demo.cpp | 59 ++++++++++ etlcpp/etl_task_scheduler_demo.h | 4 + etlcpp/etl_timer_demo.cpp | 76 +++++++++++++ etlcpp/etl_timer_demo.h | 4 + etlcpp/main.cpp | 44 ++++++++ etlcpp/my_string_view_demo.cpp | 41 +++++++ etlcpp/my_string_view_demo.h | 18 +++ 14 files changed, 496 insertions(+) create mode 100644 etlcpp/CMakeLists.txt create mode 100644 etlcpp/etl_assert_demo.cpp create mode 100644 etlcpp/etl_assert_demo.h create mode 100644 etlcpp/etl_string_view_demo.cpp create mode 100644 etlcpp/etl_string_view_demo.h create mode 100644 etlcpp/etl_task_scheduler_demo.cpp create mode 100644 etlcpp/etl_task_scheduler_demo.h create mode 100644 etlcpp/etl_timer_demo.cpp create mode 100644 etlcpp/etl_timer_demo.h create mode 100644 etlcpp/main.cpp create mode 100644 etlcpp/my_string_view_demo.cpp create mode 100644 etlcpp/my_string_view_demo.h diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index b347f58..660a977 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -47,6 +47,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Clone ETL dependency + run: git clone --depth 1 https://github.com/ETLCPP/etl.git etlcpp/etl + - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. id: strings diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d944eb..bc1dc30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ include (cmake/helper.cmake) enable_testing() add_subdirectory(maps) +add_subdirectory(etlcpp) add_subdirectory(vectors) add_subdirectory(smart_ptr) add_subdirectory(array) diff --git a/etlcpp/CMakeLists.txt b/etlcpp/CMakeLists.txt new file mode 100644 index 0000000..0d37aae --- /dev/null +++ b/etlcpp/CMakeLists.txt @@ -0,0 +1,56 @@ +# ETL Demo Project - String View and Components Benchmarking +cmake_minimum_required(VERSION 3.10) +project(demo_string_view) + +#----------------------------------------------------------------------------- +# Build Configuration +#----------------------------------------------------------------------------- +# Set Release build type and size optimization, no debug info +set(CMAKE_BUILD_TYPE Release) +if (MSVC) + # MSVC does not support GCC/Clang linker flags + set(CMAKE_CXX_FLAGS_RELEASE "/O2") +else() + set(CMAKE_CXX_FLAGS_RELEASE "-Os -Wl,-Map=${CMAKE_PROJECT_NAME}.map") +endif() + +#----------------------------------------------------------------------------- +# Dependencies +#----------------------------------------------------------------------------- +# Add ETL include directory (relative to this demo folder) +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/etl/include") + message(STATUS "Using local ETL from etlcpp/etl/include") + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/etl/include) +else() + message(WARNING "Local ETL not found, please ensure etl/include is available.") + include_directories(../etl/include) +endif() + +#----------------------------------------------------------------------------- +# ETL Configuration +#----------------------------------------------------------------------------- +# Define ETL macros for enhanced functionality +add_definitions(-DETL_CALLBACK_TIMER_USE_ATOMIC_LOCK) +add_definitions(-DETL_LOG_ERRORS) + +#----------------------------------------------------------------------------- +# Source Files +#----------------------------------------------------------------------------- +set(DEMO_SOURCES + main.cpp + etl_string_view_demo.cpp + my_string_view_demo.cpp + etl_timer_demo.cpp + etl_task_scheduler_demo.cpp + etl_assert_demo.cpp +) + +#----------------------------------------------------------------------------- +# Executable Target +#----------------------------------------------------------------------------- + +add_executable(${PROJECT_NAME} ${DEMO_SOURCES}) + +# Add test for ETL demo application +enable_testing() +add_test(NAME etlcpp_demo COMMAND $) diff --git a/etlcpp/etl_assert_demo.cpp b/etlcpp/etl_assert_demo.cpp new file mode 100644 index 0000000..9f96dd1 --- /dev/null +++ b/etlcpp/etl_assert_demo.cpp @@ -0,0 +1,124 @@ +#include "etl_assert_demo.h" +#include "etl/vector.h" +#include "etl/string.h" +#include "etl/error_handler.h" +#include "etl/exception.h" +#include "etl/not_null.h" +#include +#include +#include +#include +#include +#include + +class my_exception : public etl::exception { +public: + my_exception(const char* file, int line) + : etl::exception("Custom assertion failure", file, line) {} +}; + +namespace { + // Custom error handler to log ETL exceptions + void etl_assert_error_handler(const etl::exception& e) { + printf("[ETL ERROR HANDLER] Exception caught: %s at line %d\n", + e.what(), static_cast(e.line_number())); + } +} + + +// Basic ETL assert demonstration +void benchmark_assert_demo_etl() { + printf("\n=== ETL Assert Demo - Basic Assertions ===\n"); + + // Set up error handler to log exceptions + etl::error_handler::set_callback(); + + // Test 1: Standard ETL exception + printf("Test 1: Standard ETL exception\n"); + ETL_ASSERT(false, ETL_ERROR(etl::vector_out_of_bounds)); + printf("Standard ETL exception triggered and handled\n"); + + // Test 2: Custom exception + printf("\nTest 2: Custom exception\n"); + ETL_ASSERT(false, ETL_ERROR(my_exception)); + printf("Custom exception triggered and handled\n"); +} + +// Equivalent functionality using early returns (no assertions) +void benchmark_assert_demo_standard() { + printf("\n=== Standard Assert Demo - Early Returns ===\n"); + + // Helper lambda for error logging + auto log_error = [](const char* message, int line) { + printf("[STANDARD ERROR HANDLER] Error: %s at line %d\n", message, line); + }; + + // Test 1: Standard error with early return + printf("Test 1: Standard error with early return\n"); + auto test_function_1 = [&]() -> bool { + bool condition = false; + if (!condition) { + log_error("Vector out of bounds", __LINE__); + return false; + } + return true; + }; + + if (!test_function_1()) { + printf("Standard error triggered and handled\n"); + } + + // Test 2: Custom error with early return + printf("\nTest 2: Custom error with early return\n"); + auto test_function_2 = [&]() -> bool { + bool condition = false; + if (!condition) { + log_error("Custom assertion failure", __LINE__); + return false; + } + return true; + }; + + if (!test_function_2()) { + printf("Custom error triggered and handled\n"); + } +} + +// Plain C-style implementation using early returns (no lambdas) +void benchmark_assert_demo_plain_c() { + printf("\n=== Plain C Assert Demo - Early Returns ===\n"); + + // Test 1: Standard error with early return - plain C style + printf("Test 1: Standard error with early return (plain C)\n"); + { + bool condition = false; + if (!condition) { + printf("[PLAIN C ERROR HANDLER] Error: Vector out of bounds at line %d\n", __LINE__); + goto test1_error; + } + printf("Test 1 passed\n"); + goto test1_done; + + test1_error: + printf("Standard error triggered and handled\n"); + + test1_done:; + } + + // Test 2: Custom error with early return - plain C style + printf("\nTest 2: Custom error with early return (plain C)\n"); + { + bool condition = false; + if (!condition) { + printf("[PLAIN C ERROR HANDLER] Error: Custom assertion failure at line %d\n", __LINE__); + goto test2_error; + } + printf("Test 2 passed\n"); + goto test2_done; + + test2_error: + printf("Custom error triggered and handled\n"); + + test2_done:; + } +} \ No newline at end of file diff --git a/etlcpp/etl_assert_demo.h b/etlcpp/etl_assert_demo.h new file mode 100644 index 0000000..b40c7a8 --- /dev/null +++ b/etlcpp/etl_assert_demo.h @@ -0,0 +1,13 @@ +#ifndef ETL_ASSERT_DEMO_H +#define ETL_ASSERT_DEMO_H + +// Demo functions showing ETL assert macro usage +void benchmark_assert_demo_etl(); + +// Demo function showing equivalent functionality with early returns +void benchmark_assert_demo_standard(); + +// Demo function showing plain C-style early returns (no lambdas) +void benchmark_assert_demo_plain_c(); + +#endif // ETL_ASSERT_DEMO_H \ No newline at end of file diff --git a/etlcpp/etl_string_view_demo.cpp b/etlcpp/etl_string_view_demo.cpp new file mode 100644 index 0000000..bb70713 --- /dev/null +++ b/etlcpp/etl_string_view_demo.cpp @@ -0,0 +1,35 @@ +#include "etl/string_view.h" +#include "etl/error_handler.h" +#include "etl_string_view_demo.h" + +static etl::string_view get_sv() { + return etl::string_view{"Hello, Embedded Template Library!"}; +} + +size_t benchmark_etl_string_view_size() { + return get_sv().size(); +} + +int benchmark_etl_string_view_empty() { + return get_sv().empty(); +} + +char benchmark_etl_string_view_front() { + return get_sv().front(); +} + +char benchmark_etl_string_view_back() { + return get_sv().back(); +} + +size_t benchmark_etl_string_view_find() { + return get_sv().find("Template"); +} + +int benchmark_etl_string_view_compare() { + return get_sv().compare("Hello, Embedded Template Library!"); +} + +size_t benchmark_etl_string_view_substr_size() { + return get_sv().substr(7, 9).size(); +} diff --git a/etlcpp/etl_string_view_demo.h b/etlcpp/etl_string_view_demo.h new file mode 100644 index 0000000..90e6984 --- /dev/null +++ b/etlcpp/etl_string_view_demo.h @@ -0,0 +1,18 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t benchmark_etl_string_view_size(); +int benchmark_etl_string_view_empty(); +char benchmark_etl_string_view_front(); +char benchmark_etl_string_view_back(); +size_t benchmark_etl_string_view_find(); +int benchmark_etl_string_view_compare(); +size_t benchmark_etl_string_view_substr_size(); + +#ifdef __cplusplus +} +#endif diff --git a/etlcpp/etl_task_scheduler_demo.cpp b/etlcpp/etl_task_scheduler_demo.cpp new file mode 100644 index 0000000..c9d28c0 --- /dev/null +++ b/etlcpp/etl_task_scheduler_demo.cpp @@ -0,0 +1,59 @@ +#include "etl_task_scheduler_demo.h" +#include +#include +#include +#include +#include + +// Example task class +class PrintTask : public etl::task { +public: + PrintTask(const char* msg, etl::task_priority_t priority) + : etl::task(priority), message(msg), work_count(0) {} + + uint32_t task_request_work() const override { + // Simulate work available for 5 cycles + return work_count < 5 ? 1 : 0; + } + + void task_process_work() override { + printf("[Task] %s (cycle %d)\n", message, work_count + 1); + ++work_count; + } + +private: + const char* message; + mutable int work_count; +}; + +// ETL-only scheduler demo (no std::thread, no std::atomic) +void benchmark_etl_task_scheduler_etl_only() +{ + PrintTask t1("A", 2); + PrintTask t2("B", 1); + etl::scheduler sched; + sched.add_task(t1); + sched.add_task(t2); + + // Manually run scheduler cycles instead of using start() which loops forever + printf("Running ETL scheduler for limited cycles:\n"); + for (int cycle = 0; cycle < 5; ++cycle) { + printf("Cycle %d:\n", cycle + 1); + + // Manually check each task and process work + bool has_work = false; + if (t1.task_request_work() > 0) { + t1.task_process_work(); + has_work = true; + } + if (t2.task_request_work() > 0) { + t2.task_process_work(); + has_work = true; + } + + if (!has_work) { + printf("No more work available, stopping scheduler.\n"); + break; + } + } +} diff --git a/etlcpp/etl_task_scheduler_demo.h b/etlcpp/etl_task_scheduler_demo.h new file mode 100644 index 0000000..762527c --- /dev/null +++ b/etlcpp/etl_task_scheduler_demo.h @@ -0,0 +1,4 @@ +#pragma once + +void benchmark_etl_task_scheduler_demo_run(); +void benchmark_etl_task_scheduler_etl_only(); diff --git a/etlcpp/etl_timer_demo.cpp b/etlcpp/etl_timer_demo.cpp new file mode 100644 index 0000000..369fa4b --- /dev/null +++ b/etlcpp/etl_timer_demo.cpp @@ -0,0 +1,76 @@ +#include "etl_timer_demo.h" +#include +#include +#include +#include +#include + +namespace { + // Timer callback function + void on_timer() + { + printf("[ETL Timer] Callback fired!\n"); + } + + // Class with member data for delegate callback with "arguments" + class TimerHandler { + public: + TimerHandler(int val) : value(val) {} + + void on_timer_with_data() { + printf("[ETL Timer] Callback with value: %d\n", value); + value++; // Increment for next call + } + + void set_value(int val) { value = val; } + + private: + int value; + }; +} + + +/** + * @typedef + */ +void benchmark_etl_timer_callback_demo() +{ + etl::callback_timer<1> cb_timer; + cb_timer.enable(true); + static etl::callback_timer<1>::callback_type timer_cb = etl::delegate::create(); + auto id = cb_timer.register_timer(timer_cb, 500, true); + cb_timer.start(id); + for (int i = 0; i < 4; ++i) { + //std::this_thread::sleep_for(std::chrono::milliseconds(500)); + cb_timer.tick(500); + } + cb_timer.stop(id); +} + +// ETL timer demo with delegate and "arguments" via member data +void benchmark_etl_timer_delegate_with_args_demo() +{ + static TimerHandler handler(100); // Start with value 100 + etl::callback_timer<1> cb_timer; + cb_timer.enable(true); + + // Create delegate pointing to member function + static etl::callback_timer<1>::callback_type timer_cb = + etl::delegate::create(handler); + + auto id = cb_timer.register_timer(timer_cb, 500, true); + cb_timer.start(id); + + for (int i = 0; i < 4; ++i) { + cb_timer.tick(500); + } + cb_timer.stop(id); +} + +void benchmark_etl_timer_demo_run() +{ + printf("[Basic Timer Demo]\n"); + benchmark_etl_timer_callback_demo(); + printf("\n[Delegate with Args Demo]\n"); + benchmark_etl_timer_delegate_with_args_demo(); +} diff --git a/etlcpp/etl_timer_demo.h b/etlcpp/etl_timer_demo.h new file mode 100644 index 0000000..6169b98 --- /dev/null +++ b/etlcpp/etl_timer_demo.h @@ -0,0 +1,4 @@ +#pragma once + +// Simple ETL timer callback demo header +void benchmark_etl_timer_demo_run(); diff --git a/etlcpp/main.cpp b/etlcpp/main.cpp new file mode 100644 index 0000000..282362e --- /dev/null +++ b/etlcpp/main.cpp @@ -0,0 +1,44 @@ +#include + +#include "etl_string_view_demo.h" +#include "my_string_view_demo.h" +#include "etl_timer_demo.h" +#include "etl_task_scheduler_demo.h" +#include "etl_assert_demo.h" + +int main() { + printf("[ETL string_view]\n"); + printf("size: %zu\n", benchmark_etl_string_view_size()); + printf("empty: %d\n", benchmark_etl_string_view_empty()); + printf("front: '%c'\n", benchmark_etl_string_view_front()); + printf("back: '%c'\n", benchmark_etl_string_view_back()); + printf("find('Template'): %zu\n", benchmark_etl_string_view_find()); + printf("compare('Hello, Embedded Template Library!'): %d\n", benchmark_etl_string_view_compare()); + printf("substr(7, 9) size: %zu\n", benchmark_etl_string_view_substr_size()); + + printf("\n[My string_view]\n"); + printf("size: %zu\n", benchmark_my_string_view_size()); + printf("empty: %d\n", benchmark_my_string_view_empty()); + printf("front: '%c'\n", benchmark_my_string_view_front()); + printf("back: '%c'\n", benchmark_my_string_view_back()); + printf("find('Template'): %zu\n", benchmark_my_string_view_find()); + printf("compare('Hello, Embedded Template Library!'): %d\n", benchmark_my_string_view_compare()); + printf("substr(7, 9) size: %zu\n", benchmark_my_string_view_substr_size()); + + printf("\n[ETL Timer Demo]\n"); + benchmark_etl_timer_demo_run(); + + printf("\n[ETL Scheduler Demo]\n"); + benchmark_etl_task_scheduler_etl_only(); + + printf("\n[ETL Assert Macros Demo]\n"); + benchmark_assert_demo_etl(); + + printf("\n[Standard Assert Demo]\n"); + benchmark_assert_demo_standard(); + + printf("\n[Plain C Assert Demo]\n"); + benchmark_assert_demo_plain_c(); + + return 0; +} diff --git a/etlcpp/my_string_view_demo.cpp b/etlcpp/my_string_view_demo.cpp new file mode 100644 index 0000000..636f880 --- /dev/null +++ b/etlcpp/my_string_view_demo.cpp @@ -0,0 +1,41 @@ +#include "my_string_view_demo.h" +#include + +struct my_string_view { + const char* data_; + size_t size_; + my_string_view(const char* s) : data_(s), size_(strlen(s)) {} + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + char front() const { return data_[0]; } + char back() const { return data_[size_-1]; } + size_t find(const char* sub) const { + const char* pos = strstr(data_, sub); + return pos ? (size_t)(pos - data_) : (size_t)-1; + } + int compare(const char* other) const { + return strcmp(data_, other); + } + my_string_view substr(size_t pos, size_t count) const { + if (pos > size_) return my_string_view(""); + size_t n = (pos + count > size_) ? size_ - pos : count; + char* buf = new char[n+1]; + memcpy(buf, data_ + pos, n); + buf[n] = '\0'; + my_string_view sv(buf); + delete[] buf; + return sv; + } +}; + +static my_string_view get_sv() { + return my_string_view("Hello, Embedded Template Library!"); +} + +size_t benchmark_my_string_view_size() { return get_sv().size(); } +int benchmark_my_string_view_empty() { return get_sv().empty(); } +char benchmark_my_string_view_front() { return get_sv().front(); } +char benchmark_my_string_view_back() { return get_sv().back(); } +size_t benchmark_my_string_view_find() { return get_sv().find("Template"); } +int benchmark_my_string_view_compare() { return get_sv().compare("Hello, Embedded Template Library!"); } +size_t benchmark_my_string_view_substr_size() { return get_sv().substr(7, 9).size(); } diff --git a/etlcpp/my_string_view_demo.h b/etlcpp/my_string_view_demo.h new file mode 100644 index 0000000..7316b7d --- /dev/null +++ b/etlcpp/my_string_view_demo.h @@ -0,0 +1,18 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t benchmark_my_string_view_size(); +int benchmark_my_string_view_empty(); +char benchmark_my_string_view_front(); +char benchmark_my_string_view_back(); +size_t benchmark_my_string_view_find(); +int benchmark_my_string_view_compare(); +size_t benchmark_my_string_view_substr_size(); + +#ifdef __cplusplus +} +#endif