From 4ce1a477e3ca301ddd02f896ccf5eb6189959637 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 28 Apr 2019 18:57:17 +0200 Subject: [PATCH] [io] Refactor IOStream to use external printf --- .../basic/read_analog_voltage/project.xml | 1 + examples/avr/block_device_mirror/project.xml | 1 + examples/avr/can/mcp2515/project.xml | 1 + examples/avr/can/mcp2515_uart/project.xml | 1 + examples/avr/xpcc/receiver/project.xml | 2 + examples/avr/xpcc/sender/project.xml | 2 + .../architecture/interface/accessor_flash.hpp | 2 +- src/modm/io/iostream.cpp | 289 +++------ src/modm/io/iostream.hpp | 599 ------------------ src/modm/io/iostream.hpp.in | 320 ++++++++++ src/modm/io/iostream_float.cpp | 133 ---- src/modm/io/iostream_printf.cpp | 338 ---------- src/modm/io/iostream_printf.cpp.in | 213 +++++++ src/modm/io/module.lb | 35 +- src/modm/platform/core/avr/flash_reader.hpp | 2 +- .../platform/core/cortex/flash_reader.hpp | 3 +- test/config/al-avreb-can.xml | 3 + test/config/arduino-nano.xml | 3 + test/config/arduino-uno.xml | 3 + test/modm/io/io_stream_test.cpp | 117 +++- test/modm/io/io_stream_test.hpp | 19 + 21 files changed, 769 insertions(+), 1318 deletions(-) delete mode 100644 src/modm/io/iostream.hpp create mode 100644 src/modm/io/iostream.hpp.in delete mode 100644 src/modm/io/iostream_float.cpp delete mode 100644 src/modm/io/iostream_printf.cpp create mode 100644 src/modm/io/iostream_printf.cpp.in diff --git a/examples/arduino_uno/basic/read_analog_voltage/project.xml b/examples/arduino_uno/basic/read_analog_voltage/project.xml index 9da9af1055..c43285ab08 100644 --- a/examples/arduino_uno/basic/read_analog_voltage/project.xml +++ b/examples/arduino_uno/basic/read_analog_voltage/project.xml @@ -2,6 +2,7 @@ modm:arduino-uno + modm:platform:adc diff --git a/examples/avr/block_device_mirror/project.xml b/examples/avr/block_device_mirror/project.xml index b4b432f583..f83a2b8abc 100644 --- a/examples/avr/block_device_mirror/project.xml +++ b/examples/avr/block_device_mirror/project.xml @@ -3,6 +3,7 @@ + modm:debug diff --git a/examples/avr/can/mcp2515/project.xml b/examples/avr/can/mcp2515/project.xml index 0c4bbafe63..b8763c8eb9 100644 --- a/examples/avr/can/mcp2515/project.xml +++ b/examples/avr/can/mcp2515/project.xml @@ -3,6 +3,7 @@ + modm:driver:mcp2515 diff --git a/examples/avr/can/mcp2515_uart/project.xml b/examples/avr/can/mcp2515_uart/project.xml index 0e42b1af09..1ac9a5bc65 100644 --- a/examples/avr/can/mcp2515_uart/project.xml +++ b/examples/avr/can/mcp2515_uart/project.xml @@ -3,6 +3,7 @@ + modm:architecture:interrupt diff --git a/examples/avr/xpcc/receiver/project.xml b/examples/avr/xpcc/receiver/project.xml index 58d8921185..21c391b0a0 100644 --- a/examples/avr/xpcc/receiver/project.xml +++ b/examples/avr/xpcc/receiver/project.xml @@ -5,6 +5,8 @@ + + modm:communication:xpcc:generator diff --git a/examples/avr/xpcc/sender/project.xml b/examples/avr/xpcc/sender/project.xml index 98ebd973fc..95bc99b025 100644 --- a/examples/avr/xpcc/sender/project.xml +++ b/examples/avr/xpcc/sender/project.xml @@ -5,6 +5,8 @@ + + modm:communication:xpcc:generator diff --git a/src/modm/architecture/interface/accessor_flash.hpp b/src/modm/architecture/interface/accessor_flash.hpp index ab598d83ed..ca11ca6eb3 100644 --- a/src/modm/architecture/interface/accessor_flash.hpp +++ b/src/modm/architecture/interface/accessor_flash.hpp @@ -37,7 +37,7 @@ /// Declare a flash string inline /// @ingroup modm_architecture_accessor -#define PSTR(s) ((const char *)(s)) +#define IFSS(s) ((const char *)(s)) #else // !__DOXYGEN__ diff --git a/src/modm/io/iostream.cpp b/src/modm/io/iostream.cpp index 505601c4cc..b55bc253e4 100644 --- a/src/modm/io/iostream.cpp +++ b/src/modm/io/iostream.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2009-2010, Martin Rosekeit * Copyright (c) 2009-2012, Fabian Greif * Copyright (c) 2011, Georgi Grinshpun - * Copyright (c) 2012-2014, Niklas Hauser + * Copyright (c) 2012-2014, 2019 Niklas Hauser * Copyright (c) 2016, Sascha Schade * * This file is part of the modm project. @@ -13,256 +13,129 @@ */ // ---------------------------------------------------------------------------- -#include - -#include -#include - #include "iostream.hpp" +#include -FLASH_STORAGE(uint16_t base[]) = { 10, 100, 1000, 10000 }; - -// ---------------------------------------------------------------------------- -modm::IOStream::IOStream(IODevice& outputDevice) : - device(&outputDevice), - mode(Mode::Ascii) +namespace modm { -} -// ---------------------------------------------------------------------------- -void -modm::IOStream::writeInteger(int16_t value) +IOStream& +IOStream::get(char* s, size_t n) { - if (value < 0) { - this->device->write('-'); - this->writeInteger(static_cast(-value)); - } - else{ - this->writeInteger(static_cast(value)); + if(n < 1) { + return *this; } -} - -void -modm::IOStream::writeInteger(uint16_t value) -{ - accessor::Flash basePtr = modm::accessor::asFlash(base); - - bool zero = true; - uint8_t i = 4; - do { - i--; - char d; - for (d = static_cast('0'); value >= basePtr[i]; value -= basePtr[i]) - { - d++; - zero = false; - } - if (!zero) { - this->device->write(d); + char cc; + size_t ii; + for(ii = 0; ii < (n-1); ++ii) { + if(device->read(cc)) { + s[ii] = cc; + } else { + break; } - } while (i); - - this->device->write(static_cast(value) + '0'); -} - -void -modm::IOStream::writeInteger(int32_t value) -{ -#if defined(MODM_CPU_AVR) - char buffer[ArithmeticTraits::decimalDigits + 1]; // +1 for '\0' - - // Uses the optimized non standard function 'ltoa()' which is - // not always available. - - this->device->write(ltoa(value, buffer, 10)); -#else - if (value < 0) { - this->device->write('-'); - this->writeInteger(static_cast(-value)); - } - else{ - this->writeInteger(static_cast(value)); - } -#endif -} - -void -modm::IOStream::writeInteger(uint32_t value) -{ -#if defined(MODM_CPU_AVR) - char buffer[ArithmeticTraits::decimalDigits + 1]; // +1 for '\0' - - // Uses the optimized non standard function 'ultoa()' which is - // not always available. - this->device->write(ultoa(value, buffer, 10)); -#else - char buffer[ArithmeticTraits::decimalDigits + 1]; // +1 for '\0' - - // ptr points to the end of the string, it will be filled backwards - char *ptr = buffer + ArithmeticTraits::decimalDigits; - - *ptr = '\0'; - - // calculate the string backwards - do{ - uint32_t quot = value / 10; - uint8_t rem = value - quot*10; - *(--ptr) = static_cast(rem) + '0'; - value = quot; - }while (value != 0); - - // write string - this->device->write(ptr); -#endif -} - -#ifndef MODM_CPU_AVR -void -modm::IOStream::writeInteger(int64_t value) -{ - if (value < 0) { - this->device->write('-'); - this->writeInteger(static_cast(-value)); } - else{ - this->writeInteger(static_cast(value)); - } -} - -void -modm::IOStream::writeInteger(uint64_t value) -{ - char buffer[ArithmeticTraits::decimalDigits + 1]; // +1 for '\0' - - // ptr points to the end of the string, it will be filled backwards - char *ptr = buffer + ArithmeticTraits::decimalDigits; - - *ptr = '\0'; - - // calculate the string backwards - do{ - uint64_t quot = value / 10; - uint8_t rem = value - quot*10; - *(--ptr) = static_cast(rem) + '0'; - value = quot; - }while (value != 0); - - // write string - this->device->write(ptr); + s[ii] = '\0'; + return *this; } -#endif // ---------------------------------------------------------------------------- -void -modm::IOStream::writeHex(const char* s) -{ - while (*s != '\0') { - this->writeHex(*s); - s++; - } -} - -void -modm::IOStream::writeBin(const char* s) +IOStream& +IOStream::operator << (const bool& v) { - while (*s != '\0') { - this->writeBin(*s); - s++; + switch (mode) + { + case Mode::Ascii: + *this << (v ? IFSS("true") : IFSS("false")); + break; + case Mode::Hexadecimal: + device->write('0'); + // fallthrough + case Mode::Binary: + device->write(v ? '1' : '0'); + break; } + return *this; } // ---------------------------------------------------------------------------- void -modm::IOStream::writeHexNibble(uint8_t nibble) +IOStream::writeHex(uint8_t value) { - char character; - if (nibble > 9) { - character = nibble + 'A' - 10; - } - else { - character = nibble + '0'; - } - this->device->write(character); + const auto fn_nibble = [this](uint8_t nibble) + { + device->write( nibble + (nibble > 9 ? 'A' - 10 : '0') ); + }; + fn_nibble(value >> 4); + fn_nibble(value & 0xF); } // ---------------------------------------------------------------------------- void -modm::IOStream::writeHex(uint8_t value) -{ - writeHexNibble(value >> 4); - writeHexNibble(value & 0xF); -} - -void -modm::IOStream::writeBin(uint8_t value) +IOStream::writeBin(uint8_t value) { for (uint_fast8_t ii = 0; ii < 8; ii++) { - if (value & 0x80) { - this->device->write('1'); - } - else { - this->device->write('0'); - } + device->write(value & 0x80 ? '1' : '0'); value <<= 1; } - } // ---------------------------------------------------------------------------- -modm::IOStream& -modm::IOStream::operator << (const myfunc& value) +void +IOStream::writePointer(const void* p) { - unsigned char *p = (unsigned char *)&value; - - for (std::size_t i = 0; i < sizeof(myfunc); i++) - { - writeHex(p[sizeof(myfunc) - i - 1]); - } - return *this; -} + device->write('0'); + device->write('x'); + const uintptr_t value = reinterpret_cast(p); -modm::IOStream& -modm::IOStream::operator << (const void* p) -{ #if MODM_SIZEOF_POINTER == 2 - this->device->write('0'); - this->device->write('x'); - - uint16_t value = reinterpret_cast(p); - writeHex(value >> 8); writeHex(value); #elif MODM_SIZEOF_POINTER == 4 - this->device->write('0'); - this->device->write('x'); + for (uint8_t ii=24; ii < 32; ii -= 8) + writeHex(value >> ii); - uint32_t value = reinterpret_cast(p); +#elif MODM_SIZEOF_POINTER == 8 - writeHex(value >> 24); - writeHex(value >> 16); - writeHex(value >> 8); - writeHex(value); + for (uint8_t ii=56; ii < 64; ii -= 8) + writeHex(value >> ii); -#elif MODM_SIZEOF_POINTER == 8 +#endif +} - this->device->write('0'); - this->device->write('x'); +IOStream& +black(IOStream& ios) +{ return ios << IFSS("\033[30m"); } - uint64_t value = reinterpret_cast(p); +IOStream& +red(IOStream& ios) +{ return ios << IFSS("\033[31m"); } - writeHex(value >> 56); - writeHex(value >> 48); - writeHex(value >> 40); - writeHex(value >> 32); - writeHex(value >> 24); - writeHex(value >> 16); - writeHex(value >> 8); - writeHex(value); +IOStream& +green(IOStream& ios) +{ return ios << IFSS("\033[32m"); } -#endif - return *this; -} +IOStream& +yellow(IOStream& ios) +{ return ios << IFSS("\033[33m"); } + +IOStream& +blue(IOStream& ios) +{ return ios << IFSS("\033[34m"); } + +IOStream& +magenta(IOStream& ios) +{ return ios << IFSS("\033[35m"); } + +IOStream& +cyan(IOStream& ios) +{ return ios << IFSS("\033[36m"); } + +IOStream& +white(IOStream& ios) +{ return ios << IFSS("\033[37m"); } + +} // namespace modm diff --git a/src/modm/io/iostream.hpp b/src/modm/io/iostream.hpp deleted file mode 100644 index 582bfe47e5..0000000000 --- a/src/modm/io/iostream.hpp +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (c) 2009-2010, Martin Rosekeit - * Copyright (c) 2009-2012, Fabian Greif - * Copyright (c) 2011, Georgi Grinshpun - * Copyright (c) 2011-2017, Niklas Hauser - * Copyright (c) 2012, 2015-2016, Sascha Schade - * Copyright (c) 2015-2016, Kevin Läufer - * Copyright (c) 2017, Marten Junga - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_IOSTREAM_HPP -#define MODM_IOSTREAM_HPP - -#include - -#include -#include - -#include // va_list - -#include "iodevice.hpp" -#include "iodevice_wrapper.hpp" - -namespace modm -{ - -/** - * This formats all primary types into a string stream for - * output or it reads values from a input and converts them to - * a given type; - * - * @ingroup modm_io - * @author Martin Rosekeit - */ -class IOStream -{ -public: - /** - * @param device device to write the stream to - * - * @code - * MyIODevice device; - * IOStream stream( device ); - * @endcode - */ - IOStream(IODevice& device); - - inline IOStream& - write(char c) - { - this->device->write(c); - return *this; - } - - static constexpr char eof = -1; - - /// Reads one character and returns it if available. Otherwise, returns IOStream::eof. - inline IOStream& - get(char& c) - { - if(!this->device->read(c)) { - c = IOStream::eof; - } - return *this; - } - - /// reads characters into NULL delimited c string - /// in contrast to the standard implementation, this does not care about - /// newline characters in the input - inline IOStream& - get(char* s, size_t n) - { - if(n < 1) { - return *this; - } - char cc; - size_t ii; - for(ii = 0; ii < (n-1); ++ii) { - if(this->device->read(cc)) { - s[ii] = cc; - } else { - break; - } - } - s[ii] = '\0'; - return *this; - } - - template - inline IOStream& - get(char (&s)[N]) - { - return this->get(s, N); - } - - - - inline IOStream& - flush() - { - this->device->flush(); - this->mode = Mode::Ascii; - return *this; - } - - /// set the output mode to binary style for `char` and `char*` - modm_always_inline IOStream& - bin() - { - this->mode = Mode::Binary; - return *this; - } - - /// set the output mode to hexadecimal style for `char` and `char*` - modm_always_inline IOStream& - hex() - { - this->mode = Mode::Hexadecimal; - return *this; - } - - /// set the output mode to ASCII style for `char` and `char*` - modm_always_inline IOStream& - ascii() - { - this->mode = Mode::Ascii; - return *this; - } - - - IOStream& - operator << (const unsigned char& v) - { - if (this->mode == Mode::Ascii) { - this->writeInteger(static_cast(v)); - } - else if (this->mode == Mode::Binary) { - this->writeBin(v); - } - else { - this->writeHex(v); - } - return *this; - } - - IOStream& - operator << (const bool& v) - { - switch (this->mode) - { - case Mode::Ascii: - this->device->write(v ? "true" : "false"); - break; - case Mode::Binary: - // upper nibble - this->device->write('0'); - this->device->write('0'); - this->device->write('0'); - this->device->write('0'); - - // lower nibble - this->device->write('0'); - this->device->write('0'); - // fallthrough - case Mode::Hexadecimal: - this->device->write('0'); - this->device->write(v ? '1' : '0'); - break; - } - return *this; - } - - IOStream& - operator << (const char& v) - { - if (this->mode == Mode::Ascii) { - this->device->write(v); - } - else if (this->mode == Mode::Binary) { - this->writeBin(v); - } - else { - this->writeHex(v); - } - return *this; - } - - modm_always_inline IOStream& - operator << (const uint16_t& v) - { - if (this->mode == Mode::Ascii) { - this->writeInteger(v); - } - else if (this->mode == Mode::Binary) { - this->writeBin(static_cast(v >> 8)); - this->writeBin(static_cast(v & 0xff)); - } - else { - this->writeHex(static_cast(v >> 8)); - this->writeHex(static_cast(v)); - } - return *this; - } - - modm_always_inline IOStream& - operator << (const int16_t& v) - { - if (this->mode == Mode::Ascii) { - this->writeInteger(v); - } - else if (this->mode == Mode::Binary) { - this->writeBin(static_cast(v >> 8)); - this->writeBin(static_cast(v)); - } - else { - this->writeHex(static_cast(v >> 8)); - this->writeHex(static_cast(v)); - } - return *this; - } - - modm_always_inline IOStream& - operator << (const uint32_t& v) - { - if (this->mode == Mode::Ascii) { - this->writeInteger(v); - } - else if (this->mode == Mode::Binary) { - this->writeBin(static_cast(v >> 24)); - this->writeBin(static_cast(v >> 16)); - this->writeBin(static_cast(v >> 8)); - this->writeBin(static_cast(v)); - } - else { - this->writeHex(static_cast(v >> 24)); - this->writeHex(static_cast(v >> 16)); - this->writeHex(static_cast(v >> 8)); - this->writeHex(static_cast(v)); - } - return *this; - } - - modm_always_inline IOStream& - operator << (const int32_t& v) - { - if (this->mode == Mode::Ascii) { - this->writeInteger(v); - } - else if (this->mode == Mode::Binary) { - this->writeBin(static_cast(v >> 24)); - this->writeBin(static_cast(v >> 16)); - this->writeBin(static_cast(v >> 8)); - this->writeBin(static_cast(v)); - } - else { - this->writeHex(static_cast(v >> 24)); - this->writeHex(static_cast(v >> 16)); - this->writeHex(static_cast(v >> 8)); - this->writeHex(static_cast(v)); - } - return *this; - } - -#if defined(MODM_OS_OSX) || defined(MODM_CPU_I386) - // For OSX 'int64_t' is of type 'int'. Therefore there is no - // function here for the default type 'long int'. As 'long int' has the same - // width as 'int64_t' we just use a typedef here. - modm_always_inline IOStream& - operator << (const long int& v) - { - this->writeInteger(static_cast(v)); - return *this; - } - - modm_always_inline IOStream& - operator << (const long unsigned int& v) - { - this->writeInteger(static_cast(v)); - return *this; - } -#endif - -#if defined(MODM_CPU_ARM) && defined(MODM_OS_NONE) - // For ARM 'int32_t' is of type 'long'. Therefore there is no - // function here for the default type 'int'. As 'int' has the same - // width as 'int32_t' we just use a typedef here. - modm_always_inline IOStream& - operator << (const int& v) - { - this->writeInteger(static_cast(v)); - return *this; - } - - modm_always_inline IOStream& - operator << (const unsigned int& v) - { - this->writeInteger(static_cast(v)); - return *this; - } -#endif - -// The 64-bit types on the AVR are extremely slow and are -// therefore excluded here -#if not defined(MODM_CPU_AVR) - modm_always_inline IOStream& - operator << (const uint64_t& v) - { - this->writeInteger(v); - return *this; - } - - modm_always_inline IOStream& - operator << (const int64_t& v) - { - this->writeInteger(v); - return *this; - } -#endif - - modm_always_inline IOStream& - operator << (const float& v) - { - this->writeFloat(v); - return *this; - } - - modm_always_inline IOStream& - operator << (const double& v) - { -#if defined(MODM_CPU_AVR) - this->writeFloat(static_cast(v)); -#else - this->writeDouble(v); -#endif - return *this; - } - - IOStream& - operator << (const char* s) - { - if( this->mode == Mode::Ascii ) { - this->device->write(s); - } - else if( this->mode == Mode::Binary ) { - this->writeBin(s); - } - else { - this->writeHex(s); - } - return *this; - } - - /// write the hex value of a pointer - IOStream& - operator << (const void* p); - - modm_always_inline IOStream& - operator << (IOStream& (*function)(IOStream&)) - { - return function(*this); - } - - typedef void (*myfunc)(); - - IOStream& - operator << (const myfunc& value); - - /// Write the hex value of a function pointer, catches all kinds of function pointers. - template - IOStream& - operator << (Ret(*pointer)(Args...) ) - { - unsigned char *p = (unsigned char *)&pointer; - - for (std::size_t i = 0; i < sizeof(myfunc); i++) - { - writeHex(p[sizeof(myfunc) - i - 1]); - } - - return *this; - } - - /** - * @param fmt Format string - */ - IOStream& - printf(const char* fmt, ...) __attribute__((format(printf, 2, 3))); - - IOStream& - vprintf(const char *fmt, va_list vlist) __attribute__((format(printf, 2, 0))); - -protected: - void - writeInteger(int16_t value); - - void - writeInteger(uint16_t value); - - void - writeInteger(int32_t value); - - void - writeInteger(uint32_t value); - - void - writeInteger(int64_t value); - - void - writeInteger(uint64_t value); - - void - writeHex(const char* s); - - void - writeBin(const char* s); - - void - writeHexNibble(uint8_t nibble); - - void - writeHex(uint8_t value); - - void - writeBin(uint8_t value); - - void - writeFloat(const float& value); - -#if not defined(MODM_CPU_AVR) - void - writeDouble(const double& value); -#endif - - void - writeUnsignedInteger(unsigned long unsignedValue, uint_fast8_t base, size_t width, char fill, bool isNegative); -#if not defined(MODM_CPU_AVR) - void - writeUnsignedLongLong(unsigned long long unsignedValue, uint_fast8_t base, size_t width, char fill, bool isNegative); -#endif - - -private: - enum class - Mode - { - Ascii, - Hexadecimal, - Binary - }; - - IOStream(const IOStream&); - - IOStream& - operator =(const IOStream&); - -private: - IODevice* const device; - Mode mode; -}; - -/// @ingroup modm_io -/// @{ - -/// Flushes the output stream. -/// This manipulator simply calls the stream's flush() member function. -inline IOStream& -flush(IOStream& ios) -{ - return ios.flush(); -} - -//// Write a newline and flush the stream. -inline IOStream& -endl(IOStream& ios) -{ - return flush(ios.write('\n')); -} - -/// set the output mode to binary style for `char` and `char*` -inline IOStream& -bin(IOStream& ios) -{ - return ios.bin(); -} - -/// set the output mode to hexadecimal style for `char` and `char*` -inline IOStream& -hex(IOStream& ios) -{ - return ios.hex(); -} - -/// set the output mode to ASCII style for `char` and `char*` -inline IOStream& -ascii(IOStream& ios) -{ - return ios.ascii(); -} - -/// Set the foreground colour on ANSI terminals. -inline IOStream& -black(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('0'); - ios.write('m'); - return ios; -} - -inline IOStream& -red(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('1'); - ios.write('m'); - return ios; -} - -inline IOStream& -green(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('2'); - ios.write('m'); - return ios; -} - -inline IOStream& -yellow(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('3'); - ios.write('m'); - return ios; -} - -inline IOStream& -blue(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('4'); - ios.write('m'); - return ios; -} - -inline IOStream& -magenta(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('1'); - ios.write('m'); - return ios; -} - -inline IOStream& -cyan(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('6'); - ios.write('m'); - return ios; -} - -inline IOStream& -white(IOStream& ios) -{ - ios.write('\033'); - ios.write('['); - ios.write('3'); - ios.write('7'); - ios.write('m'); - return ios; -} -/// @} - -} // namespace modm - -#endif // MODM_IOSTREAM_HPP diff --git a/src/modm/io/iostream.hpp.in b/src/modm/io/iostream.hpp.in new file mode 100644 index 0000000000..0c0c7d54dd --- /dev/null +++ b/src/modm/io/iostream.hpp.in @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2009-2010, Martin Rosekeit + * Copyright (c) 2009-2012, Fabian Greif + * Copyright (c) 2011, Georgi Grinshpun + * Copyright (c) 2011-2017, 2019 Niklas Hauser + * Copyright (c) 2012, 2015-2016, Sascha Schade + * Copyright (c) 2015-2016, Kevin Läufer + * Copyright (c) 2017, Marten Junga + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_IOSTREAM_HPP +#define MODM_IOSTREAM_HPP + +#include +#include + +#include // va_list +#include +#include + +#include "iodevice.hpp" +#include "iodevice_wrapper.hpp" // convenience + +namespace modm +{ + +/** + * @ingroup modm_io + * @author Martin Rosekeit + * @author Niklas Hauser + */ +class IOStream +{ +public: + /** + * @param device device to write the stream to + * + * @code + * MyIODevice device; + * IOStream stream( device ); + * @endcode + */ + inline IOStream(IODevice& odevice) : + device(&odevice) + {} + + // Acccessors ------------------------------------------------------------- + inline IOStream& + write(char c) + { device->write(c); return *this; } + + static constexpr char eof = -1; + + /// Reads one character and returns it if available. Otherwise, returns IOStream::eof. + inline IOStream& + get(char& c) + { + if(!device->read(c)) { + c = IOStream::eof; + } + return *this; + } + + /// reads characters into NULL delimited c string + /// in contrast to the standard implementation, this does not care about + /// newline characters in the input + inline IOStream& + get(char* s, size_t n); + + template + inline IOStream& + get(char (&s)[N]) + { return get(s, N); } + + // Modes ------------------------------------------------------------------ + inline IOStream& + flush() + { + device->flush(); + mode = Mode::Ascii; + return *this; + } + + /// set the output mode to binary style for integer types + inline IOStream& + bin() + { mode = Mode::Binary; return *this; } + + /// set the output mode to hexadecimal style for integer types + inline IOStream& + hex() + { mode = Mode::Hexadecimal; return *this; } + + /// set the output mode to ASCII style for integer types + inline IOStream& + ascii() + { mode = Mode::Ascii; return *this; } + + // Types ------------------------------------------------------------------ + IOStream& + operator << (const bool& v); + + // char is equal to int8_t! + inline IOStream& + operator << (const char& v) + { + if (mode == Mode::Ascii) + device->write(v); + else if (mode == Mode::Binary) + writeBin(static_cast(v)); + else + writeHex(static_cast(v)); + return *this; + } + inline IOStream& + operator << (const uint8_t& v) + { + if (mode == Mode::Ascii) + writeInteger(static_cast(v)); + else if (mode == Mode::Binary) + writeBin(v); + else + writeHex(v); + return *this; + } + + inline IOStream& operator << (const int16_t& v) + { writeIntegerMode(v); return *this; } + inline IOStream& operator << (const uint16_t& v) + { writeIntegerMode(v); return *this; } + + inline IOStream& operator << (const int32_t& v) + { writeIntegerMode(v); return *this; } + inline IOStream& operator << (const uint32_t& v) + { writeIntegerMode(v); return *this; } + +%% if options["with_long_long"] + inline IOStream& operator << (const int64_t& v) + { writeIntegerMode(v); return *this; } + inline IOStream& operator << (const uint64_t& v) + { writeIntegerMode(v); return *this; } + +%% if family in ["darwin"] + // For OSX 'int64_t' is of type 'int'. Therefore there is no + // function here for the default type 'long int'. As 'long int' has the same + // width as 'int64_t' we just use a typedef here. + inline IOStream& operator << (const long int& v) + { writeIntegerMode(static_cast(v)); return *this; } + inline IOStream& operator << (const long unsigned int& v) + { writeIntegerMode(static_cast(v)); return *this; } +%% endif +%% endif + +%% if core.startswith("cortex-m") + // For ARM 'int32_t' is of type 'long'. Therefore there is no + // function here for the default type 'int'. As 'int' has the same + // width as 'int32_t' we just use a typedef here. + inline IOStream& operator << (const int& v) + { writeIntegerMode(static_cast(v)); return *this; } + inline IOStream& operator << (const unsigned int& v) + { writeIntegerMode(static_cast(v)); return *this; } +%% endif + +%% if options["with_float"] + inline IOStream& + operator << (const float& v) + { writeFloat(v); return *this; } + + inline IOStream& + operator << (const double& v) + { writeDouble(v); return *this; } +%% endif + + inline IOStream& + operator << (const char* s) + { device->write(s); return *this; } + + /// write the hex value of a pointer + inline IOStream& + operator << (const void* p) + { writePointer(p); return *this; } + + /// Write the hex value of any function pointer + template + IOStream& + operator << (Ret(*pointer)(Args...)) + { writePointer(reinterpret_cast(&pointer)); return *this; } + + inline IOStream& + operator << (IOStream& (*format)(IOStream&)) + { return format(*this); } + +%% if options["with_printf"] + // printf ----------------------------------------------------------------- + IOStream& + printf(const char* fmt, ...) __attribute__((format(printf, 2, 3))); + + IOStream& + vprintf(const char *fmt, va_list vlist) __attribute__((format(printf, 2, 0))); +%% endif + + +protected: + template< typename T > + void + writeIntegerMode(const T v) + { + constexpr size_t t_bits = sizeof(T)*8; + if (mode == Mode::Ascii) { + writeInteger(v); + } else if (mode == Mode::Binary) { + for (uint8_t ii=t_bits-8; ii < t_bits; ii -= 8) + writeBin(static_cast>(v) >> ii); + } else { + for (uint8_t ii=t_bits-8; ii < t_bits; ii -= 8) + writeHex(static_cast>(v) >> ii); + } + } + + void writeInteger(int16_t value); + void writeInteger(uint16_t value); + void writeInteger(int32_t value); + void writeInteger(uint32_t value); +%% if options["with_long_long"] + void writeInteger(int64_t value); + void writeInteger(uint64_t value); +%% endif + +%% if options["with_float"] + inline void writeFloat(float value) + { writeDouble(static_cast(value)); } + void writeDouble(const double& value); +%% endif + + void writePointer(const void* value); + void writeHex(uint8_t value); + void writeBin(uint8_t value); + +private: + enum class + Mode + { + Ascii, + Hexadecimal, + Binary + }; + + IOStream(const IOStream&) = delete; + IOStream& operator =(const IOStream&) = delete; + +private: + IODevice* const device; + Mode mode = Mode::Ascii; +}; + +/// @ingroup modm_io +/// @{ + +/// Flushes the output stream. +/// This manipulator simply calls the stream's flush() member function. +inline IOStream& +flush(IOStream& ios) +{ return ios.flush(); } + +//// Write a newline and flush the stream. +inline IOStream& +endl(IOStream& ios) +{ return flush(ios.write('\n')); } + +/// set the output mode to binary style +inline IOStream& +bin(IOStream& ios) +{ return ios.bin(); } + +/// set the output mode to hexadecimal style +inline IOStream& +hex(IOStream& ios) +{ return ios.hex(); } + +/// set the output mode to ASCII style +inline IOStream& +ascii(IOStream& ios) +{ return ios.ascii(); } + +/// Set the foreground colour on ANSI terminals. +IOStream& +black(IOStream& ios); + +IOStream& +red(IOStream& ios); + +IOStream& +green(IOStream& ios); + +IOStream& +yellow(IOStream& ios); + +IOStream& +blue(IOStream& ios); + +IOStream& +magenta(IOStream& ios); + +IOStream& +cyan(IOStream& ios); + +IOStream& +white(IOStream& ios); +/// @} + +} // namespace modm + +#endif // MODM_IOSTREAM_HPP diff --git a/src/modm/io/iostream_float.cpp b/src/modm/io/iostream_float.cpp deleted file mode 100644 index 40e5efa4af..0000000000 --- a/src/modm/io/iostream_float.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2009-2010, Martin Rosekeit - * Copyright (c) 2009-2012, Fabian Greif - * Copyright (c) 2012, Georgi Grinshpun - * Copyright (c) 2012, 2014, 2017, Niklas Hauser - * Copyright (c) 2013, Kevin Läufer - * Copyright (c) 2016, Tarik TIRE - * Copyright (c) 2018, Raphael Lehmann - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include // snprintf() -#include -#include - -#include - -#include "iostream.hpp" - -void -modm::IOStream::writeFloat(const float& value) -{ - // hard coded for -2.22507e-308 - char str[13 + 1]; // +1 for '\0' - - if(!std::isfinite(value)) { - char *ptr = &str[0]; - if(std::isinf(value)) { - if (value < 0) { - *ptr++ = '-'; - } - *ptr++ = 'i'; - *ptr++ = 'n'; - *ptr++ = 'f'; - *ptr++ = '\0'; // End of string - this->device->write(str); - return; - } - else { - *ptr++ = 'n'; - *ptr++ = 'a'; - *ptr++ = 'n'; - *ptr++ = '\0'; // End of string - this->device->write(str); - return; - } - } - -#if defined(MODM_CPU_AVR) - dtostre(value, str, 5, 0); - this->device->write(str); -#elif defined(MODM_CPU_CORTEX_M4) || defined(MODM_CPU_CORTEX_M3) || defined(MODM_CPU_CORTEX_M0) || defined(MODM_OS_WIN32) - float v; - char *ptr = &str[0]; - - if (value < 0) { - v = -value; - *ptr++ = '-'; - } - else { - v = value; - } - - int32_t ep = 0; - if (v != 0) - { - while (v < 1.f) { - v *= 10; - ep -= 1; - } - - while (v > 10) { - v *= 0.1f; - ep += 1; - } - } - - for (uint32_t i = 0; i < 6; ++i) - { - int8_t num = static_cast(v); - *ptr++ = (num + '0'); - - if (i == 0) { - *ptr++ = '.'; - } - - // next digit - v = (v - num) * 10; - } - - *ptr++ = 'e'; - if (ep < 0) { - ep = -ep; - *ptr++ = '-'; - } - else { - *ptr++ = '+'; - } - if (ep < 10) { - *ptr++ = '0'; - } - *ptr++ = '\0'; // End of string - this->device->write(str); - - this->writeInteger(ep); -#else - snprintf(str, sizeof(str), "%.5e", (double) value); - this->device->write(str); -#endif -} - -// ---------------------------------------------------------------------------- -#if !defined(MODM_CPU_AVR) -void -modm::IOStream::writeDouble(const double& value) -{ -#if defined(MODM_CPU_CORTEX_M4) || defined(MODM_CPU_CORTEX_M3) || defined(MODM_CPU_CORTEX_M0) || defined(MODM_OS_WIN32) - // TODO do this better - writeFloat(static_cast(value)); -#else - // hard coded for 2.22507e-308 - char str[13 + 1]; // +1 for '\0' - snprintf(str, sizeof(str), "%.5e", value); - this->device->write(str); -#endif -} -#endif diff --git a/src/modm/io/iostream_printf.cpp b/src/modm/io/iostream_printf.cpp deleted file mode 100644 index 5b8b2350a2..0000000000 --- a/src/modm/io/iostream_printf.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (c) 2011-2012, 2017, Fabian Greif - * Copyright (c) 2012, 2014, 2017, Niklas Hauser - * Copyright (c) 2014, 2016, Sascha Schade - * Copyright (c) 2016, Kevin Läufer - * Copyright (c) 2017, Marten Junga - * Copyright (c) 2017-2018, Raphael Lehmann - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include // snprintf() -#include -#include // modm::pow - -#include "iostream.hpp" - -modm::IOStream& -modm::IOStream::printf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - - this->vprintf(fmt, ap); - - va_end(ap); - return *this; -} - -modm::IOStream& -modm::IOStream::vprintf(const char *fmt, va_list ap) -{ - unsigned char c; - - // for all chars in format (fmt) - while ((c = *fmt++) != 0) - { - bool isSigned = false; - bool isLong = false; - bool isLongLong = false; - bool isFloat = false; - bool isNegative = false; - - if (c != '%') - { - this->device->write(c); - continue; - } - c = *fmt++; - - size_t width = 0; - size_t width_frac = 0; - char fill = ' '; - if (c == '0') - { - fill = c; - c = *fmt++; - } - if (c >= '0' && c <= '9') - { - width = c - '0'; - c = *fmt++; - } - - if (c == '.') { - c = *fmt++; - - if (c >= '0' && c <= '9') { - width_frac = c - '0'; - } - c = *fmt++; - } - - if (c == 'l') - { - isLong = true; - c = *fmt++; - } - if (c == 'l') - { - isLongLong = true; - c = *fmt++; - } - - uint_fast8_t base = 10; - char *ptr; - switch (c) - { - case 'c': - c = va_arg(ap, int); // char promoted to int - MODM_FALLTHROUGH; - default: - this->device->write(c); - continue; - - case 's': - ptr = (char *) va_arg(ap, char *); - while ((c = *ptr++)) - { - this->device->write(c); - } - continue; - - case 'f': - isFloat = true; - break; - - case 'd': - isSigned = true; - MODM_FALLTHROUGH; - - case 'u': - base = 10; - break; - - case 'p': - this->device->write('0'); - this->device->write('x'); - fill = '0'; - width = (MODM_SIZEOF_POINTER * 2); - isLong = (MODM_SIZEOF_POINTER == 4); - isLongLong = (MODM_SIZEOF_POINTER == 8); - MODM_FALLTHROUGH; - case 'x': - base = 16; - break; - - case 'b': - base = 2; - break; - } - - - // Number output - if (isFloat) - { - // va_arg(ap, float) not allowed - float float_value = va_arg(ap, double); - - if(!std::isfinite(float_value)) { - if(std::isinf(float_value)) { - if (float_value < 0) { - this->device->write('-'); - } - this->device->write('i'); - this->device->write('n'); - this->device->write('f'); - return *this; - } - else { - this->device->write('n'); - this->device->write('a'); - this->device->write('n'); - return *this; - } - } - - if (float_value < 0) - { - float_value = -float_value; // make it positive - isNegative = true; - } - - // Rounding - float_value += 0.5 / modm::pow(10, width_frac); - - // 1) Print integer part - int width_integer = width - width_frac - 1; - if (width_integer < 0) { - width_integer = 0; - } - - writeUnsignedInteger((unsigned int)float_value, base, width_integer, fill, isNegative); - - // 2) Decimal dot - this->device->write('.'); - - // 3) Fractional part - float_value = float_value - ((int) float_value); - float_value *= modm::pow(10, width_frac); - - // Alternative: Smaller code size but probably less precise - // for (uint_fast8_t ii = 0; ii < width_frac; ++ii) { - // float_value = float_value * (10.0); - // } - - // Print fractional part - writeUnsignedInteger((unsigned int)float_value, base, width_frac, '0', false); - } -#if not defined(MODM_CPU_AVR) - else if (isLongLong) - { - long long signedValue = va_arg(ap, long long); - if (isSigned) - { - if (signedValue < 0) - { - isNegative = true; - signedValue = -signedValue; // make it positive - } - } - writeUnsignedLongLong((unsigned long long) signedValue, base, width, fill, isNegative); - } -#endif - else - { - unsigned long unsignedValue; - - { - long signedValue = 0; - - if (isLongLong) { - signedValue = va_arg(ap, long long); - } - else if (isLong) { - signedValue = va_arg(ap, long); - } - else { - signedValue = va_arg(ap, int); - } - - if (isSigned) - { - if (signedValue < 0) - { - isNegative = true; - signedValue = -signedValue; // make it positive - } - } - unsignedValue = (unsigned long) signedValue; - } - - writeUnsignedInteger(unsignedValue, base, width, fill, isNegative); - } - } - - return *this; -} - -void -modm::IOStream::writeUnsignedInteger( - unsigned long unsignedValue, uint_fast8_t base, - size_t width, char fill, bool isNegative) -{ - char scratch[26]; - - char *ptr = scratch + sizeof(scratch); - *--ptr = 0; - do - { - char ch = (unsignedValue % base) + '0'; - - if (ch > '9') { - ch += 'A' - '9' - 1; - } - - *--ptr = ch; - unsignedValue /= base; - - if (width) { - --width; - } - } while (unsignedValue); - - // Insert minus sign if needed - if (isNegative) - { - *--ptr = '-'; - if (width) { - --width; - } - } - - // insert padding chars - while (width--) { - *--ptr = fill; - } - - // output result - char ch; - while ((ch = *ptr++)) { - this->device->write(ch); - } -} - -#if not defined(MODM_CPU_AVR) -void -modm::IOStream::writeUnsignedLongLong( - unsigned long long unsignedValue, uint_fast8_t base, - size_t width, char fill, bool isNegative) -{ - char scratch[26]; - - char *ptr = scratch + sizeof(scratch); - *--ptr = 0; - do - { - char ch = (unsignedValue % base) + '0'; - - if (ch > '9') { - ch += 'A' - '9' - 1; - } - - *--ptr = ch; - unsignedValue /= base; - - if (width) { - --width; - } - } while (unsignedValue); - - // Insert minus sign if needed - if (isNegative) - { - *--ptr = '-'; - if (width) { - --width; - } - } - - // insert padding chars - while (width--) { - *--ptr = fill; - } - - // output result - char ch; - while ((ch = *ptr++)) { - this->device->write(ch); - } -} -#endif diff --git a/src/modm/io/iostream_printf.cpp.in b/src/modm/io/iostream_printf.cpp.in new file mode 100644 index 0000000000..74e0737a1c --- /dev/null +++ b/src/modm/io/iostream_printf.cpp.in @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2019, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "iostream.hpp" + +extern "C" +{ + // configure printf implementation +%% if not options["with_long_long"] + #define PRINTF_DISABLE_SUPPORT_LONG_LONG +%% endif +%% if not options["with_float"] + #define PRINTF_DISABLE_SUPPORT_FLOAT +%% endif +%% if not options["with_ptrdiff"] + #define PRINTF_DISABLE_SUPPORT_PTRDIFF_T +%% endif + #define PRINTF_DEFAULT_FLOAT_PRECISION 5U + + // include source from modm/ext/printf + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdouble-promotion" + #include "printf/printf.source" + #pragma GCC diagnostic pop + void _putchar(char) {}; + +%% if core.startswith("avr") and options["with_float"] + // the double arithmetric in these functions does not work on AVRs + static size_t _etoa(out_fct_type, [[maybe_unused]] char* buffer, size_t idx, size_t, + [[maybe_unused]] double value, unsigned int, unsigned int, unsigned int) + { + *reinterpret_cast(buffer) << value; + return idx; + } + static size_t _ftoa(out_fct_type, [[maybe_unused]] char* buffer, size_t idx, size_t, + [[maybe_unused]] double value, unsigned int, unsigned int, unsigned int) + { + *reinterpret_cast(buffer) << value; + return idx; + } +%% endif +} + +namespace +{ +void out_char(char character, void* buffer, size_t, size_t) +{ + if (character) + reinterpret_cast(buffer)->write(character); +} +} + +namespace modm +{ + +%% if options["with_printf"] +IOStream& +IOStream::printf(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + vprintf(fmt, va); + va_end(va); + return *this; +} + +IOStream& +IOStream::vprintf(const char *fmt, va_list ap) +{ + _vsnprintf(out_char, (char*)this, -1, fmt, ap); + return *this; +} +%% endif + +void +IOStream::writeInteger(int16_t value) +{ +%% if core.startswith("avr") and not options["with_printf"] + // hard coded for -32768 + char str[7 + 1]; // +1 for '\0' + itoa(value, str, 10); + device->write(str); +%% else + _ntoa_long(out_char, (char*)this, + 0, -1, + uint16_t(value < 0 ? -value : value), + value < 0, + 10, 0, 0, + FLAGS_SHORT); +%% endif +} + +void +IOStream::writeInteger(uint16_t value) +{ +%% if core.startswith("avr") and not options["with_printf"] + // hard coded for 32768 + char str[6 + 1]; // +1 for '\0' + utoa(value, str, 10); + device->write(str); +%% else + _ntoa_long(out_char, (char*)this, + 0, -1, + value, + false, + 10, 0, 0, + FLAGS_SHORT); +%% endif +} + +void +IOStream::writeInteger(int32_t value) +{ +%% if core.startswith("avr") and not options["with_printf"] + // hard coded for -2147483648 + char str[11 + 1]; // +1 for '\0' + ltoa(value, str, 10); + device->write(str); +%% else + _ntoa_long(out_char, (char*)this, + 0, -1, + uint32_t(value < 0 ? -value : value), + value < 0, + 10, 0, 0, + FLAGS_LONG); +%% endif +} + +void +IOStream::writeInteger(uint32_t value) +{ +%% if core.startswith("avr") and not options["with_printf"] + // hard coded for 4294967295 + char str[10 + 1]; // +1 for '\0' + ultoa(value, str, 10); + device->write(str); +%% else + _ntoa_long(out_char, (char*)this, + 0, -1, + value, + false, + 10, 0, 0, + FLAGS_LONG); +%% endif +} + +%% if options["with_long_long"] +void +IOStream::writeInteger(int64_t value) +{ + _ntoa_long_long(out_char, (char*)this, + 0, -1, + uint64_t(value < 0 ? -value : value), + value < 0, + 10, 0, 0, + FLAGS_LONG_LONG); +} + +void +IOStream::writeInteger(uint64_t value) +{ + _ntoa_long_long(out_char, (char*)this, + 0, -1, + value, + false, + 10, 0, 0, + FLAGS_LONG_LONG); +} +%% endif + +%% if options["with_float"] +void +IOStream::writeDouble(const double& value) +{ +%% if core.startswith("avr") + if(!std::isfinite(value)) { + if(std::isinf(value)) { + if (value < 0) device->write('-'); + *this << IFSS("inf"); + } + else { + *this << IFSS("nan"); + } + } + else { + // hard coded for -2.22507e-308 + char str[13 + 1]; // +1 for '\0' + dtostre(value, str, 5, 0); + device->write(str); + } +%% else + _etoa(out_char, (char*)this, + 0, -1, + value, + 0, 0, 0); +%% endif +} +%% endif + +} // namespace modm diff --git a/src/modm/io/module.lb b/src/modm/io/module.lb index e8e794b22c..7df616b9a1 100644 --- a/src/modm/io/module.lb +++ b/src/modm/io/module.lb @@ -19,11 +19,44 @@ def prepare(module, options): module.depends( ":architecture:accessor", ":math:utils") + + is_avr = options[":target"].identifier.platform in ["avr"] + module.add_option( + BooleanOption(name="with_long_long", + description="Support for 64-bit integer formatting", + default=not is_avr)) + module.add_option( + BooleanOption(name="with_float", + description="Support for floating point formatting", + default=not is_avr)) + module.add_option( + BooleanOption(name="with_ptrdiff", + description="Support for pointer difference formatting", + default=not is_avr)) + module.add_option( + BooleanOption(name="with_printf", + description="Support for printf-style formatting", + default=not is_avr)) + return True def build(env): env.outbasepath = "modm/src/modm/io" - env.copy(".", ignore=env.ignore_files("io.hpp")) + env.copy(".", ignore=env.ignore_files("io.hpp", "iostream_printf.cpp.in", "iostream.hpp.in")) + env.substitutions = { + "family": env[":target"].identifier.family, + "core": env[":target"].get_driver("core")["type"], + } + env.template("iostream_printf.cpp.in") + env.template("iostream.hpp.in") + env.outbasepath = "modm/src/modm" env.copy("io.hpp") + env.outbasepath = "modm/ext/printf" + # Copy custom printf header + env.copy(repopath("ext/mpaland/printf.h"), "printf.h") + # Copy source file but without .c suffix so it doesn't get indexed for compilation. + env.copy(repopath("ext/mpaland/printf/printf.c"), "printf.source") + env.collect(":build:path.include", "modm/ext") + diff --git a/src/modm/platform/core/avr/flash_reader.hpp b/src/modm/platform/core/avr/flash_reader.hpp index 9a06c8ca33..ae2658ea1d 100644 --- a/src/modm/platform/core/avr/flash_reader.hpp +++ b/src/modm/platform/core/avr/flash_reader.hpp @@ -24,7 +24,7 @@ #define FLASH_STORAGE_STRING(s) extern const char s[] PROGMEM; const char s[] #define EXTERN_FLASH_STORAGE_STRING(s) extern const char s[] PROGMEM -#define INLINE_FLASH_STORAGE_STRING(s) PSTR(s) +#define IFSS(s) (modm::accessor::Flash(PSTR(s))) namespace modm { diff --git a/src/modm/platform/core/cortex/flash_reader.hpp b/src/modm/platform/core/cortex/flash_reader.hpp index 968c777316..08a89e1575 100644 --- a/src/modm/platform/core/cortex/flash_reader.hpp +++ b/src/modm/platform/core/cortex/flash_reader.hpp @@ -29,8 +29,7 @@ #define FLASH_STORAGE_STRING(s) extern const char s[]; const char s[] #define EXTERN_FLASH_STORAGE_STRING(s) extern const char s[] -#define INLINE_FLASH_STORAGE_STRING(s) ((const char *)(s)) -#define PSTR(s) ((const char *)(s)) +#define IFSS(s) ((const char *)(s)) namespace modm { diff --git a/test/config/al-avreb-can.xml b/test/config/al-avreb-can.xml index 479c6c58f9..0e78cbe0f6 100644 --- a/test/config/al-avreb-can.xml +++ b/test/config/al-avreb-can.xml @@ -5,6 +5,9 @@ + + + diff --git a/test/config/arduino-nano.xml b/test/config/arduino-nano.xml index 94f6a65114..0a614b2c6a 100644 --- a/test/config/arduino-nano.xml +++ b/test/config/arduino-nano.xml @@ -4,6 +4,9 @@ + + + diff --git a/test/config/arduino-uno.xml b/test/config/arduino-uno.xml index 0f17db7fef..6bd5c8186c 100644 --- a/test/config/arduino-uno.xml +++ b/test/config/arduino-uno.xml @@ -4,6 +4,9 @@ + + + diff --git a/test/modm/io/io_stream_test.cpp b/test/modm/io/io_stream_test.cpp index 0d3015b62a..f7353bd6e0 100644 --- a/test/modm/io/io_stream_test.cpp +++ b/test/modm/io/io_stream_test.cpp @@ -20,18 +20,7 @@ #include // MODM_ARRAY_SIZE #include // snprintf #include // memset - -#if not defined(MODM_CPU_AVR) #include -#else -#include -namespace std { -template struct numeric_limits { - static constexpr T quiet_NaN() { return NAN; } - static constexpr T infinity() { return INFINITY; } -}; -} -#endif // ---------------------------------------------------------------------------- // simple IODevice which stores all data in a memory buffer @@ -108,14 +97,12 @@ IoStreamTest::testString() TEST_ASSERT_EQUALS(device.bytesWritten, 6U); } -FLASH_STORAGE_STRING(flashString) = "abc"; - void IoStreamTest::testFlashString() { char string[] = "abc"; - (*stream) << modm::accessor::asFlash(flashString); + (*stream) << IFSS("abc"); TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 3); TEST_ASSERT_EQUALS(device.bytesWritten, 3U); @@ -252,27 +239,23 @@ IoStreamTest::testStreamInt32_3() void IoStreamTest::testStreamUint64() { -#ifndef __AVR__ char string[] = "12345678901234"; (*stream) << static_cast(12345678901234ULL); TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 14); TEST_ASSERT_EQUALS(device.bytesWritten, 14U); -#endif } void IoStreamTest::testStreamInt64() { -#ifndef __AVR__ char string[] = "-12345678901234"; (*stream) << static_cast(-12345678901234LL); TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 15); TEST_ASSERT_EQUALS(device.bytesWritten, 15U); -#endif } // ---------------------------------------------------------------------------- @@ -342,6 +325,72 @@ IoStreamTest::testFloat6() TEST_ASSERT_EQUALS(device.bytesWritten, 3U); } +void +IoStreamTest::testFloatPrintf() +{ + char string[] = "1.23000e+00"; + + stream->printf("%e", double(1.23)); + + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 11); + TEST_ASSERT_EQUALS(device.bytesWritten, 11U); +} + +void +IoStreamTest::testFloatPrintf2() +{ + char string[] = "4.57000e+02"; + + stream->printf("%e", double(457.0)); + + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 11); + TEST_ASSERT_EQUALS(device.bytesWritten, 11U); +} + +void +IoStreamTest::testFloatPrintf3() +{ + char string[] = "-5.12314e+07"; + + stream->printf("%e", double(-51231400.0)); + + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 12); + TEST_ASSERT_EQUALS(device.bytesWritten, 12U); +} + +void +IoStreamTest::testFloatPrintf4() +{ + char string[] = "-7.23400e-04"; + + stream->printf("%e", double(-0.0007234)); + + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 12); + TEST_ASSERT_EQUALS(device.bytesWritten, 12U); +} + +void +IoStreamTest::testFloatPrintf5() +{ + char string[] = "nan"; + + stream->printf("%e", std::numeric_limits::quiet_NaN()); + + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 3); + TEST_ASSERT_EQUALS(device.bytesWritten, 3U); +} + +void +IoStreamTest::testFloatPrintf6() +{ + char string[] = "inf"; + + stream->printf("%e", std::numeric_limits::infinity()); + + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 3); + TEST_ASSERT_EQUALS(device.bytesWritten, 3U); +} + void IoStreamTest::testBool1() { @@ -385,14 +434,14 @@ IoStreamTest::testHex1() void IoStreamTest::testHex2() { - char string[] = "48616C6C6F04"; + char string[] = "48616C6C6F00"; char s[] = "Hallo"; + (*stream) << modm::hex; + for (char c : s) (*stream) << c; - (*stream) << modm::hex << s; - - TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 10); - TEST_ASSERT_EQUALS(device.bytesWritten, 10U); + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 12); + TEST_ASSERT_EQUALS(device.bytesWritten, 12U); } void @@ -454,14 +503,14 @@ IoStreamTest::testBin1() void IoStreamTest::testBin2() { - char string[] = "0100100001100001011011000110110001101111"; + char string[] = "010010000110000101101100011011000110111100000000"; char s[] = "Hallo"; + (*stream) << modm::bin; + for (char c : s) (*stream) << c; - (*stream) << modm::bin << s; - - TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 40); - TEST_ASSERT_EQUALS(device.bytesWritten, 40U); + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 48); + TEST_ASSERT_EQUALS(device.bytesWritten, 48U); } void @@ -493,7 +542,7 @@ IoStreamTest::testBin4() void IoStreamTest::testBin5() { - char string[] = "0000000100000000"; + char string[] = "10"; bool boo = true; @@ -502,8 +551,8 @@ IoStreamTest::testBin5() boo = false; (*stream) << modm::bin << boo; - TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 16); - TEST_ASSERT_EQUALS(device.bytesWritten, 16U); + TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, 2); + TEST_ASSERT_EQUALS(device.bytesWritten, 2U); } void @@ -565,10 +614,9 @@ IoStreamTest::testPrintf2() void IoStreamTest::testPrintf3() { -#if not defined(MODM_CPU_AVR) // Test for 64 bit uints and ints on printf unsigned long long unsignedlonglong = 0xFEDCBA9876543210; - (*stream).printf("%llx", unsignedlonglong); + (*stream).printf("%llX", unsignedlonglong); TEST_ASSERT_EQUALS_ARRAY("FEDCBA9876543210", device.buffer, 16); (*stream).flush(); @@ -576,7 +624,6 @@ IoStreamTest::testPrintf3() (*stream).printf("%lld", longlong); TEST_ASSERT_EQUALS_ARRAY("-9223372036854775806", device.buffer, 20); (*stream).flush(); -#endif } int myFunc1(void) { return -1; }; @@ -615,7 +662,7 @@ IoStreamTest::testPointer() const size_t bytesWritten = 18; #endif - (*stream).printf("%p", p); + (*stream).printf("0x%p", p); TEST_ASSERT_EQUALS_ARRAY(string, device.buffer, bytesWritten); TEST_ASSERT_EQUALS(device.bytesWritten, bytesWritten); diff --git a/test/modm/io/io_stream_test.hpp b/test/modm/io/io_stream_test.hpp index 09e4f7460b..65cdd7d17a 100644 --- a/test/modm/io/io_stream_test.hpp +++ b/test/modm/io/io_stream_test.hpp @@ -95,6 +95,25 @@ class IoStreamTest : public unittest::TestSuite void testFloat6(); + // float via printf + void + testFloatPrintf(); + + void + testFloatPrintf2(); + + void + testFloatPrintf3(); + + void + testFloatPrintf4(); + + void + testFloatPrintf5(); + + void + testFloatPrintf6(); + // bool void testBool1();