diff --git a/api/Client.h b/api/Client.h index 5a1d99fe..5cf69125 100644 --- a/api/Client.h +++ b/api/Client.h @@ -27,14 +27,14 @@ namespace arduino { class Client : public Stream { public: - virtual int connect(IPAddress ip, uint16_t port) =0; - virtual int connect(const char *host, uint16_t port) =0; + virtual ErrorCode connect(IPAddress ip, uint16_t port) =0; + virtual ErrorCode connect(const char *host, uint16_t port) =0; virtual size_t write(uint8_t) =0; virtual size_t write(const uint8_t *buf, size_t size) =0; - virtual int available() = 0; - virtual int read() = 0; - virtual int read(uint8_t *buf, size_t size) = 0; - virtual int peek() = 0; + virtual ReturnValue available() = 0; + virtual ReturnValue read() = 0; + virtual ReturnValue read(uint8_t *buf, size_t size) = 0; + virtual ReturnValue peek() = 0; virtual void flush() = 0; virtual void stop() = 0; virtual uint8_t connected() = 0; @@ -43,4 +43,4 @@ class Client : public Stream { uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); }; }; -} \ No newline at end of file +} diff --git a/api/ErrorCodes.h b/api/ErrorCodes.h new file mode 100644 index 00000000..c0b11254 --- /dev/null +++ b/api/ErrorCodes.h @@ -0,0 +1,142 @@ +#pragma once +#include + +namespace arduino { + +typedef int_fast32_t error_t; + +enum : error_t { + ArduinoSuccess = 0, + ArduinoError = -1, + + // TODO better names + ArduinoEPERM = -1, /**< Not owner */ + ArduinoENOENT = -2, /**< No such file or directory */ + ArduinoESRCH = -3, /**< No such context */ + ArduinoEINTR = -4, /**< Interrupted system call */ + ArduinoEIO = -5, /**< I/O error */ + ArduinoENXIO = -6, /**< No such device or address */ + ArduinoE2BIG = -7, /**< Arg list too long */ + ArduinoENOEXEC = -8, /**< Exec format error */ + ArduinoEBADF = -9, /**< Bad file number */ + ArduinoECHILD = -10, /**< No children */ + ArduinoEAGAIN = -11, /**< No more contexts */ + ArduinoENOMEM = -12, /**< Not enough core */ + ArduinoEACCES = -13, /**< Permission denied */ + ArduinoEFAULT = -14, /**< Bad address */ + ArduinoENOTBLK = -15, /**< Block device required */ + ArduinoEBUSY = -16, /**< Mount device busy */ + ArduinoEEXIST = -17, /**< File exists */ + ArduinoEXDEV = -18, /**< Cross-device link */ + ArduinoENODEV = -19, /**< No such device */ + ArduinoENOTDIR = -20, /**< Not a directory */ + ArduinoEISDIR = -21, /**< Is a directory */ + ArduinoEINVAL = -22, /**< Invalid argument */ + ArduinoENFILE = -23, /**< File table overflow */ + ArduinoEMFILE = -24, /**< Too many open files */ + ArduinoENOTTY = -25, /**< Not a typewriter */ + ArduinoETXTBSY = -26, /**< Text file busy */ + ArduinoEFBIG = -27, /**< File too large */ + ArduinoENOSPC = -28, /**< No space left on device */ + ArduinoESPIPE = -29, /**< Illegal seek */ + ArduinoEROFS = -30, /**< Read-only file system */ + ArduinoEMLINK = -31, /**< Too many links */ + ArduinoEPIPE = -32, /**< Broken pipe */ + ArduinoENOMSG = -35, /**< Unexpected message type */ + ArduinoEDEADLK = -45, /**< Resource deadlock avoided */ + ArduinoENOLCK = -46, /**< No locks available */ + ArduinoENOSTR = -60, /**< STREAMS device required */ + ArduinoENODATA = -61, /**< Missing expected message data */ + ArduinoETIME = -62, /**< STREAMS timeout occurred */ + ArduinoENOSR = -63, /**< Insufficient memory */ + ArduinoEPROTO = -71, /**< Generic STREAMS error */ + ArduinoEBADMSG = -77, /**< Invalid STREAMS message */ + ArduinoENOSYS = -88, /**< Function not implemented */ + ArduinoENOTEMPTY = -90, /**< Directory not empty */ + ArduinoENAMETOOLONG = -91, /**< File name too long */ + ArduinoELOOP = -92, /**< Too many levels of symbolic links */ + ArduinoEOPNOTSUPP = -95, /**< Operation not supported on socket */ + ArduinoEPFNOSUPPORT = -96, /**< Protocol family not supported */ + ArduinoECONNRESET = -104, /**< Connection reset by peer */ + ArduinoENOBUFS = -105, /**< No buffer space available */ + ArduinoEAFNOSUPPORT = -106, /**< Addr family not supported */ + ArduinoEPROTOTYPE = -107, /**< Protocol wrong type for socket */ + ArduinoENOTSOCK = -108, /**< Socket operation on non-socket */ + ArduinoENOPROTOOPT = -109, /**< Protocol not available */ + ArduinoESHUTDOWN = -110, /**< Can't send after socket shutdown */ + ArduinoECONNREFUSED = -111, /**< Connection refused */ + ArduinoEADDRINUSE = -112, /**< Address already in use */ + ArduinoECONNABORTED = -113, /**< Software caused connection abort */ + ArduinoENETUNREACH = -114, /**< Network is unreachable */ + ArduinoENETDOWN = -115, /**< Network is down */ + ArduinoETIMEDOUT = -116, /**< Connection timed out */ + ArduinoEHOSTDOWN = -117, /**< Host is down */ + ArduinoEHOSTUNREACH = -118, /**< No route to host */ + ArduinoEINPROGRESS = -119, /**< Operation now in progress */ + ArduinoEALREADY = -120, /**< Operation already in progress */ + ArduinoEDESTADDRREQ = -121, /**< Destination address required */ + ArduinoEMSGSIZE = -122, /**< Message size */ + ArduinoEPROTONOSUPPORT = -123, /**< Protocol not supported */ + ArduinoESOCKTNOSUPPORT = -124, /**< Socket type not supported */ + ArduinoEADDRNOTAVAIL = -125, /**< Can't assign requested address */ + ArduinoENETRESET = -126, /**< Network dropped connection on reset */ + ArduinoEISCONN = -127, /**< Socket is already connected */ + ArduinoENOTCONN = -128, /**< Socket is not connected */ + ArduinoETOOMANYREFS = -129, /**< Too many references: can't splice */ + ArduinoENOTSUP = -134, /**< Unsupported value */ + ArduinoEOVERFLOW = -139, /**< Value overflow */ + ArduinoECANCELED = -140, /**< Operation canceled */ + ArduinoEWOULDBLOCK = ArduinoEAGAIN, /**< Operation would block */ +}; + +/* Error Codes: + * In Arduino if a function returns 0 is considered to have failed, + * while any value different from 0 is considered success. + * Errors are generally represented with an int type that may vary in size depending on the platform. + * For this reason in this representation error_t type is defined with an integer type with a defined size. + */ +class ErrorCode { +public: + constexpr ErrorCode(int value): error(value != 0? ArduinoSuccess : ArduinoError) {} + constexpr ErrorCode(error_t value): error(value) {} + const error_t error; + + constexpr operator bool() const { + return error == ArduinoSuccess; + } +}; + +/* ReturnValueClass is meant to represent all the cases where with a single value + * we want to return an error, if the value is negative, or a meaningful result + * if greater than or equal to 0. + * In order to be retrocompatible with the previous definition boolean evaluation: + * - It must return true, if the value is greater than or equal 0 + * - It must return false, if the value is negatie + * - It must be evaluable as the primitive type associated with + */ +template +class ReturnValueClass { +public: + constexpr ReturnValueClass(T value) + : value(value >= 0? value : 0), error(value < 0? (error_t)value : ArduinoSuccess) {} + + // it would be nice to have a default value on error to Success + constexpr ReturnValueClass(T value, error_t error) + : value(value), error(error) {} + + const T value; // TODO should we leave the const modifier? + const error_t error; + + constexpr operator bool() const { + return error == ArduinoSuccess; + } + + constexpr operator T() const { + return error == ArduinoSuccess ? value : error; + } +}; + +using ReturnValue = ReturnValueClass; +using ReturnLongValue = ReturnValueClass; + +} diff --git a/api/HardwareCAN.h b/api/HardwareCAN.h index 57d9d582..45a21c7e 100644 --- a/api/HardwareCAN.h +++ b/api/HardwareCAN.h @@ -26,6 +26,7 @@ #include "CanMsg.h" #include "CanMsgRingbuffer.h" +#include "ErrorCodes.h" /************************************************************************************** * TYPEDEF @@ -53,7 +54,7 @@ namespace arduino class HardwareCAN { public: - virtual ~HardwareCAN() {} + virtual ~HardwareCAN() = default; /** @@ -62,7 +63,7 @@ class HardwareCAN * @param can_bitrate the bus bit rate * @return true if initialization succeeded and the controller is operational */ - virtual bool begin(CanBitRate const can_bitrate) = 0; + virtual ErrorCode begin(CanBitRate const can_bitrate) = 0; /** * Disable the CAN controller. diff --git a/api/HardwareI2C.h b/api/HardwareI2C.h index 98c6bfd7..a0cd779b 100644 --- a/api/HardwareI2C.h +++ b/api/HardwareI2C.h @@ -27,12 +27,12 @@ namespace arduino { class HardwareI2C : public Stream { public: - virtual void begin() = 0; - virtual void begin(uint8_t address) = 0; + virtual ErrorCode begin() = 0; + virtual ErrorCode begin(uint8_t address) = 0; virtual void end() = 0; virtual void setClock(uint32_t freq) = 0; - + virtual void beginTransmission(uint8_t address) = 0; virtual uint8_t endTransmission(bool stopBit) = 0; virtual uint8_t endTransmission(void) = 0; diff --git a/api/HardwareSPI.h b/api/HardwareSPI.h index 7f6aea2b..b9dd91eb 100644 --- a/api/HardwareSPI.h +++ b/api/HardwareSPI.h @@ -121,7 +121,7 @@ class HardwareSPI virtual void attachInterrupt() = 0; virtual void detachInterrupt() = 0; - virtual void begin() = 0; + virtual ErrorCode begin() = 0; virtual void end() = 0; }; diff --git a/api/HardwareSerial.h b/api/HardwareSerial.h index b687cdf1..a1bd85b1 100644 --- a/api/HardwareSerial.h +++ b/api/HardwareSerial.h @@ -88,12 +88,12 @@ namespace arduino { class HardwareSerial : public Stream { public: - virtual void begin(unsigned long) = 0; - virtual void begin(unsigned long baudrate, uint16_t config) = 0; + virtual ErrorCode begin(unsigned long) = 0; + virtual ErrorCode begin(unsigned long baudrate, uint16_t config) = 0; virtual void end() = 0; virtual int available(void) = 0; - virtual int peek(void) = 0; - virtual int read(void) = 0; + virtual ReturnValue peek(void) = 0; + virtual ReturnValue read(void) = 0; virtual void flush(void) = 0; virtual size_t write(uint8_t) = 0; using Print::write; // pull in write(str) and write(buf, size) from Print @@ -103,4 +103,4 @@ class HardwareSerial : public Stream // XXX: Are we keeping the serialEvent API? extern void serialEventRun(void) __attribute__((weak)); -} \ No newline at end of file +} diff --git a/api/Stream.h b/api/Stream.h index e81c71ba..45809914 100644 --- a/api/Stream.h +++ b/api/Stream.h @@ -23,6 +23,7 @@ #include #include "Print.h" +#include "ErrorCodes.h" // compatibility macros for testing /* @@ -57,9 +58,9 @@ class Stream : public Print int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout public: - virtual int available() = 0; - virtual int read() = 0; - virtual int peek() = 0; + virtual ReturnValue available() = 0; + virtual ReturnValue read() = 0; + virtual ReturnValue peek() = 0; Stream() {_timeout=1000;} @@ -67,7 +68,7 @@ class Stream : public Print void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second unsigned long getTimeout(void) { return _timeout; } - + bool find(const char *target); // reads data from the stream until the target string is found bool find(const uint8_t *target) { return find ((const char *)target); } // returns true if target string is found, false if timed out (see setTimeout) @@ -130,4 +131,4 @@ class Stream : public Print } -using arduino::Stream; \ No newline at end of file +using arduino::Stream; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 31b75483..a206015e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,6 +40,8 @@ set(TEST_SRCS src/Common/test_map.cpp src/Common/test_max.cpp src/Common/test_min.cpp + src/ErrorCodes/test_ErrorCodes.cpp + src/ErrorCodes/test_ReturnValues.cpp src/IPAddress/test_toString.cpp src/IPAddress/test_fromString.cpp src/IPAddress/test_fromString6.cpp diff --git a/test/include/StreamMock.h b/test/include/StreamMock.h index afa4b487..805e59e3 100644 --- a/test/include/StreamMock.h +++ b/test/include/StreamMock.h @@ -26,9 +26,9 @@ class StreamMock : public arduino::Stream void operator << (char const * str); virtual size_t write(uint8_t ch) override; - virtual int available() override; - virtual int read() override; - virtual int peek() override; + arduino::ReturnValue available() override; + arduino::ReturnValue read() override; + arduino::ReturnValue peek() override; private: std::deque _stream; diff --git a/test/src/ErrorCodes/test_ErrorCodes.cpp b/test/src/ErrorCodes/test_ErrorCodes.cpp new file mode 100644 index 00000000..811d31cf --- /dev/null +++ b/test/src/ErrorCodes/test_ErrorCodes.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Arduino. All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +#include + +using namespace arduino; + +/************************************************************************************** + * TEST CODE + **************************************************************************************/ + +TEST_CASE ("An ErrorCode can be evaluated as boolean and respect Arduino values", "[ErrorCodes-Evaluation]") { + REQUIRE((bool)ErrorCode(1) == true); + REQUIRE((bool)ErrorCode(0) == false); +} + +TEST_CASE ("An error is returned with a value different from 0 and 1", "[ErrorCodes-Evaluation]") { + THEN ("if the error is well defined the boolean evaluation of the error code respects Arduino standard") { + arduino::error_t err = 125; + ErrorCode ec(err); + REQUIRE((bool)ec == false); + REQUIRE(ec.error == err); + } + THEN ("if an int is provided the boolean evaluation of the error code respects Arduino standard and error is evaluated to ArduinoSuccess") { + int err = 1; + ErrorCode ec(err); + REQUIRE((bool)ec == true); + REQUIRE(ec.error == ArduinoSuccess); + } + THEN ("if an int is provided the boolean evaluation of the error code respects Arduino standard and error is evaluated to ArduinoError") { + int err = 0; + ErrorCode ec(err); + REQUIRE((bool)ec == false); + REQUIRE(ec.error == ArduinoError); + } + THEN ("if an int is provided the boolean evaluation of the error code respects Arduino standard and the value is not preserved") { + int err = 125; + ErrorCode ec(err); + REQUIRE((bool)ec == false); + REQUIRE(ec.error == ArduinoError); + } + THEN ("if an int is provided the boolean evaluation of the error code respects Arduino standard and the value is not preserved") { + ErrorCode ec(ArduinoELOOP); + REQUIRE((bool)ec == false); + REQUIRE(ec.error == ArduinoELOOP); + } +} + diff --git a/test/src/ErrorCodes/test_ReturnValues.cpp b/test/src/ErrorCodes/test_ReturnValues.cpp new file mode 100644 index 00000000..563128ae --- /dev/null +++ b/test/src/ErrorCodes/test_ReturnValues.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Arduino. All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +#include + +using namespace arduino; + +/************************************************************************************** + * TEST CODE + **************************************************************************************/ + +int test_int(int i) { + return i; +} + +ReturnValue test(int i) { + return i; +} + +TEST_CASE ("A ReturnValue can be evaluated as boolean", "[ReturnValues-Evaluation]") { + // Default constructor takes an integer implicitly, if negative it means error + REQUIRE((bool)ReturnValue(1) == true); + REQUIRE((bool)ReturnValue(-12) == false); +} + +TEST_CASE ("A ReturnValue can be substituted to int in a function return value", "[ReturnValues-Evaluation]") { + REQUIRE((bool)test(1) == true); + REQUIRE((bool)test(-123) == false); + REQUIRE(test_int(1) == (int)test(1)); + REQUIRE(test_int(-123) == (int)test(-123)); +} + +TEST_CASE ("A ReturnValue can be instantiated with explicit error code and value", "[ReturnValues-Evaluation]") { + THEN("It can take a negative value as value and still be successful") { + auto rv = ReturnValue(-1231, ArduinoSuccess); + + REQUIRE((bool)rv == true); + } + THEN("It can take both a value and an error code ") { + auto rv = ReturnValue(-1231, ArduinoError); + + REQUIRE((bool)rv == false); + REQUIRE(rv.value == -1231); + REQUIRE(rv.error == ArduinoError); + } +} diff --git a/test/src/StreamMock.cpp b/test/src/StreamMock.cpp index c1768859..8c42964e 100644 --- a/test/src/StreamMock.cpp +++ b/test/src/StreamMock.cpp @@ -21,17 +21,17 @@ void StreamMock::operator << (char const * str) } size_t StreamMock::write(uint8_t ch) -{ +{ _stream.push_back(static_cast(ch)); return 1; } -int StreamMock::available() +arduino::ReturnValue StreamMock::available() { return _stream.size(); } -int StreamMock::read() +arduino::ReturnValue StreamMock::read() { if (available() == 0) return -1; @@ -44,7 +44,7 @@ int StreamMock::read() return c; } -int StreamMock::peek() +arduino::ReturnValue StreamMock::peek() { if (available() == 0) return -1; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy