From 5a8b87fa74f0c4ed3e4a4ceaede1a77d4b18b7b8 Mon Sep 17 00:00:00 2001 From: babayaga Date: Fri, 23 May 2025 16:53:09 +0200 Subject: [PATCH] c17 trait experiments --- experiments/main.c | 130 ++++++++++++++++++++++++++ experiments/main.cpp | 217 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 experiments/main.c create mode 100644 experiments/main.cpp diff --git a/experiments/main.c b/experiments/main.c new file mode 100644 index 00000000..8c1d19cf --- /dev/null +++ b/experiments/main.c @@ -0,0 +1,130 @@ +// component_networked_demo.cpp --------------------------------------------- +#include +#include +#include + +#include +#include + +/* ────────────────────────────────── 0. Build flag → constexpr ──────────── */ +#if defined(ENABLE_MODBUS) && ENABLE_MODBUS + #define kEnableModbus true +#else + +#endif + +/* ────────────────────────────────── 1. Helper: maybe‑base ──────────────── */ +struct dummy_base {}; + +template +using maybe_base_t = + typename std::conditional::type; + +/* ────────────────────────────────── 2. Core contracts ──────────────────── */ +struct Component { + virtual void setup() = 0; + virtual void loop() = 0; + virtual ~Component() = default; +}; + +struct RegisterMap { + const uint16_t* data; + std::size_t size; +}; + +struct Networked { + virtual RegisterMap registerMapping() const = 0; + virtual uint16_t deviceAddress() const { return 1; } + virtual void onRegisterWrite(uint16_t, uint16_t) {} + virtual ~Networked() = default; +}; + +/* ────────────────────────────────── 3. ComponentNetworked ──────────────── */ +// Carries Networked only when kEnableModbus == true +using NetworkBase = maybe_base_t; + +class ComponentNetworked : public Component, public NetworkBase { + /* purely a glue; still abstract if Networked is really inherited */ +}; + +/* ────────────────────────────────── 4. TemperatureSensorNetworked ─────── */ +// Two specialisations – the compiler picks one at translation time +template +class TemperatureSensorNetworkedImpl; + +/* ----- 4.a NON‑networked flavour --------------------------------------- */ +template <> +class TemperatureSensorNetworkedImpl : public ComponentNetworked +{ +public: + void setup() override { Log.notice(F("[Temp<%d>] setup (lean)\n"), kEnableModbus); } + void loop() override { /* Log.verb(F("[Temp<%d>] loop (lean)\n"), kEnableModbus); */ } + + float readCelsius() const { return 25.0f; } +}; + +/* ----- 4.b Network‑capable flavour ------------------------------------- */ +template <> +class TemperatureSensorNetworkedImpl : public Component, public Networked +{ + std::array regs_{ 250 /*25 °C×10*/, 0 }; + +public: + /* Component part */ + void setup() override { Log.notice(F("[Temp<%d>] setup (net)\n"), kEnableModbus); } + void loop() override { /* Log.verb(F("[Temp<%d>] loop (net)\n"), kEnableModbus); */ } + + /* Networked part */ + RegisterMap registerMapping() const override { + return { regs_.data(), regs_.size() }; + } + uint16_t deviceAddress() const override { return 42; } + void onRegisterWrite(uint16_t r, uint16_t v) override { + if (r < regs_.size()) regs_[r] = v; + } + + float readCelsius() const { return regs_[0] / 10.0f; } +}; + +/* ----- 4.c Public alias ------------------------------------------------- */ +using TemperatureSensorNetworked = + TemperatureSensorNetworkedImpl; + +/* ────────────────────────────────── 5. Arduino setup/loop ───────────── */ + +// Global instance of the sensor +TemperatureSensorNetworked dev; + +void setup() { + // Initialize Serial and Logging + Serial.begin(115200); + while (!Serial && millis() < 3000) { delay(100); } // Wait for serial port connection + + Log.begin(LOG_LEVEL_VERBOSE, &Serial); + Log.notice(F("\n--- Logging Initialized ---\n")); + Log.notice(F("kEnableModbus = %d\n"), kEnableModbus); + + dev.setup(); + + // Conditionally compile the network check using the preprocessor +#if kEnableModbus + Log.notice(F("Network features compiled.\n")); + // Check if the resolved type actually inherits from Networked + if (std::is_base_of::value) { + Log.notice(F(" Device IS Networked. Address: %d, Registers:"), dev.deviceAddress()); + auto map = dev.registerMapping(); + for (std::size_t i = 0; i < map.size; ++i) Log.notice(F(" %d"), map.data[i]); + Log.notice(F("\n")); + } else { + Log.warning(F(" Device IS NOT Networked (type check failed).\n")); + } +#else + Log.notice(F("Network features NOT compiled.\n")); + // Demonstrate accessing non-networked specific method + Log.notice(F(" Device reading (lean): %.1f C\n"), dev.readCelsius()); +#endif +} + +void loop() { + dev.loop(); +} \ No newline at end of file diff --git a/experiments/main.cpp b/experiments/main.cpp new file mode 100644 index 00000000..81019459 --- /dev/null +++ b/experiments/main.cpp @@ -0,0 +1,217 @@ +// feature_di_component_demo.cpp ------------------------------------------- +// Build the "lean" variant : g++ -std=c++20 -O2 -DDEMO_MAIN feature_di_component_demo.cpp -o demo +// Build the "Modbus" variant: g++ -std=c++20 -O2 -DDEMO_MAIN -DENABLE_MODBUS feature_di_component_demo.cpp -o demo_net +// @link ://chatgpt.com/share/6803c6b7-0468-8001-857f-352a31c8c9bd +// ─────────────────────────────────────────────────────────────────────────── + +#include // Added for Serial +#include +#include +#include +// #include // Replaced by Arduino.h +#include +#include // For info() return type example +#include // For strcmp + +/*──────────────────── 1. Tiny utility -----------------------------------*/ +struct RegisterMap { + const uint16_t* data; + std::size_t size; +}; + +/*──────────────────── 2. "Feature" concept (any module that has setup/loop/info)*/ +// Renamed and updated SFINAE check +template> +struct is_valid_feature : std::false_type {}; + +template +struct is_valid_feature().setup()), + decltype(std::declval().loop()), + decltype(std::declval().info()) // Added const info() check +>> : std::true_type {}; + +// Updated helper function +template constexpr bool is_valid_feature_func() { return is_valid_feature::value; } + + +/*──────────────────── 3. Individual feature packs -----------------------*/ +// 3a. Core sensor logic ──────────────────────────────────────────────── +struct CoreFeature { + static constexpr bool has_network = false; + + void setup() { Serial.println(F("[Core] setup")); } // Use Serial + void loop () { /*Serial.println(F("[Core] loop"));*/ } // Commented out for less noise + // Added info method + void info() const { Serial.println(F("[Core] Info: Basic temperature sensing.")); } // Use Serial + + float readCelsius() const { return 25.0f; } +}; + +// 3b. Optional Modbus/Network feature ────────────────────────────────── +struct ModbusFeature { + static constexpr bool has_network = true; + + std::array regs{ 250 /*25 °C×10*/, 0 }; + + void setup() { Serial.println(F("[Modbus] setup")); } // Use Serial + void loop () { /*Serial.println(F("[Modbus] loop"));*/ } // Commented out for less noise + // Added info method + void info() const { // Use Serial + Serial.print(F("[Modbus] Info: Modbus TCP/IP enabled. Address: ")); + Serial.println(deviceAddress()); + } + + + RegisterMap registerMapping() const { return { regs.data(), regs.size() }; } + uint16_t deviceAddress() const { return 42; } + + void onRegisterWrite(uint16_t r, uint16_t v) { + if (r < regs.size()) regs[r] = v; + } +}; + +// 3c. Default Logger feature ───────────────────────────────────────────── +struct LoggerFeature { + static constexpr bool has_network = true; // Assuming logger might send logs over network + void setup() { Serial.println(F("[Logger] setup")); } // Use Serial + void loop () { /*Serial.println(F("[Logger] loop"));*/ } // Commented out for less noise + // Added info method + void info() const { Serial.println(F("[Logger] Info: Basic console logging active.")); } // Use Serial +}; + + +/*──────────────────── 4. Generic "Device" aggregator ───────────────────*/ +// Use static_assert with the updated SFINAE check +// Inherit LoggerFeature by default +template +class Device : public LoggerFeature, public Fs... +{ + // Ensure the default and all *additional* base classes Fs satisfy the requirements + static_assert(is_valid_feature_func(), "Default LoggerFeature must implement setup(), loop(), and info()"); + // Use C++17 fold expression within the static_assert for additional features + static_assert(sizeof...(Fs) == 0 || (is_valid_feature_func() && ...), "All additional features must implement setup(), loop(), and info()"); + + +public: + void setup() { + LoggerFeature::setup(); // Explicitly call setup for the default feature + if constexpr (sizeof...(Fs) > 0) { // Check if there are other features + (Fs::setup(), ...); // Call setup for the rest using fold-expression + } + } + + void loop () { + LoggerFeature::loop(); // Explicitly call loop for the default feature + if constexpr (sizeof...(Fs) > 0) { + (Fs::loop (), ...); // Call loop for the rest + } + } + + // Added aggregated info method + void info() const { + // Call info() for LoggerFeature and all Fs... using a fold expression + (LoggerFeature::info(), (Fs::info(), ...)); + } + + // Include LoggerFeature's network status in the calculation + static constexpr bool has_network = LoggerFeature::has_network || (false || ... || Fs::has_network); +}; + +/*──────────────────── 5. Choose the feature bundle (application layer) ───*/ +// LoggerFeature is now implicitly included by Device +#ifdef ENABLE_MODBUS +using TemperatureSensor = Device; +#else +using TemperatureSensor = Device; +#endif + +/*──────────────────── Arduino Entry Points -------------------------------*/ +// Instantiate the device globally +TemperatureSensor device; + +// Buffer for serial commands +#define SERIAL_CMD_BUFFER_SIZE 64 +char serialCmdBuffer[SERIAL_CMD_BUFFER_SIZE]; +uint8_t serialCmdBufferIdx = 0; + +// Helper function to print commands +void printHelp() { + Serial.println(F("--- Serial Commands ---")); + Serial.println(F("1 - Show device info")); + Serial.println(F("? - Print this help message")); +} + +void setup() { + // Initialize Serial for logging, if needed (optional) + Serial.begin(115200); + // Wait for serial port to connect (needed for native USB) + // On some boards, this delay is needed to allow the Serial Monitor to connect + // On others (like ESP32), Serial begins immediately. Adjust if needed. + delay(1000); + while (!Serial && millis() < 3000); // Wait up to 3 seconds + + Serial.println(F("\n--- Serial Interface Ready ---")); + printHelp(); + + // <<< Hypothetical serial connection point >>> + Serial.println(F("--- Device Info ---")); + device.info(); // Call the aggregated info method + Serial.println(F("--- Device Setup ---")); + device.setup(); // Call the aggregated setup + Serial.println(F("--- Setup Complete ---")); +} + +void loop() { + device.loop(); // Call the aggregated loop + + // --- Handle Serial Input --- + while (Serial.available() > 0) { + char receivedChar = Serial.read(); + + // Handle backspace + if (receivedChar == '\b' || receivedChar == 127) { + if (serialCmdBufferIdx > 0) { + serialCmdBufferIdx--; + Serial.print("\b \b"); // Move cursor back, print space, move back again + } + continue; // Continue to next character + } + + // Echo character back (optional, good for interactive use) + Serial.print(receivedChar); + + // Process command on newline or carriage return + if (receivedChar == '\n' || receivedChar == '\r') { + serialCmdBuffer[serialCmdBufferIdx] = '\0'; // Null-terminate the string + Serial.println(); // Move to next line after command received + + if (serialCmdBufferIdx > 0) { // Process if buffer is not empty + // --- Process Commands --- + if (strcmp(serialCmdBuffer, "1") == 0) { + Serial.println(F("--- Device Info ---")); + device.info(); + } else if (strcmp(serialCmdBuffer, "?") == 0) { + printHelp(); + } else { + Serial.print(F("Unknown command: '")); + Serial.print(serialCmdBuffer); + Serial.println(F("'")); + printHelp(); // Show help on unknown command + } + } + // Reset buffer index for the next command + serialCmdBufferIdx = 0; + serialCmdBuffer[0] = '\0'; + } else if (serialCmdBufferIdx < SERIAL_CMD_BUFFER_SIZE - 1) { + // Add character to buffer if it's not newline/CR and buffer has space + serialCmdBuffer[serialCmdBufferIdx++] = receivedChar; + } + // Ignore characters if buffer is full until newline/CR is received + } + + + // Add delay or other logic if needed for stability + // delay(10); +} +// ───────────────────────────────────────────────────────────────────────────