From 4ea8b348a4ea4d1827e8c5cad079b9f0ae34abbf Mon Sep 17 00:00:00 2001 From: MichaelJonker Date: Mon, 9 Mar 2015 19:02:32 +0100 Subject: [PATCH] pull request version --- .../11.Print/PrintNewFeaturesDemo/.gitignore | 16 + .../PrintNewFeaturesDemo/PrintDemo.cpp | 495 +++++++++++++++++ .../PrintNewFeaturesDemo.ino | 204 +++++++ .../PrintSequenceTeaser.cpp | 525 ++++++++++++++++++ hardware/arduino/avr/cores/arduino/Print.cpp | 479 +++++++--------- hardware/arduino/avr/cores/arduino/Print.h | 244 +++++--- .../arduino/avr/cores/arduino/PrintFormat.h | 129 +++++ 7 files changed, 1744 insertions(+), 348 deletions(-) create mode 100644 build/shared/examples/11.Print/PrintNewFeaturesDemo/.gitignore create mode 100644 build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintDemo.cpp create mode 100644 build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintNewFeaturesDemo.ino create mode 100644 build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintSequenceTeaser.cpp create mode 100644 hardware/arduino/avr/cores/arduino/PrintFormat.h diff --git a/build/shared/examples/11.Print/PrintNewFeaturesDemo/.gitignore b/build/shared/examples/11.Print/PrintNewFeaturesDemo/.gitignore new file mode 100644 index 00000000000..7a0f8f03d21 --- /dev/null +++ b/build/shared/examples/11.Print/PrintNewFeaturesDemo/.gitignore @@ -0,0 +1,16 @@ +# Note: because this repository is a 'fine' selection of a much larger file structure in my work directory +# I choose to ignore all +* + +# As git does not ignore files specified in .gitignore that are already tracked, +# files can be added to the repository with git add -f. +# However, for completeness, all what should be in the repository is summed up here. +# note: git is case insensitive to the entries in .gitignore + +### not ignored (hence included) are ====================== + +#this file +!.gitignore +!PrintDemo.cpp +!PrintSequenceTeaser.cpp +!PrintNewFeaturesDemo.ino diff --git a/build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintDemo.cpp b/build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintDemo.cpp new file mode 100644 index 00000000000..11650675f4b --- /dev/null +++ b/build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintDemo.cpp @@ -0,0 +1,495 @@ +/* PrintDemo.cpp copyright notice + + Copyright (c) 2015 Michael Jonker. All right reserved. + + "THE BEER-WARE LICENSE" (Revision 42++) http://en.wikipedia.org/wiki/Beerware + Michael Jonker wrote this file. + + As long as you retain this notice you can can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. + + This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + For a copy of the GNU Lesser General Public License write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* compilation & testing + +# options to get it compiled outside Arduino environment (for debugging and code optimisation) +export Arduino=/Arduino/arduino-git/ # or whatever +cd $Arduino/examples/test_FormattedPrint + +g++ -g \ + -DtestCode\ + -Dno_testCompilationErrors\ + -I$Arduino/hardware/arduino/avr/cores/arduino\ + -I$Arduino/hardware/arduino/sam/cores/arduino\ + PrintDemo.cpp\ + 2>&1| grep --text -E "error:"\ + ||\ + gdb \ + ./a.exe + +# command for code inspection +$Arduino/arduino-1.0.5/hardware/tools/avr/bin/avr-g++ \ + -DtestCode \ + -I../../../arduino-1.0.5/hardware/arduino/cores/arduino \ + -I../../../arduino-1.0.5/hardware/arduino/variants/standard \ + -I../../../arduino-1.0.5/hardware/tools/avr/avr/include \ + -mmcu=atmega328p -Os PrintDemo.cpp \ + -S \ + 2>&1| grep -E "error" + +# note: includes are needed for WString.h and avr/pgmspace.h +*/ + +#ifdef testCode +#define __ATTR_PROGMEM__ +#define pgm_read_byte(x) *(x) +#define __PGMSPACE_H_ +#define PSTR +typedef const char* PGM_P; + +#include +#include + +#define Arduino_h +#define _define_static_PrintFormat_instances // force declaration of static PrintFormat instances here (in PrintFromat.h included from Print.h) +#include "Print.h" + +#include +#include + +class HardwareSerial : public Print +{ + public: + virtual size_t write(uint8_t c) + { + printf("%c", c); + return 1; + } +} Serial; + +void printDemo(); // forward definition + +int main() +{ + printDemo(); +} +#include "Print.cpp" + + +#else + +#include "Arduino.h" +#endif + + +static unsigned long& _asLong (const float& aFloat) { return (unsigned long&) aFloat;} // interpret a float as long +static float& _asFloat(const unsigned long& aLong) { return (float&) aLong; } // interpret a long as float + +void printDemo() +{ + unsigned int nc; + unsigned int i; + Serial.println(F("\nrunning printDemo... ======================================================================================\n")); + + Serial.println(F("The new version of the Print class allows you to code things like:")); + Serial.print(F("Serial.print(3.141592, PRECISION(4) & FIELDSIZE(3)); // ")); + Serial.print(3.141592, PRECISION(4) & FIELDSIZE(3)); Serial.println(F(" ; note FIELDSIZE only affects the integer part and does not concern the fraction")); + Serial.print(F("Serial.print(66, HEX); // ")); + Serial.print(66, HEX); Serial.println(F(" ;")); + Serial.print(F("Serial.print(22, RADIX(5) & FIELDSIZE(8) & ALIGNLEFT); // ")); + Serial.print(22, RADIX(5) & FIELDSIZE(8) & ALIGNLEFT); Serial.println(F(" ;")); + Serial.print(F("Serial.print(34, OCT & FIELDSIZE(8) & FORCESIGN & FILLZEROS); // ")); + Serial.print(34, OCT & FIELDSIZE(8) & FORCESIGN & FILLZEROS); Serial.println(F(" ; note forced sign does not consume 'fieldsize'")); + Serial.print(F("Serial.print(54, 13); // ")); + Serial.print(54, 13); Serial.println(F(" ; backward compatible base argument")); + Serial.print(F("Serial.print(3.141592, 3); // ")); + Serial.print(3.141592, 3); Serial.println(F(" ; backward compatible precision argument")); + + Serial.println(F("\nRadix test, looping from radix 0 .. 34 (0x22)")); + IntegerFormat di = HEX & FIELDSIZE(2) & FILLZEROS; + IntegerFormat ti = RADIX(0) & FIELDSIZE(4); + Serial.print("0x"); Serial.print( 0, di); nc=Serial.print(42u, ti & RADIX( 0) & FIELDSIZE(8)); Serial.print(" nc="); Serial.println(nc); // will result in the default radix + Serial.print("0x"); Serial.print( 1, di); nc=Serial.print(42u, ti & RADIX( 1) & FIELDSIZE(8)); Serial.print(" nc="); Serial.println(nc); // should become binary format? + Serial.print("0x"); Serial.print( 2, di); nc=Serial.print(42u, ti & RADIX( 2) & FIELDSIZE(8)); Serial.print(" nc="); Serial.println(nc); + ti = ti & FIELDSIZE(8); + for(i=3; i<35; i++) + { + Serial.print("0x"); Serial.print( i, di); nc=Serial.print(42u, ti & RADIX( i) ); Serial.print(" nc="); Serial.println(nc); + } + Serial.println(F("note: Radix 33 (0x21)... wraps onto radix 1...")); + + Serial.println(F("\nPrecision test looping over precisison and complemented with Serial.printPattern((char*) \" - -.- - |\", 42-nc, nc%10)")); + IntegerFormat df = DEC & FIELDSIZE(2); + PrintFormat tf = FIELDSIZE(8); + for(i=0; i<32; i++) + { + Serial.print( i, df); + nc =Serial.print(41.9876543210987654321098765432109876543210, tf & PRECISION( i) ); + nc+=Serial.print(" "); + nc+=Serial.printPattern((char*) " - -.- - |", 42-nc, nc%10); + Serial.print(" nc="); Serial.println(nc,FIELDSIZE(2)); + } + + Serial.println(F("\nundocumented printPattern feature:")); + Serial.print(F("Serial.printPattern((char*) \" - -.- - |\\0surprise \", 60, 11); // ")); + Serial.printPattern((char*) " - -.- - |\0surprise :", 60, 11); + Serial.println(); + + Serial.print(F("\ntest print FlashStringHelper\nSerial.print(F(\"waar eens de boterbloempjes bloeiden...\")); // ")); + Serial.println(F("waar eens de boterbloempjes bloeiden...")); + + Serial.println("\ndefault test:"); + Serial.print("Serial.print(0xB) :"); Serial.println(0xB); + Serial.print("Serial.print(12.345) :"); Serial.println(12.345); + + Serial.println("\nspecial float test"); + { + nc=Serial.println("test positive float overflow (Note, ovf corresponds to float that cannot be represented as a long) this will be resolved by a future Scientific format)"); + FloatFormat ff = FIELDSIZE(15) & PRECISION(2); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff60, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffff60, FIELDSIZE(10)); nc +=Serial.print( float(0xffffff60), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff70, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffff70, FIELDSIZE(10)); nc +=Serial.print( float(0xffffff70), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff7f, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffff7f, FIELDSIZE(10)); nc +=Serial.print( float(0xffffff7f), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff80, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffff80, FIELDSIZE(10)); nc +=Serial.print( float(0xffffff80), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff81, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffff81, FIELDSIZE(10)); nc +=Serial.print( float(0xffffff81), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff90, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffff90, FIELDSIZE(10)); nc +=Serial.print( float(0xffffff90), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffffa0, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffffa0, FIELDSIZE(10)); nc +=Serial.print( float(0xffffffa0), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xfffffff0, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xfffffff0, FIELDSIZE(10)); nc +=Serial.print( float(0xfffffff0), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffffff, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print( 0xffffffff, FIELDSIZE(10)); nc +=Serial.print( float(0xffffffff), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.println("test negative float overflow"); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff60, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffff60, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffff60), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff70, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffff70, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffff70), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff7f, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffff7f, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffff7f), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff80, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffff80, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffff80), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff81, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffff81, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffff81), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffff90, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffff90, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffff90), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffffa0, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffffa0, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffffa0), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xfffffff0, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xfffffff0, FIELDSIZE(10)); nc +=Serial.print( -float(0xfffffff0), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xffffffff, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" -"); nc +=Serial.print( 0xffffffff, FIELDSIZE(10)); nc +=Serial.print( -float(0xffffffff), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.println("test positive/negative float nan & inf"); // see also http://en.wikipedia.org/wiki/IEEE_754-1985 and + nc=Serial.print("0x"); nc +=Serial.print( 0x7F800000, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0x7F800000, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0x7F800000), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0x7F800001, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0x7F800001, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0x7F800001), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0x7FC00000, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0x7FC00000, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0x7FC00000), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0x7FFFFFFF, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0x7FFFFFFF, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0x7FFFFFFF), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xFF800000, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0xFF800000, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0xFF800000), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xFF800001, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0xFF800001, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0xFF800001), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xFFC00000, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0xFFC00000, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0xFFC00000), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + nc=Serial.print("0x"); nc +=Serial.print( 0xFFFFFFFF, HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print((long)0xFFFFFFFF, FIELDSIZE(10)); nc +=Serial.print(_asFloat(0xFFFFFFFF), ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + +#ifdef development_test_negative_nan_conversion + float negnan = _asFloat(0xFFFFFFFF); + nc=Serial.println("test negative float nan conversion"); + nc=Serial.print("0x"); nc +=Serial.print(_asLong(negnan), HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print(_asLong(negnan), FIELDSIZE(10)); nc +=Serial.print(negnan, ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); + if(_asLong(negnan)&0x80000000) { _asLong(negnan) = _asLong(negnan) & ~0x80000000; } + nc=Serial.print("0x"); nc +=Serial.print(_asLong(negnan), HEX & FIELDSIZE(8) & FILLZEROS); nc +=Serial.print(" "); nc +=Serial.print(_asLong(negnan), FIELDSIZE(10)); nc +=Serial.print(negnan, ff); nc +=Serial.print(" | "); nc +=Serial.println(nc,FIELDSIZE(2)); +#endif + + Serial.print("\nBeware, HEX constants like 0xFFFFFFFF and 0xFFFFFFFFl are interpreted as (unsigned long)\n"); + Serial.print("print( 0xFFFFFFFF, HEX/DEC) : "); Serial.print( 0xFFFFFFFF, HEX & FIELDSIZE(8) & FILLZEROS); Serial.print("/"); Serial.println( 0xFFFFFFFF); + Serial.print("print( 0xFFFFFFFFl, HEX/DEC) : "); Serial.print( 0xFFFFFFFFl, HEX & FIELDSIZE(8) & FILLZEROS); Serial.print("/"); Serial.println( 0xFFFFFFFF); + Serial.print("print( (long)0xFFFFFFFF, HEX/DEC) : "); Serial.print( (long)0xFFFFFFFF, HEX & FIELDSIZE(8) & FILLZEROS); Serial.print("/"); Serial.println( (long)0xFFFFFFFF); + Serial.print("print((signed)0xFFFFFFFF, HEX/DEC) : "); Serial.print((signed)0xFFFFFFFF, HEX & FIELDSIZE(8) & FILLZEROS); Serial.print("/"); Serial.println((signed)0xFFFFFFFF); + Serial.print("print( (unsigned)-1, HEX/DEC) : "); Serial.print( (unsigned)-1, HEX & FIELDSIZE(8) & FILLZEROS); Serial.print("/"); Serial.println( (unsigned)-1); + Serial.print("print( -1, HEX/DEC) : "); Serial.print( -1, HEX & FIELDSIZE(8) & FILLZEROS); Serial.print("/"); Serial.println( -1); + Serial.print("\n"); + } + Serial.println("\ntest FIELDSIZE(5)"); + { + Serial.println( 0, DEC & FIELDSIZE(5)); + Serial.println( 1, DEC & FIELDSIZE(5)); + Serial.println( 12, DEC & FIELDSIZE(5)); + Serial.println( 123, DEC & FIELDSIZE(5)); + Serial.println( 1234, DEC & FIELDSIZE(5)); + Serial.println( 12345, DEC & FIELDSIZE(5)); + Serial.println( 123456, DEC & FIELDSIZE(5)); + Serial.println( 1234567, DEC & FIELDSIZE(5)); + Serial.println(- 1, DEC & FIELDSIZE(5)); + Serial.println(- 12, DEC & FIELDSIZE(5)); + Serial.println(- 123, DEC & FIELDSIZE(5)); + Serial.println(- 1234, DEC & FIELDSIZE(5)); + Serial.println(- 12345, DEC & FIELDSIZE(5)); + Serial.println(- 123456, DEC & FIELDSIZE(5)); + Serial.println(-1234567, DEC & FIELDSIZE(5)); + + Serial.println("\ntest FIELDSIZE(5) & STRICTSIZE"); + + Serial.println( 0, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 1, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 12, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 123, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 1234, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 12345, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 123456, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println( 1234567, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(- 1, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(- 12, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(- 123, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(- 1234, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(- 12345, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(- 123456, DEC & FIELDSIZE(5) & STRICTSIZE); + Serial.println(-1234567, DEC & FIELDSIZE(5) & STRICTSIZE); + + Serial.println("\ntest FIELDSIZE(5) & STRICTSIZE & FORCESIGN"); + Serial.println( 0, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 1, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 12, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 123, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 1234, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 12345, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 123456, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println( 1234567, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(- 1, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(- 12, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(- 123, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(- 1234, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(- 12345, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(- 123456, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(-1234567, DEC & FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + + Serial.println("\ntest FIELDSIZE(1) & STRICTSIZE"); + Serial.println( 0, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println( 1, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println( 12, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println( 123, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println(- 1, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println(- 12, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println(- 123, DEC & FIELDSIZE(1) & STRICTSIZE); + Serial.println("\ntest FIELDSIZE(1) & STRICTSIZE & FORCESIGN"); + Serial.println( 0, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + Serial.println( 1, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + Serial.println( 12, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + Serial.println( 123, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + Serial.println(- 1, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + Serial.println(- 12, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + Serial.println(- 123, DEC & FIELDSIZE(1) & STRICTSIZE & FORCESIGN); + } + Serial.println("\ntest alignments"); + { + Serial.print( 1234, DEC & FIELDSIZE(8) & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & FILLZEROS")); + Serial.print( 1234, DEC & FIELDSIZE(8) ); Serial.println(F(" : FIELDSIZE(8)")); + Serial.print( 1234, DEC & FIELDSIZE(8) & ALIGNLEFT ); Serial.println(F(" : FIELDSIZE(8) & ALIGNLEFT")); + Serial.print( 1234, DEC & FIELDSIZE(8) & ALIGNLEFT & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & ALIGNLEFT & FILLZEROS")); + Serial.print(-1234, DEC & FIELDSIZE(8) & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & FILLZEROS")); + Serial.print(-1234, DEC & FIELDSIZE(8) ); Serial.println(F(" : FIELDSIZE(8)")); + Serial.print(-1234, DEC & FIELDSIZE(8) & ALIGNLEFT ); Serial.println(F(" : FIELDSIZE(8) & ALIGNLEFT")); + Serial.print(-1234, DEC & FIELDSIZE(8) & ALIGNLEFT & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & ALIGNLEFT & FILLZEROS")); + Serial.println("\ntest alignments with forced sign"); + Serial.print( 1234, DEC & FIELDSIZE(8) & FORCESIGN & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN & FILLZEROS")); + Serial.print( 1234, DEC & FIELDSIZE(8) & FORCESIGN ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN")); + Serial.print( 1234, DEC & FIELDSIZE(8) & FORCESIGN & ALIGNLEFT ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN & ALIGNLEFT")); + Serial.print( 1234, DEC & FIELDSIZE(8) & FORCESIGN & ALIGNLEFT & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN & ALIGNLEFT & FILLZEROS")); + Serial.print(-1234, DEC & FIELDSIZE(8) & FORCESIGN & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN & FILLZEROS")); + Serial.print(-1234, DEC & FIELDSIZE(8) & FORCESIGN ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN")); + Serial.print(-1234, DEC & FIELDSIZE(8) & FORCESIGN & ALIGNLEFT ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN & ALIGNLEFT")); + Serial.print(-1234, DEC & FIELDSIZE(8) & FORCESIGN & ALIGNLEFT & FILLZEROS ); Serial.println(F(" : FIELDSIZE(8) & FORCESIGN & ALIGNLEFT & FILLZEROS")); + } + Serial.println(F("notes:")); + Serial.println(F("& FILLZER0 & ALLIGNLEFT produces dashes and not zeros, should we find a more appropriate name for FILLZEROS (FILLALTCHAR, NOSPACEFILL, ...)")); + Serial.println(F("& FORCESIGN symbol is not reduced from FIELDSIZE(), the same holds for PRECISION")); + PrintFormat pf0 = FORCESIGN; + + Serial.println(); + Serial.println(F("The PrintFormatter class for custom formatted print has not been implemented.")); + Serial.println(F("Custom formatting can be realized very easily with an adapter class.")); + #define __stringify(__ITEM) #__ITEM + #define __tostring(__ITEM) __stringify(__ITEM) + Serial.print (F("See the code in " __FILE__ ", line " __tostring(__LINE__) ", for an example of an IPAddressFormatAdapter")); + + +// Using an adapter class IPAddressFormatAdapter + class IPAddressFormatAdapter : public Printable + { + typedef unsigned char IPAdress[4]; + IPAdress myIPAddress; + static unsigned long& _asLong(IPAdress& anIPAdress) { return (unsigned long&) anIPAdress;} // interpret an unsigned long as a unsigned char[4] + + public: + IPAddressFormatAdapter(unsigned long anIpAddress) { _asLong(myIPAddress)=anIpAddress;} + + size_t printTo(Print& out) const + { + unsigned char nc=0; + for(int i=0;i<4; i++) {if(i!=0) nc+=out.print('.'); nc+=out.print(myIPAddress[3-i]);} // I'm an endian, I'm a little endian! (but not an Inglishmen in NY) + return nc; + } + }; + Serial.println(); + Serial.println(F("Serial.print(IPAddressFormatAdapter(0x21428418)); // produces:")); + Serial.print(IPAddressFormatAdapter(0x21428418)); + + Serial.println('\n'); + Serial.println(F("Compiler checks: an illegal PrintFormat usage is trapped as a compilation error:")); + Serial.println(F("Serial.print(42, PRECISION(4)); // error: 'PrintFormat' is an inaccessible base of 'FloatFormat'")); + Serial.println(F("Serial.print(42., HEX); // error: 'PrintFormat' is an inaccessible base of 'IntegerFormat'")); + Serial.println(F("PrintFormat myFormat(HEX & PRECISION(4)); // error: ambiguous overload for 'operator&' (operand types are 'const IntegerFormat' and 'const FloatFormat')")); + Serial.println(F("// To test trapping of (almost) all illegal PrintFormat usage, compile with -DtestCompilationErrors")); + +#ifdef testCompilationErrors +// The following code will produce compilation errors, as it involves impossible PrintFormat combinations or conversions + PrintFormat pf1 = HEX; // error : 'PrintFormat' is an inaccessible base of 'IntegerFormat' + PrintFormat pf2 = PRECISION(4); // error : 'PrintFormat' is an inaccessible base of 'FloatFormat' + PrintFormat pf3 = HEX & PRECISION(4); // error : conversion from 'invalid_PrintFormat_operation' to non-scalar type 'PrintFormat' requested + PrintFormat pf4 = PRECISION(4) & HEX; // error : conversion from 'invalid_PrintFormat_operation' to non-scalar type 'PrintFormat' requested + PrintFormat pf5 = PRECISION(4) & FIELDSIZE(4) & HEX; // error : conversion from 'invalid_PrintFormat_operation' to non-scalar type 'PrintFormat' requested + PrintFormat pf6 = PRECISION(4) & FIELDSIZE(4) & 42; // error : conversion from 'invalid_PrintFormat_operation' to non-scalar type 'PrintFormat' requested + + int i0 = HEX & PRECISION(4); // error : cannot convert 'invalid_PrintFormat_operation' to 'int' in initialization + int i1 = PRECISION(4) & HEX; // error : cannot convert 'invalid_PrintFormat_operation' to 'int' in initialization + int i2 = PRECISION(4) & FIELDSIZE(4) & HEX; // error : cannot convert 'invalid_PrintFormat_operation' to 'int' in initialization + int i3 = PRECISION(4) & FIELDSIZE(4) & 42; // error : conversion from 'invalid_PrintFormat_operation' to non-scalar type 'PrintFormat' requested + + Serial.print((unsigned)42, FIELDSIZE(4) & PRECISION(4) & FILLZEROS & FORCESIGN); // error : 'PrintFormat' is an inaccessible base of 'FloatFormat' + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & FILLZEROS & FORCESIGN); // error : 'PrintFormat' is an inaccessible base of 'FloatFormat' + + Serial.print((unsigned)42, HEX & PRECISION(4) & FIELDSIZE(4) & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'const IntegerFormat' and 'const FloatFormat') + Serial.print((unsigned)42, PRECISION(4) & HEX & FIELDSIZE(4) & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'const FloatFormat' and 'const IntegerFormat') + Serial.print((unsigned)42, 42 & PRECISION(4) & FIELDSIZE(4) & HEX & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'int' and 'const FloatFormat') + Serial.print((unsigned)42, PRECISION(4) & 42 & FIELDSIZE(4) & HEX & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'const FloatFormat' and 'int') + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & HEX & 42 & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'FloatFormat' and 'const IntegerFormat') + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & HEX & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'FloatFormat' and 'const IntegerFormat') + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & 42 & HEX & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'FloatFormat' and 'int') + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & HEX ); // error : ambiguous overload for 'operator&' (operand types are 'FloatFormat' and 'const IntegerFormat') + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & 42 ); // error : ambiguous overload for 'operator&' (operand types are 'FloatFormat' and 'int') + + Serial.print((unsigned)42, FIELDSIZE(4) & HEX & PRECISION(4) & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'IntegerFormat' and 'const FloatFormat') + Serial.print((unsigned)42, FIELDSIZE(4) & PRECISION(4) & HEX & FILLZEROS & FORCESIGN); // error : ambiguous overload for 'operator&' (operand types are 'FloatFormat' and 'const IntegerFormat') + Serial.print((unsigned)42, PRECISION(4) & FIELDSIZE(4) & FILLZEROS & FORCESIGN); // error : call of overloaded 'print(unsigned int, FloatFormat)' is ambiguous + + // no match for operator + Serial.print((unsigned)42, FIELDSIZE(4) || HEX || FILLZEROS); + Serial.print((unsigned)42, FIELDSIZE(4) | HEX | FILLZEROS); + Serial.print((unsigned)42, FIELDSIZE(4) && HEX && FILLZEROS); + Serial.print((unsigned)42, FIELDSIZE(4) & HEX & FILLZEROS); // ok + Serial.print((unsigned)42, FIELDSIZE(4) + HEX + FILLZEROS); // not initutive, because HEX+HEX = ?? + Serial.print((unsigned)42, FIELDSIZE(4), HEX, FILLZEROS); // do not think this is technically possible, and if so (vararg) inefficient + Serial.print((unsigned)42, (FIELDSIZE(4), HEX, FILLZEROS) ); // unnatural (but could be made to work by overloading the operator, + + PrintFormat myPrintFormat1 = FIELDSIZE(4) & RADIX(4) & FILLZEROS; // error : 'PrintFormat' is an inaccessible base of 'IntegerFormat' + Serial.print((unsigned)42, myPrintFormat1); + Serial.print((unsigned)42, myPrintFormat1 & ~FILLZEROS); + + PrintFormat myPrintFormat2 = FIELDSIZE(4) + RADIX(4) + FILLZEROS; // error : no match for 'operator+' (operand types are 'const PrintFormat' and 'const IntegerFormat') + Serial.print((unsigned)42, myPrintFormat2); + Serial.print((unsigned)42, myPrintFormat2 -FILLZEROS); // error : no match for 'operator-' (operand types are 'PrintFormat' and 'const PrintFormat') + + (FIELDSIZE(4) + HEX + HEX + HEX) == (FIELDSIZE(4) + HEX); // error : no match for 'operator+' (operand types are 'const PrintFormat' and 'const IntegerFormat') +#endif + + Serial.print(F("\nEnd printDemo... ==========================================================================================\n\n")); +} + +/* Still todo: + +implement +//- radix offset handling +//- precision offset handling +//- fieldsize offset handling +//- ALIGNLEFT +//- STRICTSIZE +//- SIGNED inf nan ovf +//- ALIGNED inf nan ovf +//- use printString in printNumber +//- add inline padding function printPadding(char padding, unsigned char size) +//- printPadding to return number of characters written; +//- use printPadding in printString +//- use printPadding to add precision space for inf nan ovf +//- add inline pattern function printPattern(char* pattern, unsigned char size) +//- add tests cases for nan and inf +//- recognize negative nan +//- test on Arduino and release + +- StringFormat +- printString specific options +- ScientificFormat +- printScientific +- footprint studies +- optimize floatPrint +- invalid_PrintFormat_operation sink, i.e. define a 'black hole' class that absorbs all non defined operations involving classes derived from PrintFormat +Debatable: +- implementation of .withSize() .withEtc() functions +*/ +/* Discussions + +for reflection +FIELDSIZE(n), RADIX(n), PRECISION(n) +a value of n=0 indicates the default. +IntegerFormat mfbase = DEC & FIELDSIZE(4); +IntegerFormat mfder1 = mfbase & HEX; //gives HEX & FIELDSIZE(4); +IntegerFormat mfder2 = mfbase & FIELDSIZE(2); //gives DEC & FIELDSIZE(2); + +Hence FIELDSIZE(0), RADIX(0), PRECISION(0) are not available. We may get the 0 values back if we could have a removal operator (and/or a removal Format Class) +other solution, each format has a bit mask showing the active fields, this bit mask is only used for combining format fields + +Naming conventions +Currently Print related constants are part of the namespace ArduinoPrint. This namespace is automatically used when Print.h is included +- namespace ArduinoPrint, vs nested namespace Arduino::Print:: +- using namespace by default? + +printFormat hierarchy based on +- PrintFormat +- IntegerFormat +- FloatFormat +- StringFormat +- ScientificFormat +- NumericFormat + +- RADIX(n) note: free choice RADIX alternative would be BASE(n) + RADIX(0) means unchanged (or default) radix. + RADIX(1) should be RADIX(10) or binary format +- BIN note: is implemented as RADIX(2) +- OCT idem RADIX(8) +- DEC idem RADIX(10) +- HEX idem RADIX(16) +- FIELDSIZE(n) note: FIELDSIZE does not count forced signs, and float fractions +- ALIGNLEFT note: not allowed for float format +- FORCESIGN +- FILLZEROS note FILLZEROS with ALIGNLEFT will fill dashes, should we rename FILLZEROS with FILLALT +- STRICTSIZE note better alternative ENFORCEDSIZE, or maybe STRICTSIZE(n) +- PRECISION(n) +- EXPONENT EXPONENT(n) SCIENTIFIC(n) Note, still to be implemented, but what format to be chosen + +breaking of old code +- RADIX(0) now gives default, used to give raw format, RADIX(1) gives Decimal, should give raw format? swap RADIX(0) and RADIX(1)? +- decimal point in float is not suppressed (or should precision parameter include the decimal point) + +Discussion +- option to suppress decimal point for PRECISION(0)? (one could convert floats to int before printing, one can add a decimal point after printing) +- implement .withSize() etc... explain the fear for an "unhealthy" explosion of added methods for all possible derived classes. +*/ +/* On the CustomFormatter + i.e. print(elementary_type, customFormatter) + a custom formatter class is lengthy and boring to implement: one cannot define templates for virtual methods (dat is logisch he!) + Furthermore, the need for a custom formatter class is questionable. Encapsulation of data in a printable adapter class may be more appropriate, like: + +// Using an adapter class IPAddressFormatAdapter + class IPAddressFormatAdapter : public Printable + { + typedef unsigned char IPAdress[4]; + IPAdress myIPAddress; + static unsigned long& _asLong(IPAdress& anIPAdress) { return (unsigned long&) anIPAdress;} // interpret an unsigned long as a unsigned char[4] + + public: + IPAddressFormatAdapter(unsigned long anIpAddress) { _asLong(myIPAddress)=anIpAddress;} + + size_t printTo(Print& out) const + { + unsigned char nc=0; + for(int i=0;i<4; i++) {if(i!=0) nc+=out.print('.'); nc+=out.print(myIPAddress[3-i]);} // I'm an endian, I'm a little endian! (but not an Inglishmen in NY) + return nc; + } + }; + +// the following hypothetical (because not available) method invocation: +// Serial.print(myIpAdress, myIpAdressFormatter); +// may be implemented as: + Serial.print(IPAddressFormatAdapter(myIpAdress)); +*/ + +// eof diff --git a/build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintNewFeaturesDemo.ino b/build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintNewFeaturesDemo.ino new file mode 100644 index 00000000000..1d59b7b6641 --- /dev/null +++ b/build/shared/examples/11.Print/PrintNewFeaturesDemo/PrintNewFeaturesDemo.ino @@ -0,0 +1,204 @@ +void printDemo(); +void printSequenceTeaser(); + +void setup() { + // put your setup code here, to run once: + Serial.begin(9600); + while (!Serial) {} // wait for serial port to connect. Needed for Leonardo only +} + +void loop() { + // put your main code here, to run repeatedly: + + // The new version of the Print class allows you to code things like: + Serial.println("\nFirst a trivial exercise:"); + Serial.println(3.141592, PRECISION(4) & FIELDSIZE(3)); // note FIELDSIZE only concerns the integer part + Serial.println(66, HEX); + Serial.println(22, RADIX(5) & FIELDSIZE(8) & ALIGNLEFT); + Serial.println(34, OCT & FIELDSIZE(8) & FORCESIGN & FILLZEROS); + + // print is backward compatible for the radix and precision argument, so old code won't break + Serial.println(54, 13); + Serial.println(3.141592, 3); + + Serial.println(F("\nFor real long string, we recommend you to use the F() macro, it will save dynamic memory.")); + Demo_Radix(); // Demonstration of RADIX usage complemented with Serial.printPattern() + Demo_Precision(); // Demonstration of PRECISION usage complemented with Serial.printPattern() + Demo_Fieldsize(); // Demonstration of FIELDSIZE usage + Demo_Alignment(); // Demonstration of ALIGNMENT usage + Demo_customFormat();// Demonstration of custom format option + Teaser(); // coming next + + // for more demos, uncomment the next line + // printDemo(); // but this has now become rather boring + + // for a sneak preview of what's coming next uncomment the next line + // printSequenceTeaser(); + +// Note: the following lines produce compiler errors as they involve incompatible PrintFormat objects: +/* So it is commented out + FloatPrintFormat HexFloatFormat = HEX; + IntegerPrintFormat PrecisionIntergerFormat = PRECISION(5); + PrintFormat HexPrintFormat = HEX; + PrintFormat PrecisionPrintFormat = PRECISION(5); + IntegerFormat PrecisionHexCombination = HEX & PRECISION(5); + Serial.print(42, PRECISION(5)); // PRECISION only works for floats + Serial.print(3.14, HEX); // HEX only for integers + Serial.print("42", HEX); // HEX also does not work for strings +// */ + + delay(5000); +} + +void Demo_Radix() +{ + Serial.println(F("\nRADIX() demo, for RADIX( 0 .. 42)")); + const PrintFormat testFormat = FIELDSIZE(8); + for(int i=0; i<42; i++) + { + Serial.print("0x"); Serial.print( i, FIELDSIZE(2)); Serial.println(42, testFormat & RADIX( i) ); + } +} + +void Demo_Precision() +{ + Serial.println(F("\nPRECISION() Demo, for PRECISION(0 ..42); complemented with Serial.printPattern()")); + IntegerFormat myIntFormat = DEC & FIELDSIZE(2); + PrintFormat myTestFormat = FIELDSIZE(8); + + for(int i=0; i<42; i++) + { + unsigned char nchar=0; // number of characters printed this line + nchar+=Serial.print( i, myIntFormat); + nchar+=Serial.print(41.9876543210987654321098765432109876543210, myTestFormat & PRECISION(i) ); + nchar+=Serial.print(" "); + // we now fill it up with: + char* patternString = " - -.- - |"; + int patternLength = 50 - nchar; + int patternOffset = nchar % strlen(patternString); + nchar+=Serial.printPattern(patternString, patternLength, patternOffset ); + Serial.print(" nchar="); Serial.println(nchar,FIELDSIZE(6) & ALIGNLEFT & FILLZEROS); + } +} + +void Demo_Fieldsize() +{ + Serial.println(F("FIELDSIZE demo, printing varying length patterns with fixed FIELDSIZE with / without ALIGNLEFT")); + long testValues[] = {0, 1, 12, 123, 1234, 1234, 12345, 123456, -1223456, -12345, -1234, -123, -12, -1, -0}; + int nTestValues = sizeof(testValues) / sizeof(testValues[0]); + + Serial.println(F("FIELDSIZE(8)")); + for(int i=0; i< nTestValues; i++) + { + Serial.print(testValues[i], FIELDSIZE(8) & ALIGNLEFT); + Serial.print(" / "); + Serial.print(testValues[i], FIELDSIZE(8)); + Serial.println(); + } + + Serial.println(F("FIELDSIZE(5)")); + for(int i=0; i< nTestValues; i++) + { + Serial.print(testValues[i], FIELDSIZE(5) & ALIGNLEFT); + Serial.print(" / "); + Serial.print(testValues[i], FIELDSIZE(5)); + Serial.println(); + } + + Serial.println(F("FIELDSIZE(5) & STRICTSIZE")); + for(int i=0; i< nTestValues; i++) + { + Serial.print(testValues[i], FIELDSIZE(5) & STRICTSIZE & ALIGNLEFT); + Serial.print(" / "); + Serial.print(testValues[i], FIELDSIZE(5) & STRICTSIZE); + Serial.println(); + } + + Serial.println(F("FIELDSIZE(5) & STRICTSIZE & FORCESIGN")); + for(int i=0; i< nTestValues; i++) + { + Serial.print(testValues[i], FIELDSIZE(5) & STRICTSIZE & FORCESIGN & ALIGNLEFT); + Serial.print(" / "); + Serial.print(testValues[i], FIELDSIZE(5) & STRICTSIZE & FORCESIGN); + Serial.println(); + } +} + +void Demo_Alignment() +{ + Serial.println(F("\nALIGNMENT demo, printing +/-1234 with FIELDSIZE(8) and various combinations of FORCESIGN & FILLZEROS & ALIGNLEFT")); + // zut FIELDSIZE ALIGLEFT does not work (yet) with strings, so ... verzin toch een list jonge vriend. + int nc; + nc=Serial.print(F(" ") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) ); Serial.println(";"); + nc=Serial.print(F(" & ALIGNLEFT") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & ALIGNLEFT ); Serial.println(";"); + nc=Serial.print(F(" & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" & ALIGNLEFT & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & ALIGNLEFT & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" ") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) ); Serial.println(";"); + nc=Serial.print(F(" & ALIGNLEFT") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & ALIGNLEFT ); Serial.println(";"); + nc=Serial.print(F(" & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" & ALIGNLEFT & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & ALIGNLEFT & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN ") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & FORCESIGN ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN & ALIGNLEFT") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & FORCESIGN & ALIGNLEFT ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & FORCESIGN & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN & ALIGNLEFT & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print( 1234, FIELDSIZE(8) & FORCESIGN & ALIGNLEFT & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN ") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & FORCESIGN ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN & ALIGNLEFT") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & FORCESIGN & ALIGNLEFT ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & FORCESIGN & FILLZEROS ); Serial.println(";"); + nc=Serial.print(F(" & FORCESIGN & ALIGNLEFT & FILLZEROS") ); Serial.printPadding(' ', 40-nc); Serial.print(" : "); Serial.print(-1234, FIELDSIZE(8) & FORCESIGN & ALIGNLEFT & FILLZEROS ); Serial.println(";"); +} + +void Teaser() +{ + #ifdef Teaser + Serial.println(F("\nPrintSequence teaser")); + PrintSequence mps(Serial); +//PrintSequence mps=Serial.getPrintSequence();// alternative + + n = mps<<"Ciao Arduini"< wrote this file. + + As long as you retain this notice you can can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. + + This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + For a copy of the GNU Lesser General Public License write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + + This code replaces the version of David A. Mellis. +*/ + +/* compilation & testing + +# options to get it compiled outside Arduino environment (for debugging and code optimisation) +export Arduino=/Arduino/arduino-git/ # or whatever +cd $Arduino/examples/test_FormattedPrint + +g++ -g \ + -DtestCode\ + -Dno_testCompilationErrors\ + -I$Arduino/hardware/arduino/avr/cores/arduino\ + -I$Arduino/hardware/arduino/sam/cores/arduino\ + PrintSequenceTeaser.cpp\ + 2>&1| grep --text -E "error:"\ + ||\ + gdb \ + ./a.exe + +# command for code inspection +$Arduino/arduino-1.0.5/hardware/tools/avr/bin/avr-g++ \ + -DtestCode \ + -I../../../arduino-1.0.5/hardware/arduino/cores/arduino \ + -I../../../arduino-1.0.5/hardware/arduino/variants/standard \ + -I../../../arduino-1.0.5/hardware/tools/avr/avr/include \ + -mmcu=atmega328p -Os PrintSequenceTeaser.cpp \ + -S \ + 2>&1| grep -E "error" + +# note: includes are needed for WString.h and avr/pgmspace.h +*/ + +#ifdef testCode +#define __ATTR_PROGMEM__ +#define pgm_read_byte(x) *(x) +#define __PGMSPACE_H_ +#define PSTR +typedef const char* PGM_P; + +#include +#include + +#define Arduino_h +#define _define_static_PrintFormat_instances // force declaration of static PrintFormat instances here (in PrintFromat.h included from Print.h) +#include "Print.h" + +#else +#include "Arduino.h" + +#endif + + + +#define PrintFormatterV1_h // no, no, no-ho, we don't want this printFormatter +#ifndef PrintFormatterV1_h // == PrintFormatter.h =================================================================================== +#define PrintFormatterV1_h + +class Print; +class PrintFormatter; + +// implementation of Print method print(T_TYPE item, PrintFormatter aPrintFormatter) +template size_t Print::print(T_TYPE item, PrintFormatter aPrintFormatter) { aPrintFormatter.printFormatted(this, item);} + +class PrintFormatter +{ + public: + // so I thought "that will be rather easy" and wrote (before commenting it out with //) + // template virtual size_t printFormatted(Print& out, T_TYPE item) {}; + + // but of course templates may not be virtual, (en da's logisch hè). + // this means that the PrintFormatter class has to define virtual methods for all basic types (making the vtable rather large I guess), + + // So we could define entries for all basic types that map to a few intrinsic primitive types (integer, float, string),... + // which are defined as virtual. + virtual size_t printFormattedInterger (Print& out, unsigned long item) const = 0; + virtual size_t printFormattedFloat (Print& out, double item) const = 0; + virtual size_t printFormattedString (Print& out, char* item) const = 0; + virtual size_t printFormattedPrintable(Print& out, Printable& item) const = 0; + + inline size_t printFormatted(print& out, unsigned long item) printFormattedInterger(out, (unsigned long) item); + // and other integer like objects + // idem for float, string, and Printable + // + // hm that will be rather messy... and our vtable has still 4 entries (mostly for John with the short surname) + // + // However, who needs such feature? Nobody, i.e. see discussion at the end. +}; +#endif // PrintFormatter_h ======================================================================================================== + +#define PrintFormatterV2_h // I am maybe onto something, still some compiler hiccups to be removed +#ifndef PrintFormatterV2_h // == PrintFormatter.h =================================================================================== +#define PrintFormatterV2_h + +template class PrintFormatter; + +// temporary replacement for Print in development cycle +class DevPrint +{ + // for test I replace this with Serial +//template size_t print(T_TYPE item, PrintFormatter thePrintFormatter) {return thePrintFormatter.printFormatted(this, item); } + template size_t print(T_TYPE item, PrintFormatter& thePrintFormatter) {return thePrintFormatter.printFormatted(this, item); } +} SerialDev; + +template class PrintFormatter +{ + public: + virtual size_t printFormatted (DevPrint& out, T_TYPE item) const = 0; +}; + +template +class MyGeneralFormatter : public PrintFormatter +{ + long nbase; + public: + MyGeneralFormatter(long base) : nbase(base){} + size_t printFormatted (DevPrint& out, T_TYPE item) const {return out.print(item%nbase);} +}; + +class MyExplicitLongFormatter : public PrintFormatter +{ + long nbase; + public: + MyExplicitLongFormatter(long base) : nbase(base){} + size_t printFormatted (DevPrint& out, long item) const {return out.print(item%nbase);} +}; + +void testPrintFormatter() +{ + SerialDev.print(0x12345678l, MyGeneralFormatter(13)); + SerialDev.print(0x12345678l, MyExplicitLongFormatter(13)); + SerialDev.print(12345678., MyGeneralFormatter(13)); + SerialDev.print(12345678., MyExplicitLongFormatter(13)); +} + + +#endif // PrintFormatter_h ======================================================================================================== + + +// still in development, to be published independently when more or less stable. +#ifndef PrintSequence_h // == PrintSequence.h ===================================================================================== +#define PrintSequence_h +class Print; +class EndSequence +{ + friend class PrintSequence; + virtual unsigned int printCloseString(Print& out) const { return 0;} +}; + +//TODO define derived class ClosedPrintSequence (which only has the method to Open() ), Alternative: use PrintSequence and OpenPrintSequence +class PrintSequence +{ +protected: + Print& myPrint; + PrintFormat myPrintFormat; + PrintFormat myDefaultPrintFormat; + int nchar; + virtual unsigned int printOpenString(Print& out) const { return 0;} + +public: + PrintSequence(Print& aPrint) : myPrint(aPrint), myDefaultPrintFormat(0), myPrintFormat(0) { Open(); } + void reset() { nchar=0; } + int end() { return nchar; } + + inline PrintSequence& Open() { nchar = printOpenString(myPrint); return *this; } + inline unsigned int operator<<(const EndSequence& item) { int nres = nchar+item.printCloseString(myPrint); nchar = 0; return nres; } + + inline PrintSequence& operator<<(const PrintFormat& format) { myPrintFormat = myPrintFormat & format; return *this; } + inline PrintSequence& operator<<(const IntegerFormat& format) { myPrintFormat = myPrintFormat & format; return *this; } + inline PrintSequence& operator<<(const FloatFormat& format) { myPrintFormat = myPrintFormat & format; return *this; } + inline PrintSequence& operator<<(const char item) { nchar += myPrint.print(item); myPrintFormat = myDefaultPrintFormat; return *this; } + inline PrintSequence& operator<<(const Printable& item) { nchar += myPrint.print(item); myPrintFormat = myDefaultPrintFormat; return *this; } + template + inline PrintSequence& operator<<(const R item) { nchar += myPrint.print(item, myPrintFormat); myPrintFormat = myDefaultPrintFormat; return *this; } +}; + +// define as weak so it can be overridden; +// PrintSequence Print::getPrintSequence() __attribute__((weak)); +PrintSequence Print::getPrintSequence() { return PrintSequence(*this); } + +#endif //PrintSequence_h ========================================================================================================== + + +#ifdef testCode + +#include +#include + +class HardwareSerial : public Print +{ + public: + virtual size_t write(uint8_t c) + { + printf("%c", c); + return 1; + } +} Serial; + +void printSequenceTeaser(); + +int main() +{ + printSequenceTeaser(); +} + +#include "Print.cpp" +#endif + + +#ifndef PrintSequenceTeaser_cpp // == PrintSequenceTeaser.cpp ===================================================================== +#define PrintSequenceTeaser_cpp + +void printSequenceTeaser() +{ + Serial.print(F("\nrunning PrintSequence teaser... ===========================================================================\n")); + + int n; + + PrintSequence mps(Serial); // alternative PrintSequence mps=Serial.getPrintSequence(); + Serial.println(F("n = mps<<\"Hi, one=\"<<1< inline friend IntegerFormat operator&(T_lhs lhs, T_rhs rhs) = delete; // default or delete +// template inline friend void operator&(T...) = delete // variadic templates +// +// use of SFINAE +http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error +http://stackoverflow.com/questions/982808/c-sfinae-examples (=== TypeSink example ====) +http://stackoverflow.com/questions/26220144/disable-default-template-and-only-use-specialization-through-sfinae +http://stackoverflow.com/questions/7986125/disabling-hiding-functions-in-a-template-based-on-compile-time-constants + +//hiding or unhiding members in derived classes (not sure this will work for constructors) +http://www.learncpp.com/cpp-tutorial/116-adding-changing-and-hiding-members-in-a-derived-class/ + +// what is explicit good for?? (http://programmers.stackexchange.com/questions/197893/why-are-constructors-not-inherited) + +can one access a base class from a derived class (http://www.cplusplus.com/forum/general/35681/) +class Derived : Base {} d; +d.base:: + + +http://stackoverflow.com/questions/1628768/why-does-an-overridden-function-in-the-derived-class-hide-other-overloads-of-the + + +c++ traits (see reference: http://www.cplusplus.com/reference/type_traits/is_integral/ + +disabling casts: http://www.gockelhut.com/c++/articles/disable_implicit_casts +void foo(uint32_t x); +// delete all implied one parameter foo functions: +template void foo(T) = delete; +//or in general, delete all implied foo functions +template void func_name(T...) = delete + +declare constructor as explicit could do? +*/ + +/* PrintSequence operator candidate list (for setting NextFormat and DefaultFormat, should have a lower precedence then operator<< +example && default format +& next format + +out && (HEX & FILLZEROS)<<42<<" 0x"<<39 & DEC << 39 + +http://en.cppreference.com/w/cpp/language/operator_precedence +Precedence Operator O Description Associativity +1 :: x Scope resolution Left-to-right +2 ++ -- Suffix/postfix increment and decrement + type() type{} ? Function-style type cast + () Function call + [] Array subscripting + . x Element selection by reference + -> Element selection through pointer +3 ++ -- Prefix increment and decrement Right-to-left + + - Unary plus and minus + ! ~ Logical NOT and bitwise NOT + (type) ? C-style type cast + * Indirection (dereference) + & Address-of + sizeof ? Size-of[note 1] + new, new[] Dynamic memory allocation + delete, delete[] Dynamic memory deallocation +4 .* x Pointer to member Left-to-right + ->* Pointer to member Left-to-right +5 * / % Multiplication, division, and remainder +6 + - Addition and subtraction +7 << >> Bitwise left shift and right shift +8 < <= For relational operators < and = respectively + > >= For relational operators > and = respectively +9 == != For relational = and ? respectively +10 & Bitwise AND +11 ^ Bitwise XOR (exclusive or) +12 | Bitwise OR (inclusive or) +13 && Logical AND +14 || Logical OR +15 ?: x Ternary conditional[note 2] Right-to-left + = Direct assignment (provided by default for C++ classes) + += -= Assignment by sum and difference + *= /= %= Assignment by product, quotient, and remainder + <<= >>= Assignment by bitwise left shift and right shift + &= ^= |= Assignment by bitwise AND, XOR, and OR +16 throw ? Throw operator (for exceptions) +17 , Comma Left-to-right + +DFDDFPS DefaultFormated_DefaultDefaultFormat_PrintSequence +SFDDFPS StringFormated_DefaultDefaultFormat_PrintSequence +IFDDFPS IntegerFormated_DefaultDefaultFormat_PrintSequence +FFDDFPS FloatFormated_DefaultDefaultFormat_PrintSequence + +DFDSFPS DefaultFormated_StringDefaultFormat_PrintSequence +SFDSFPS StringFormated_StringDefaultFormat_PrintSequence +IFDSFPS IntegerFormated_StringDefaultFormat_PrintSequence +FFDSFPS FloatFormated_StringDefaultFormat_PrintSequence + +DFDIFPS DefaultFormated_IntegerDefaultFormat_PrintSequence +SFDIFPS StringFormated_IntegerDefaultFormat_PrintSequence +IFDIFPS IntegerFormated_IntegerDefaultFormat_PrintSequence +FFDIFPS FloatFormated_IntegerDefaultFormat_PrintSequence + +DFDFFPS DefaultFormated_FloatDefaultFormat_PrintSequence +SFDFFPS StringFormated_FloatDefaultFormat_PrintSequence +IFDFFPS IntegerFormated_FloatDefaultFormat_PrintSequence +FFDFFPS FloatFormated_FloatDefaultFormat_PrintSequence + +operation < + printSequence operator<<(T_TYPE item); // output an item +} + +try this one out: + +mps <<"start" &HEX <<66 && myDefaultFormat <<" default set" &FIELDSIZE(5) &ALIGNLEFT <<36; + +<< & && .F() + + +mps << item <(ifsh); - size_t n = 0; - while (1) { - unsigned char c = pgm_read_byte(p++); - if (c == 0) break; - n += write(c); - } - return n; -} - -size_t Print::print(const String &s) -{ - return write(s.c_str(), s.length()); -} - -size_t Print::print(const char str[]) -{ - return write(str); -} - -size_t Print::print(char c) -{ - return write(c); -} - -size_t Print::print(unsigned char b, int base) -{ - return print((unsigned long) b, base); -} - -size_t Print::print(int n, int base) -{ - return print((long) n, base); -} - -size_t Print::print(unsigned int n, int base) -{ - return print((unsigned long) n, base); -} - -size_t Print::print(long n, int base) -{ - if (base == 0) { - return write(n); - } else if (base == 10) { - if (n < 0) { - int t = print('-'); - n = -n; - return printNumber(n, 10) + t; - } - return printNumber(n, 10); - } else { - return printNumber(n, base); - } -} - -size_t Print::print(unsigned long n, int base) -{ - if (base == 0) return write(n); - else return printNumber(n, base); -} - -size_t Print::print(double n, int digits) -{ - return printFloat(n, digits); -} - -size_t Print::println(const __FlashStringHelper *ifsh) -{ - size_t n = print(ifsh); - n += println(); - return n; -} - -size_t Print::print(const Printable& x) -{ - return x.printTo(*this); -} - -size_t Print::println(void) -{ - size_t n = print('\r'); - n += print('\n'); - return n; -} - -size_t Print::println(const String &s) -{ - size_t n = print(s); - n += println(); - return n; -} - -size_t Print::println(const char c[]) -{ - size_t n = print(c); - n += println(); - return n; -} - -size_t Print::println(char c) -{ - size_t n = print(c); - n += println(); - return n; -} - -size_t Print::println(unsigned char b, int base) -{ - size_t n = print(b, base); - n += println(); - return n; -} - -size_t Print::println(int num, int base) -{ - size_t n = print(num, base); - n += println(); - return n; -} - -size_t Print::println(unsigned int num, int base) -{ - size_t n = print(num, base); - n += println(); - return n; -} - -size_t Print::println(long num, int base) -{ - size_t n = print(num, base); - n += println(); - return n; -} - -size_t Print::println(unsigned long num, int base) -{ - size_t n = print(num, base); - n += println(); - return n; -} - -size_t Print::println(double num, int digits) -{ - size_t n = print(num, digits); - n += println(); - return n; -} - -size_t Print::println(const Printable& x) -{ - size_t n = print(x); - n += println(); - return n; -} - -// Private Methods ///////////////////////////////////////////////////////////// - -size_t Print::printNumber(unsigned long n, uint8_t base) { - char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte. - char *str = &buf[sizeof(buf) - 1]; - - *str = '\0'; - - // prevent crash if called with base == 1 - if (base < 2) base = 10; - - do { - unsigned long m = n; - n /= base; - char c = m - base * n; - *--str = c < 10 ? c + '0' : c + 'A' - 10; - } while(n); - - return write(str); -} - -size_t Print::printFloat(double number, uint8_t digits) -{ - size_t n = 0; - - if (isnan(number)) return print("nan"); - if (isinf(number)) return print("inf"); - if (number > 4294967040.0) return print ("ovf"); // constant determined empirically - if (number <-4294967040.0) return print ("ovf"); // constant determined empirically - - // Handle negative numbers - if (number < 0.0) - { - n += print('-'); - number = -number; - } - - // Round correctly so that print(1.999, 2) prints as "2.00" - double rounding = 0.5; - for (uint8_t i=0; i 0) { - n += print("."); - } - - // Extract digits from the remainder one at a time - while (digits-- > 0) - { - remainder *= 10.0; - int toPrint = int(remainder); - n += print(toPrint); - remainder -= toPrint; - } - - return n; -} +/* Print.cpp copyright notice + + Copyright (c) 2015 Michael Jonker. All right reserved. + + "THE BEER-WARE LICENSE" (Revision 42++) http://en.wikipedia.org/wiki/Beerware + Michael Jonker wrote this file. + + As long as you retain this notice you can can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. + + This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + For a copy of the GNU Lesser General Public License write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + + This code replaces the version of David A. Mellis. +*/ + +/* Print.cpp short description + this code provides the implementation of the non trivial methods of the Print class. +*/ + +#define _define_static_PrintFormat_instances // force declaration of static PrintFormat instances here (in PrintFromat.h included from Print.h) + +#include "Print.h" +#include + +// == Public Methods ============================================================================================================== + +/* default implementation: may be overridden */ +size_t Print::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + while (size--) { n += write(*buffer++); } + return n; +} + +// these basic print method are declared but not defined in the header file. The space penalty of inlineing a call to a virtual method not negligible. +size_t Print::print(char item) { return write((uint8_t)item);} +size_t Print::print(const char* item, size_t size) { return write((const uint8_t *)item, size); } + +size_t Print::print(const __FlashStringHelper *ifsh) +{ + PGM_P p = reinterpret_cast(ifsh); + size_t n = 0; + while (1) { + unsigned char c = pgm_read_byte(p++); + if (c == 0) break; + n += write(c); + } + return n; +} + +// == Private Methods ============================================================================================================= + +size_t Print::printString(const char* cstr, unsigned int size, unsigned int formatControl) +{ + unsigned int nchar = 0; + + // field size handling + int fillLen = (formatControl&PrintFormat::m_FieldSizeMask) -size; + + // sign handling + char sign; + if ( (formatControl&PrintFormat::m_IsNegative) ) sign = '-'; // negative signed value (always add '-' sign) + else if (!(formatControl&PrintFormat::m_ForceSign) ) sign = '\0'; // no forced sign + else if ( *cstr == '0' ) sign = ' '; // forced signed and zero value (add a space ) + else sign = '+'; // forced signed and non-zero value (add a '+' sign) + + if(formatControl&PrintFormat::m_ForceSign) fillLen++; // with forcedSign we get one position more + // reduce the fill length if we add sign symbol + if(sign) fillLen--; + + + unsigned char fillControl = formatControl&(PrintFormat::m_FillZeros|PrintFormat::m_AlignLeft); + // if we have to fill spaces, fill spaces + if(fillControl == 0) nchar+=printPadding(' ', fillLen); + + // if there is a sign symbol add the sign + if(sign) nchar += write(sign); + + // if we have to fill zeros, fill zeros + if( fillControl == PrintFormat::m_FillZeros) nchar+=printPadding('0', fillLen); + + // write what needs to be written + nchar += write((const uint8_t *)cstr, size); + + // post fill whatever space is left with whatever is needed + if( fillControl == (PrintFormat::m_AlignLeft|PrintFormat::m_FillZeros)) nchar+=printPadding('-', fillLen); + if( fillControl == (PrintFormat::m_AlignLeft )) nchar+=printPadding(' ', fillLen); + + return nchar; +} + +size_t Print::printNumber(unsigned long n, unsigned int formatControl) +{ + // special cases + // if (base == 0) return write(n); // binary output ==> breaking backward compatibility + unsigned char base = ((formatControl>>8) & (PrintFormat::m_RadixMask>>8))+1; + if (base == 1) base = 10; // default value is decimal (also prevent infinite loop) + + char buf[8 * sizeof(long) + 2]; // Assumes maximum of 8 characters per byte (binary radix ) + sign character plus + termination byte. + char *str = &buf[sizeof(buf) - 1]; + + *str = '\0'; + + do { // loop to print the number + unsigned long m = n; + n /= base; + char c = m - base * n; + *--str = c < 10 ? c + '0' : c + 'A' - 10; + } while(n); + + // field size handling + int8_t fillLen = (formatControl&PrintFormat::m_FieldSizeMask) - (&buf[sizeof(buf)-1] - str); + + if((formatControl&(PrintFormat::m_IsNegative|PrintFormat::m_ForceSign)) == PrintFormat::m_IsNegative) fillLen--; + if(formatControl&PrintFormat::m_StrictSize && fillLen<0) + { + str -= fillLen; + *str = '#'; + } + + return printString(&str[0], &buf[sizeof(buf)-1] - str, formatControl); +} + + +unsigned long& asLong (const float& aFloat) { return (unsigned long&) aFloat;} // interpret a float as long +float& asFloat(const unsigned long& aLong) { return (float&) aLong; } // interpret a long as float + +size_t Print::printFloat(double number, unsigned int formatControl) +{ + size_t nc = 0; + +#ifdef slow_code // use this on machines where a double has more than 4 bytes +// if(sizeof(double) >4) +// if negative, mark as negative and make value positive + if (number<0) { number = -number; formatControl |= PrintFormat::m_IsNegative; } // Mark as negative item if negative + +#else + +// if negative, mark as negative and make value positive (irrespective of nan ovf ...) + if (asLong((float)number)&0x80000000) { number = asFloat(asLong((float)number) & ~0x80000000); formatControl |= PrintFormat::m_IsNegative; } // Mark as negative item if negative + // note: the more elegant alternative asLong(number) = asLong((float)number) & ~0x80000000; // only works when a double equals a float (like on the Arduino, but not while emulating) +#endif + + unsigned char digits = ((formatControl>>8) & (PrintFormat::m_PrecisionMask>>8)); + if(digits) digits--; + + // TODO write some fast code testing for nan: (asLong(number) == 0x7F800000) and inf: ((asLong(number) & 0x7F800000) == 0x7F800000) + float ovfValue = float(0xFFFFFF00u); // constant determined by reasoning ;-) + char* specialNumber = NULL; + if (isnan(number)) specialNumber=(char*) "nan"; + else if (isinf(number)) specialNumber=(char*) "inf"; + else if (float(number) > ovfValue) specialNumber=(char*) "ovf"; + + if(specialNumber) + { + nc = printString(specialNumber, 3, formatControl); + digits++; + nc += printPadding(' ', digits); + return nc; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + double rounding = 0.5; + for (uint8_t i=0; i>1; + if(!digitMask) break; + factor *= factor; + } + } + } + */ + + number = (float)(number +rounding); // cast to float to test behaviour on machines were double is double (and "ca ne mange pas de pain" on the Arduino) + + // Extract the integer part of the number and print it + unsigned long int_part = (unsigned long)number; + + double remainder = number - (double)int_part; + + formatControl = (formatControl &~PrintFormat::m_PrecisionMask) | ((10-1)<<8); // replace precision with radix 10 + nc += printNumber(int_part, formatControl); + + // Print the decimal point, always! // changed!! but only if there are digits beyond + nc += write('.'); + + // Extract digits from the remainder one at a time + while (digits-- > 0) + { + remainder *= 10.0; + unsigned char toPrint = int(remainder); + nc += write('0' + toPrint); + remainder -= toPrint; + } + + return nc; +} + +//Print_cpp ======================================================================================================================= diff --git a/hardware/arduino/avr/cores/arduino/Print.h b/hardware/arduino/avr/cores/arduino/Print.h index 7b53aa4d17e..088723696a8 100644 --- a/hardware/arduino/avr/cores/arduino/Print.h +++ b/hardware/arduino/avr/cores/arduino/Print.h @@ -1,84 +1,160 @@ -/* - Print.h - Base class that provides print() and println() - Copyright (c) 2008 David A. Mellis. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef Print_h -#define Print_h - -#include -#include // for size_t - -#include "WString.h" -#include "Printable.h" - -#define DEC 10 -#define HEX 16 -#define OCT 8 -#define BIN 2 - -class Print -{ - private: - int write_error; - size_t printNumber(unsigned long, uint8_t); - size_t printFloat(double, uint8_t); - protected: - void setWriteError(int err = 1) { write_error = err; } - public: - Print() : write_error(0) {} - - int getWriteError() { return write_error; } - void clearWriteError() { setWriteError(0); } - - virtual size_t write(uint8_t) = 0; - size_t write(const char *str) { - if (str == NULL) return 0; - return write((const uint8_t *)str, strlen(str)); - } - virtual size_t write(const uint8_t *buffer, size_t size); - size_t write(const char *buffer, size_t size) { - return write((const uint8_t *)buffer, size); - } - - size_t print(const __FlashStringHelper *); - size_t print(const String &); - size_t print(const char[]); - size_t print(char); - size_t print(unsigned char, int = DEC); - size_t print(int, int = DEC); - size_t print(unsigned int, int = DEC); - size_t print(long, int = DEC); - size_t print(unsigned long, int = DEC); - size_t print(double, int = 2); - size_t print(const Printable&); - - size_t println(const __FlashStringHelper *); - size_t println(const String &s); - size_t println(const char[]); - size_t println(char); - size_t println(unsigned char, int = DEC); - size_t println(int, int = DEC); - size_t println(unsigned int, int = DEC); - size_t println(long, int = DEC); - size_t println(unsigned long, int = DEC); - size_t println(double, int = 2); - size_t println(const Printable&); - size_t println(void); -}; - -#endif +/* Print.h copyright notice + + Copyright (c) 2015 Michael Jonker. All right reserved. + + "THE BEER-WARE LICENSE" (Revision 42++) http://en.wikipedia.org/wiki/Beerware + Michael Jonker wrote this file. + + As long as you retain this notice you can can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. + + This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + For a copy of the GNU Lesser General Public License write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + + This code replaces the version of David A. Mellis. +*/ + +/* Print.h short description + this code defines the Print class and provides the implementation of the trivial methods of this class. +*/ + +#ifndef Print_h // == Print.h ===================================================================================================== +#define Print_h + +#include +#include + +#include "WString.h" +#include "Printable.h" +#include "PrintFormat.h" +namespace ArduinoPrint +{ + static const PrintFormat FIELDSIZE(unsigned char number) { return PrintFormat::FIELDSIZE(number); } + static const IntegerFormat RADIX (unsigned char number) { return IntegerFormat::RADIX (number); } + static const FloatFormat PRECISION(unsigned char number) { return FloatFormat::PRECISION(number); } + + static const PrintFormat STRICTSIZE = PrintFormat::STRICTSIZE; + static const PrintFormat FILLZEROS = PrintFormat::FILLZEROS; + static const PrintFormat FORCESIGN = PrintFormat::FORCESIGN; + static const IntegerFormat ALIGNLEFT = IntegerFormat::ALIGNLEFT; + static const IntegerFormat BIN = RADIX( 2); + static const IntegerFormat OCT = RADIX( 8); + static const IntegerFormat DEC = RADIX(10); + static const IntegerFormat HEX = RADIX(16); + +// these are defined in some standard include file... + static const char NEWLINE = '\n'; + static const char endl = '\n'; // defined in: #include ; using namespace std; +}; +using namespace ArduinoPrint; + +class PrintSequence; +class Print +{ + private: + int write_error; + size_t printString (const char* value, unsigned int size, unsigned int formatControl); + size_t printNumber (unsigned long value, unsigned int formatControl); + size_t printFloat (double value, unsigned int formatControl); + size_t printExpon (double value, unsigned int formatControl); + inline size_t printSignedNumber(signed long value, unsigned int formatControl) { if(value<0) { value=-value; formatControl |= PrintFormat::m_IsNegative; } + return printNumber((unsigned long)value, formatControl); } + + + protected: + inline void setWriteError(int err = 1) { write_error = err; } + + public: + + inline Print() : write_error(0) {} + + inline int getWriteError() { return write_error; } + inline void clearWriteError() { setWriteError(0); } + + inline size_t printPadding(char value, unsigned char length) + { + for(unsigned char nc = length; nc!=0; nc--) write(value); + return length; + } + inline size_t printPattern(char* value, unsigned char length, unsigned char offset=0) + { + char* cp = value+offset; + for(unsigned char nc = length; nc!=0; nc--) { write(*cp); cp = *(cp+1) ? cp+1 : value; } + return length; + } + inline size_t printPadding(char value, signed char length ) { return length>0 ? printPadding(value, (unsigned char) length ) : 0; } + inline size_t printPadding(char value, signed int length ) { return length>0 ? printPadding(value, (unsigned char) length ) : 0; } + inline size_t printPadding(char value, unsigned int length ) { return printPadding(value, (unsigned char) length ) ; } + inline size_t printPattern(char* pattern, signed char length, unsigned char offset=0) { return length>0 ? printPattern(pattern, (unsigned char) length, offset) : 0; } + inline size_t printPattern(char* pattern, signed int length, unsigned char offset=0) { return length>0 ? printPattern(pattern, (unsigned char) length, offset) : 0; } + inline size_t printPattern(char* pattern, unsigned int length, unsigned char offset=0) { return printPattern(pattern, (unsigned char) length, offset) ; } + inline size_t printPattern(const String& pattern, signed char length, unsigned char offset=0) { return length>0 ? printPattern(pattern.c_str(), (unsigned char) length, offset) : 0; } + inline size_t printPattern(const String& pattern, signed int length, unsigned char offset=0) { return length>0 ? printPattern(pattern.c_str(), (unsigned char) length, offset) : 0; } + inline size_t printPattern(const String& pattern, unsigned int length, unsigned char offset=0) { return printPattern(pattern.c_str(), (unsigned char) length, offset) ; } + // TODO simply with templates + + PrintSequence getPrintSequence(); + +// virtual methods that must/can be provided by the extending class + virtual size_t write(uint8_t) = 0; // pure virtual, must be provided + virtual size_t write(const uint8_t *buffer, size_t size); // locally defined, can be overridden + +// these methods are not inlined as the space penalty for inlining a virtual method call is non negligible + size_t print(char item); + size_t print(const char* item, size_t size); + size_t print(const __FlashStringHelper* item); + +// all other print methods are inlined calls to others print() methods or to Print private methods. + + inline size_t print(uint8_t* item, size_t size, const PrintFormat& format=PrintFormat::DEFAULTPF) { return printString((char*)item, size, format.getFormatControl()); } + inline size_t print(const char* item, size_t size, const PrintFormat& format=PrintFormat::DEFAULTPF) { return printString(item, size, format.getFormatControl()); } + inline size_t print(const char* item, const PrintFormat& format=PrintFormat::DEFAULTPF) { return printString(item, strlen(item), format.getFormatControl()); } + inline size_t print(const String& item, const PrintFormat& format=PrintFormat::DEFAULTPF) { return printString(item.c_str(), item.length(), format.getFormatControl()); } + //TODO fix make a printFlashString(item, format) + inline size_t print(const __FlashStringHelper* item, const PrintFormat& format/*=PrintFormat::DEFAULTPF */) { return print(item); } + + //TODO templatify (if possible) + inline size_t print(unsigned char item, const IntegerFormat& format=DEC) { return printNumber((unsigned long)item, format.getFormatControl() ); } + inline size_t print(unsigned int item, const IntegerFormat& format=DEC) { return printNumber((unsigned long)item, format.getFormatControl() ); } + inline size_t print(unsigned long item, const IntegerFormat& format=DEC) { return printNumber((unsigned long)item, format.getFormatControl() ); } + inline size_t print( int item, const IntegerFormat& format=DEC) { return printSignedNumber( (long)item, format.getFormatControl() ); } + inline size_t print( long item, const IntegerFormat& format=DEC) { return printSignedNumber( (long)item, format.getFormatControl() ); } + inline size_t print( double item, const FloatFormat& format=PRECISION(2)) { return printFloat(item, format.getFormatControl() ); } + + inline size_t print(unsigned char item, const PrintFormat& format ) { return printNumber((unsigned long)item, format.getFormatControl() ); } + inline size_t print(unsigned int item, const PrintFormat& format ) { return printNumber((unsigned long)item, format.getFormatControl() ); } + inline size_t print(unsigned long item, const PrintFormat& format ) { return printNumber((unsigned long)item, format.getFormatControl() ); } + inline size_t print( int item, const PrintFormat& format ) { return printSignedNumber( (long)item, format.getFormatControl() ); } + inline size_t print( long item, const PrintFormat& format ) { return printSignedNumber( (long)item, format.getFormatControl() ); } + inline size_t print( double item, const PrintFormat& format ) { return printFloat(item, format.getFormatControl() ); } + + inline size_t print(const Printable& item) { return item.printTo(*this);} + + // backward compatible calls: + inline size_t print(unsigned char item, unsigned char radix) { return printNumber((unsigned long)item, RADIX(radix).getFormatControl() ); } + inline size_t print(unsigned int item, unsigned char radix) { return printNumber((unsigned long)item, RADIX(radix).getFormatControl() ); } + inline size_t print(unsigned long item, unsigned char radix) { return printNumber((unsigned long)item, RADIX(radix).getFormatControl() ); } + inline size_t print( int item, unsigned char radix) { return printSignedNumber( (long)item, RADIX(radix).getFormatControl() ); } + inline size_t print( long item, unsigned char radix) { return printSignedNumber( (long)item, RADIX(radix).getFormatControl() ); } + inline size_t print( double item, unsigned char digits) { return printFloat(item, PRECISION(digits).getFormatControl());} + +// define println() methods by template + inline size_t println(void) { return print('\n'); } + template inline size_t println(R item ) { return print(item) + print('\n'); } + template inline size_t println(R item, const PrintFormat& format ) { return print(item, format) + print('\n'); } + template inline size_t println(R item, const IntegerFormat& format ) { return print(item, format) + print('\n'); } + template inline size_t println(R item, const FloatFormat& format ) { return print(item, format) + print('\n'); } + template inline size_t println(R item, unsigned char extra) { return print(item, extra) + print('\n'); } +// TODO: see if we can remove the first templates by giving a default for PrintFormat without making print() ambiguous for R=char*, ... + +}; + +#endif //Print_h ================================================================================================================== + +// eof \ No newline at end of file diff --git a/hardware/arduino/avr/cores/arduino/PrintFormat.h b/hardware/arduino/avr/cores/arduino/PrintFormat.h new file mode 100644 index 00000000000..a11022fb93e --- /dev/null +++ b/hardware/arduino/avr/cores/arduino/PrintFormat.h @@ -0,0 +1,129 @@ +/* PrintFormat.h copyright notice + + Copyright (c) 2015 Michael Jonker. All right reserved. + + "THE BEER-WARE LICENSE" (Revision 42++) http://en.wikipedia.org/wiki/Beerware + Michael Jonker wrote this file. + + As long as you retain this notice you can can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. + + This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + For a copy of the GNU Lesser General Public License write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +*/ + +/* PrintFormat.h short description + This code defines the classes and constant class instances for controlling the print output format, as used by the Print class +*/ + +#ifndef PrintFormat_h +#define PrintFormat_h + +class PrintFormat +{ +friend class Print; +friend class PrintSequence; + +protected: + unsigned int myFormatControl; + + static const unsigned char m_FieldSizeMask = 0x1f; /* contains the field width -1 of the entry to be printed */ + static const unsigned char m_StrictSize = 0x20; /* if set, the field width is strict and shall not be exceeded */ + static const unsigned char m_AlignLeft = 0x40; /* if set, left align the field by adding the padding characters to the right */ + static const unsigned char m_FillZeros = 0x80; /* if set, fill with '0' instead of spaces */ + + static const unsigned int m_RadixMask = 0x1f00; /* gives the radix -1 of the entry to be printed IntegerFormat only) */ + static const unsigned int m_PrecisionMask = 0x1f00; /* gives the precision -1 of the entry to be printed (FloatFormat only) */ + static const unsigned int m_Reserved = 0x2000; /* reserved (possibly to mark negated option) */ + static const unsigned int m_ForceSign = 0x4000; /* if set, force sign symbol, including for positive values */ + static const unsigned int m_IsNegative = 0x8000; /* if set, the value shall be interpreted as a signed item (reserved for internal use) */ + + // define a format control merging constructor. For non-null FieldSize or Radix/Precision values, the right hand size values override the left hand size values; + PrintFormat(unsigned int lhs, unsigned int rhs) : + myFormatControl( lhs // get the right hand side + & ~((rhs & m_FieldSizeMask) ? m_FieldSizeMask : 0) // if rhs has a non-zero FieldSize value , wipe out the fieldSize from the lhs + & ~((rhs & m_RadixMask ) ? m_RadixMask : 0) // idem for Radix/Precision + | rhs // merge with the lefthand side + ) {} + + PrintFormat(unsigned int aFormatControl) : myFormatControl(aFormatControl) {} + +public: + + unsigned int getFormatControl() const { return myFormatControl; } + + inline friend PrintFormat operator&(const PrintFormat& lhs, const PrintFormat& rhs) { return PrintFormat(lhs.myFormatControl, rhs.myFormatControl); } + + static const PrintFormat FIELDSIZE(unsigned char number) { return PrintFormat( number & m_FieldSizeMask); } + static const PrintFormat STRICTSIZE; + static const PrintFormat FILLZEROS; + static const PrintFormat FORCESIGN; + static const PrintFormat DEFAULTPF; +}; + + +class IntegerFormat : PrintFormat +{ +friend class Print; +friend class PrintSequence; + IntegerFormat(unsigned int aFormatControl) : PrintFormat(aFormatControl) {} + IntegerFormat(unsigned int lhs, unsigned int rhs) : PrintFormat(lhs, rhs) {} +public: + // todo try to formulate this more concise + inline friend IntegerFormat operator&(const IntegerFormat& lhs, const IntegerFormat& rhs) { return IntegerFormat(lhs.getFormatControl(), rhs.getFormatControl() ); } + inline friend IntegerFormat operator&(const IntegerFormat& lhs, const PrintFormat& rhs) { return IntegerFormat(lhs.getFormatControl(), rhs.getFormatControl() ); } + inline friend IntegerFormat operator&(const PrintFormat& lhs, const IntegerFormat& rhs) { return IntegerFormat(lhs.getFormatControl(), rhs.getFormatControl() ); } + + using PrintFormat::getFormatControl; + + static const IntegerFormat RADIX(unsigned char number) { return IntegerFormat(number ? ((number-1)&(m_RadixMask>>8))<<8 : 0); } // bit truncation (wrapping) +//static const IntegerFormat RADIX(unsigned char number) { return IntegerFormat(number ? (number>(m_RadixMask>>8) ? m_RadixMask : (number-1)<<8) : 0); } // min/max limitation + static const IntegerFormat ALIGNLEFT; +}; + + +class FloatFormat : PrintFormat +{ +friend class Print; +friend class PrintSequence; + FloatFormat(unsigned int aFormatControl) : PrintFormat(aFormatControl) {} + FloatFormat(unsigned int lhs, unsigned int rhs) : PrintFormat(lhs, rhs) {} +public: + inline friend FloatFormat operator&(const FloatFormat& lhs, const FloatFormat& rhs) { return FloatFormat(lhs.getFormatControl(), rhs.getFormatControl() ); } + inline friend FloatFormat operator&(const FloatFormat& lhs, const PrintFormat& rhs) { return FloatFormat(lhs.getFormatControl(), rhs.getFormatControl() ); } + inline friend FloatFormat operator&(const PrintFormat& lhs, const FloatFormat& rhs) { return FloatFormat(lhs.getFormatControl(), rhs.getFormatControl() ); } + + using PrintFormat::getFormatControl; + + static const FloatFormat PRECISION(unsigned char number) { return FloatFormat(number>=(m_PrecisionMask>>8) ? m_PrecisionMask : (number+1)<<8); } +}; + + +#ifdef trap_invalid_PrintFormat_operation +// Define a 'sink' to capture all invalid PrintFormat operations to make error messages more readable. +// First attempt to set the scene: (However, this limited implementation makes the compiler error messages even more confusing). +// TODO extend further, use templates. + class invalid_PrintFormat_operation{}; + inline const invalid_PrintFormat_operation operator& (const IntegerFormat& lhs, const FloatFormat& rhs); + inline const invalid_PrintFormat_operation operator& (const FloatFormat& lhs, const IntegerFormat& rhs); +#endif + +#ifdef _define_static_PrintFormat_instances +// this is my way (and choice) to keep implementation and definition in the same file. +// the macro _define_static_PrintFormat_instances is #defined in one .cpp file (either PrintFormat.cpp or Print.cpp) before the #include of this header file. +const PrintFormat PrintFormat::STRICTSIZE(m_StrictSize); +const PrintFormat PrintFormat::FILLZEROS (m_FillZeros); +const PrintFormat PrintFormat::FORCESIGN (m_ForceSign); +const PrintFormat PrintFormat::DEFAULTPF (0); +const IntegerFormat IntegerFormat::ALIGNLEFT (m_AlignLeft); +#endif // _define_static_PrintFormat_instances + +#endif // PrintFormat_h =========================================================================================================== +