c17 trait experiments
This commit is contained in:
parent
c1cc2895e5
commit
5a8b87fa74
130
experiments/main.c
Normal file
130
experiments/main.c
Normal file
@ -0,0 +1,130 @@
|
||||
// component_networked_demo.cpp ---------------------------------------------
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include <type_traits>
|
||||
#include <ArduinoLog.h>
|
||||
|
||||
/* ────────────────────────────────── 0. Build flag → constexpr ──────────── */
|
||||
#if defined(ENABLE_MODBUS) && ENABLE_MODBUS
|
||||
#define kEnableModbus true
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
/* ────────────────────────────────── 1. Helper: maybe‑base ──────────────── */
|
||||
struct dummy_base {};
|
||||
|
||||
template <typename Base, bool Enable>
|
||||
using maybe_base_t =
|
||||
typename std::conditional<Enable, Base, dummy_base>::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<Networked, kEnableModbus>;
|
||||
|
||||
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 <bool Enable>
|
||||
class TemperatureSensorNetworkedImpl;
|
||||
|
||||
/* ----- 4.a NON‑networked flavour --------------------------------------- */
|
||||
template <>
|
||||
class TemperatureSensorNetworkedImpl<false> : 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<true> : public Component, public Networked
|
||||
{
|
||||
std::array<uint16_t, 2> 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<kEnableModbus>;
|
||||
|
||||
/* ────────────────────────────────── 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<Networked, TemperatureSensorNetworked>::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();
|
||||
}
|
||||
217
experiments/main.cpp
Normal file
217
experiments/main.cpp
Normal file
@ -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 <Arduino.h> // Added for Serial
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
// #include <iostream> // Replaced by Arduino.h
|
||||
#include <type_traits>
|
||||
#include <string_view> // For info() return type example
|
||||
#include <cstring> // 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<typename, typename = std::void_t<>>
|
||||
struct is_valid_feature : std::false_type {};
|
||||
|
||||
template<typename F>
|
||||
struct is_valid_feature<F, std::void_t<
|
||||
decltype(std::declval<F&>().setup()),
|
||||
decltype(std::declval<F&>().loop()),
|
||||
decltype(std::declval<const F&>().info()) // Added const info() check
|
||||
>> : std::true_type {};
|
||||
|
||||
// Updated helper function
|
||||
template<typename F> constexpr bool is_valid_feature_func() { return is_valid_feature<F>::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<uint16_t,2> 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<typename... Fs>
|
||||
class Device : public LoggerFeature, public Fs...
|
||||
{
|
||||
// Ensure the default and all *additional* base classes Fs satisfy the requirements
|
||||
static_assert(is_valid_feature_func<LoggerFeature>(), "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<Fs>() && ...), "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<CoreFeature, ModbusFeature>;
|
||||
#else
|
||||
using TemperatureSensor = Device<CoreFeature>;
|
||||
#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);
|
||||
}
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
Loading…
Reference in New Issue
Block a user