From 9afa05e2f4cd8f2554acf4e98dd6adceac2cbded Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 22 Apr 2017 11:33:40 +0200 Subject: [PATCH] Removed `Print` class and converted `printTo()` to a template method (issue #276) --- CHANGELOG.md | 2 + .../IndentedPrintExample.ino | 35 -------------- src/ArduinoJson/Data/Encoding.hpp | 2 - src/ArduinoJson/JsonVariant.hpp | 4 +- src/ArduinoJson/Print.hpp | 44 ----------------- src/ArduinoJson/Serialization/DummyPrint.hpp | 10 ++-- .../Serialization/DynamicStringBuilder.hpp | 13 +++-- .../Serialization/IndentedPrint.hpp | 18 ++++--- .../Serialization/JsonPrintable.hpp | 21 +++++--- .../Serialization/JsonSerializer.hpp | 11 +++-- .../Serialization/JsonSerializerImpl.hpp | 25 ++++++---- src/ArduinoJson/Serialization/JsonWriter.hpp | 5 +- src/ArduinoJson/Serialization/Prettyfier.hpp | 48 +++++++++++-------- .../Serialization/StaticStringBuilder.hpp | 30 ++++++------ .../Serialization/StreamPrintAdapter.hpp | 13 +++-- src/ArduinoJson/StringTraits/StdString.hpp | 4 ++ test/JsonWriter/writeFloat.cpp | 2 +- test/JsonWriter/writeString.cpp | 2 +- test/Misc/StringBuilder.cpp | 31 ++++++++---- 19 files changed, 147 insertions(+), 173 deletions(-) delete mode 100644 examples/IndentedPrintExample/IndentedPrintExample.ino delete mode 100644 src/ArduinoJson/Print.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index fa92bf24..995bfc32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ HEAD * Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)` * Renamed folder `include/` to `src/` * Fixed warnings `floating constant exceeds range of float`and `floating constant truncated to zero` (issue #483) +* Removed `Print` class and converted `printTo()` to a template method (issue #276) +* Removed example `IndentedPrintExample.ino` v5.8.4 ------ diff --git a/examples/IndentedPrintExample/IndentedPrintExample.ino b/examples/IndentedPrintExample/IndentedPrintExample.ino deleted file mode 100644 index 711360f9..00000000 --- a/examples/IndentedPrintExample/IndentedPrintExample.ino +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#include - -using namespace ArduinoJson::Internals; - -void setup() { - Serial.begin(9600); - while (!Serial) { - // wait serial port initialization - } - - IndentedPrint serial(Serial); - serial.setTabSize(4); - - serial.println("This is at indentation 0"); - serial.indent(); - serial.println("This is at indentation 1"); - serial.println("This is also at indentation 1"); - serial.indent(); - serial.println("This is at indentation 2"); - - serial.unindent(); - serial.unindent(); - serial.println("This is back at indentation 0"); -} - -void loop() { - // not used in this example -} diff --git a/src/ArduinoJson/Data/Encoding.hpp b/src/ArduinoJson/Data/Encoding.hpp index 158ee07d..dba785aa 100644 --- a/src/ArduinoJson/Data/Encoding.hpp +++ b/src/ArduinoJson/Data/Encoding.hpp @@ -7,8 +7,6 @@ #pragma once -#include "../Print.hpp" - namespace ArduinoJson { namespace Internals { diff --git a/src/ArduinoJson/JsonVariant.hpp b/src/ArduinoJson/JsonVariant.hpp index 93772961..93abcd76 100644 --- a/src/ArduinoJson/JsonVariant.hpp +++ b/src/ArduinoJson/JsonVariant.hpp @@ -40,8 +40,8 @@ class JsonObject; // - a string (const char*) // - a reference to a JsonArray or JsonObject class JsonVariant : public JsonVariantBase { - friend void Internals::JsonSerializer::serialize(const JsonVariant &, - JsonWriter &); + template + friend class Internals::JsonSerializer; public: // Creates an uninitialized JsonVariant diff --git a/src/ArduinoJson/Print.hpp b/src/ArduinoJson/Print.hpp deleted file mode 100644 index cfb0db6d..00000000 --- a/src/ArduinoJson/Print.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -#ifndef ARDUINO - -#include -#include - -namespace ArduinoJson { -// This class reproduces Arduino's Print class -class Print { - public: - virtual ~Print() {} - - virtual size_t write(uint8_t) = 0; - - size_t print(const char* s) { - size_t n = 0; - while (*s) { - n += write(static_cast(*s++)); - } - return n; - } - - size_t println() { - size_t n = 0; - n += write('\r'); - n += write('\n'); - return n; - } -}; -} - -#else - -#include - -#endif diff --git a/src/ArduinoJson/Serialization/DummyPrint.hpp b/src/ArduinoJson/Serialization/DummyPrint.hpp index 43cfce0f..656aa9ed 100644 --- a/src/ArduinoJson/Serialization/DummyPrint.hpp +++ b/src/ArduinoJson/Serialization/DummyPrint.hpp @@ -7,17 +7,19 @@ #pragma once -#include "../Print.hpp" - namespace ArduinoJson { namespace Internals { // A dummy Print implementation used in JsonPrintable::measureLength() -class DummyPrint : public Print { +class DummyPrint { public: - virtual size_t write(uint8_t) { + size_t print(char) { return 1; } + + size_t print(const char* s) { + return strlen(s); + } }; } } diff --git a/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp b/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp index c4c39b84..a344588d 100644 --- a/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp +++ b/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp @@ -7,7 +7,6 @@ #pragma once -#include "../Print.hpp" #include "../StringTraits/StringTraits.hpp" namespace ArduinoJson { @@ -15,15 +14,21 @@ namespace Internals { // A Print implementation that allows to write in a String template -class DynamicStringBuilder : public Print { +class DynamicStringBuilder { public: DynamicStringBuilder(TString &str) : _str(str) {} - virtual size_t write(uint8_t c) { - StringTraits::append(_str, static_cast(c)); + size_t print(char c) { + StringTraits::append(_str, c); return 1; } + size_t print(const char *s) { + size_t initialLen = _str.length(); + StringTraits::append(_str, s); + return _str.length() - initialLen; + } + private: DynamicStringBuilder &operator=(const DynamicStringBuilder &); diff --git a/src/ArduinoJson/Serialization/IndentedPrint.hpp b/src/ArduinoJson/Serialization/IndentedPrint.hpp index 6995e4fa..66ca6ac2 100644 --- a/src/ArduinoJson/Serialization/IndentedPrint.hpp +++ b/src/ArduinoJson/Serialization/IndentedPrint.hpp @@ -7,15 +7,14 @@ #pragma once -#include "../Print.hpp" - namespace ArduinoJson { namespace Internals { // Decorator on top of Print to allow indented output. // This class is used by JsonPrintable::prettyPrintTo() but can also be used // for your own purpose, like logging. -class IndentedPrint : public Print { +template +class IndentedPrint { public: explicit IndentedPrint(Print &p) : sink(&p) { level = 0; @@ -23,14 +22,21 @@ class IndentedPrint : public Print { isNewLine = true; } - virtual size_t write(uint8_t c) { + size_t print(char c) { size_t n = 0; if (isNewLine) n += writeTabs(); - n += sink->write(c); + n += sink->print(c); isNewLine = c == '\n'; return n; } + size_t print(const char *s) { + // TODO: optimize + size_t n = 0; + while (*s) n += print(*s++); + return n; + } + // Adds one level of indentation void indent() { if (level < MAX_LEVEL) level++; @@ -54,7 +60,7 @@ class IndentedPrint : public Print { size_t writeTabs() { size_t n = 0; - for (int i = 0; i < level * tabSize; i++) n += sink->write(' '); + for (int i = 0; i < level * tabSize; i++) n += sink->print(' '); return n; } diff --git a/src/ArduinoJson/Serialization/JsonPrintable.hpp b/src/ArduinoJson/Serialization/JsonPrintable.hpp index f215a8d0..f33e5584 100644 --- a/src/ArduinoJson/Serialization/JsonPrintable.hpp +++ b/src/ArduinoJson/Serialization/JsonPrintable.hpp @@ -31,9 +31,12 @@ namespace Internals { template class JsonPrintable { public: - size_t printTo(Print &print) const { - JsonWriter writer(print); - JsonSerializer::serialize(downcast(), writer); + template + typename TypeTraits::EnableIf::value, + size_t>::type + printTo(Print &print) const { + JsonWriter writer(print); + JsonSerializer >::serialize(downcast(), writer); return writer.bytesWritten(); } @@ -62,8 +65,9 @@ class JsonPrintable { return printTo(sb); } - size_t prettyPrintTo(IndentedPrint &print) const { - Prettyfier p(print); + template + size_t prettyPrintTo(IndentedPrint &print) const { + Prettyfier p(print); return printTo(p); } @@ -77,8 +81,11 @@ class JsonPrintable { return prettyPrintTo(buffer, N); } - size_t prettyPrintTo(Print &print) const { - IndentedPrint indentedPrint = IndentedPrint(print); + template + typename TypeTraits::EnableIf::value, + size_t>::type + prettyPrintTo(Print &print) const { + IndentedPrint indentedPrint(print); return prettyPrintTo(indentedPrint); } diff --git a/src/ArduinoJson/Serialization/JsonSerializer.hpp b/src/ArduinoJson/Serialization/JsonSerializer.hpp index 19d4c39a..86ed73ee 100644 --- a/src/ArduinoJson/Serialization/JsonSerializer.hpp +++ b/src/ArduinoJson/Serialization/JsonSerializer.hpp @@ -20,14 +20,15 @@ class JsonVariant; namespace Internals { +template class JsonSerializer { public: - static void serialize(const JsonArray &, JsonWriter &); - static void serialize(const JsonArraySubscript &, JsonWriter &); - static void serialize(const JsonObject &, JsonWriter &); + static void serialize(const JsonArray &, Writer &); + static void serialize(const JsonArraySubscript &, Writer &); + static void serialize(const JsonObject &, Writer &); template - static void serialize(const JsonObjectSubscript &, JsonWriter &); - static void serialize(const JsonVariant &, JsonWriter &); + static void serialize(const JsonObjectSubscript &, Writer &); + static void serialize(const JsonVariant &, Writer &); }; } } diff --git a/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp b/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp index eddddad9..63b2c3f3 100644 --- a/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp +++ b/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp @@ -14,8 +14,9 @@ #include "../JsonVariant.hpp" #include "JsonSerializer.hpp" -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonArray& array, JsonWriter& writer) { +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonArray& array, Writer& writer) { writer.beginArray(); JsonArray::const_iterator it = array.begin(); @@ -31,13 +32,15 @@ inline void ArduinoJson::Internals::JsonSerializer::serialize( writer.endArray(); } -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonArraySubscript& arraySubscript, JsonWriter& writer) { +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonArraySubscript& arraySubscript, Writer& writer) { serialize(arraySubscript.as(), writer); } -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonObject& object, JsonWriter& writer) { +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonObject& object, Writer& writer) { writer.beginObject(); JsonObject::const_iterator it = object.begin(); @@ -55,14 +58,16 @@ inline void ArduinoJson::Internals::JsonSerializer::serialize( writer.endObject(); } +template template -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonObjectSubscript& objectSubscript, JsonWriter& writer) { +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonObjectSubscript& objectSubscript, Writer& writer) { serialize(objectSubscript.template as(), writer); } -inline void ArduinoJson::Internals::JsonSerializer::serialize( - const JsonVariant& variant, JsonWriter& writer) { +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonVariant& variant, Writer& writer) { switch (variant._type) { case JSON_UNDEFINED: return; diff --git a/src/ArduinoJson/Serialization/JsonWriter.hpp b/src/ArduinoJson/Serialization/JsonWriter.hpp index 0f8774a6..68822a1b 100644 --- a/src/ArduinoJson/Serialization/JsonWriter.hpp +++ b/src/ArduinoJson/Serialization/JsonWriter.hpp @@ -7,13 +7,13 @@ #pragma once +#include #include "../Data/Encoding.hpp" #include "../Data/JsonFloat.hpp" #include "../Data/JsonInteger.hpp" #include "../Polyfills/attributes.hpp" #include "../Polyfills/math.hpp" #include "../Polyfills/normalize.hpp" -#include "../Print.hpp" namespace ArduinoJson { namespace Internals { @@ -25,6 +25,7 @@ namespace Internals { // - JsonVariant::writeTo() // Its derived by PrettyJsonWriter that overrides some members to add // indentation. +template class JsonWriter { public: explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {} @@ -150,7 +151,7 @@ class JsonWriter { _length += _sink.print(s); } void writeRaw(char c) { - _length += _sink.write(c); + _length += _sink.print(c); } protected: diff --git a/src/ArduinoJson/Serialization/Prettyfier.hpp b/src/ArduinoJson/Serialization/Prettyfier.hpp index 57406560..377138b4 100644 --- a/src/ArduinoJson/Serialization/Prettyfier.hpp +++ b/src/ArduinoJson/Serialization/Prettyfier.hpp @@ -13,19 +13,27 @@ namespace ArduinoJson { namespace Internals { // Converts a compact JSON string into an indented one. -class Prettyfier : public Print { +template +class Prettyfier { public: - explicit Prettyfier(IndentedPrint& p) : _sink(p) { + explicit Prettyfier(IndentedPrint& p) : _sink(p) { _previousChar = 0; _inString = false; } - virtual size_t write(uint8_t c) { + size_t print(char c) { size_t n = _inString ? handleStringChar(c) : handleMarkupChar(c); _previousChar = c; return n; } + size_t print(const char* s) { + // TODO: optimize + size_t n = 0; + while (*s) n += print(*s++); + return n; + } + private: Prettyfier& operator=(const Prettyfier&); // cannot be assigned @@ -33,15 +41,15 @@ class Prettyfier : public Print { return _previousChar == '{' || _previousChar == '['; } - size_t handleStringChar(uint8_t c) { + size_t handleStringChar(char c) { bool isQuote = c == '"' && _previousChar != '\\'; if (isQuote) _inString = false; - return _sink.write(c); + return _sink.print(c); } - size_t handleMarkupChar(uint8_t c) { + size_t handleMarkupChar(char c) { switch (c) { case '{': case '[': @@ -65,31 +73,29 @@ class Prettyfier : public Print { } } - size_t writeBlockClose(uint8_t c) { + size_t writeBlockClose(char c) { size_t n = 0; n += unindentIfNeeded(); - n += _sink.write(c); + n += _sink.print(c); return n; } - size_t writeBlockOpen(uint8_t c) { + size_t writeBlockOpen(char c) { size_t n = 0; n += indentIfNeeded(); - n += _sink.write(c); + n += _sink.print(c); return n; } size_t writeColon() { size_t n = 0; - n += _sink.write(':'); - n += _sink.write(' '); + n += _sink.print(": "); return n; } size_t writeComma() { size_t n = 0; - n += _sink.write(','); - n += _sink.println(); + n += _sink.print(",\r\n"); return n; } @@ -97,14 +103,14 @@ class Prettyfier : public Print { _inString = true; size_t n = 0; n += indentIfNeeded(); - n += _sink.write('"'); + n += _sink.print('"'); return n; } - size_t writeNormalChar(uint8_t c) { + size_t writeNormalChar(char c) { size_t n = 0; n += indentIfNeeded(); - n += _sink.write(c); + n += _sink.print(c); return n; } @@ -112,18 +118,18 @@ class Prettyfier : public Print { if (!inEmptyBlock()) return 0; _sink.indent(); - return _sink.println(); + return _sink.print("\r\n"); } size_t unindentIfNeeded() { if (inEmptyBlock()) return 0; _sink.unindent(); - return _sink.println(); + return _sink.print("\r\n"); } - uint8_t _previousChar; - IndentedPrint& _sink; + char _previousChar; + IndentedPrint& _sink; bool _inString; }; } diff --git a/src/ArduinoJson/Serialization/StaticStringBuilder.hpp b/src/ArduinoJson/Serialization/StaticStringBuilder.hpp index 083efcc8..4f7352a0 100644 --- a/src/ArduinoJson/Serialization/StaticStringBuilder.hpp +++ b/src/ArduinoJson/Serialization/StaticStringBuilder.hpp @@ -7,31 +7,33 @@ #pragma once -#include "../Print.hpp" - namespace ArduinoJson { namespace Internals { // A Print implementation that allows to write in a char[] -class StaticStringBuilder : public Print { +class StaticStringBuilder { public: - StaticStringBuilder(char *buf, size_t size) - : buffer(buf), capacity(size - 1), length(0) { - buffer[0] = '\0'; + StaticStringBuilder(char *buf, size_t size) : end(buf + size - 1), p(buf) { + *p = '\0'; } - virtual size_t write(uint8_t c) { - if (length >= capacity) return 0; - - buffer[length++] = c; - buffer[length] = '\0'; + size_t print(char c) { + if (p >= end) return 0; + *p++ = c; + *p = '\0'; return 1; } + size_t print(const char *s) { + char *begin = p; + while (p < end && *s) *p++ = *s++; + *p = '\0'; + return p - begin; + } + private: - char *buffer; - size_t capacity; - size_t length; + char *end; + char *p; }; } } diff --git a/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp b/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp index 76e1e35a..9c2f86fd 100644 --- a/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp +++ b/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp @@ -11,22 +11,25 @@ #if ARDUINOJSON_ENABLE_STD_STREAM -#include "../Print.hpp" - #include namespace ArduinoJson { namespace Internals { -class StreamPrintAdapter : public Print { +class StreamPrintAdapter { public: explicit StreamPrintAdapter(std::ostream& os) : _os(os) {} - virtual size_t write(uint8_t c) { - _os << static_cast(c); + size_t print(char c) { + _os << c; return 1; } + size_t print(const char* s) { + _os << s; + return strlen(s); + } + private: // cannot be assigned StreamPrintAdapter& operator=(const StreamPrintAdapter&); diff --git a/src/ArduinoJson/StringTraits/StdString.hpp b/src/ArduinoJson/StringTraits/StdString.hpp index 7c0205cd..959c4746 100644 --- a/src/ArduinoJson/StringTraits/StdString.hpp +++ b/src/ArduinoJson/StringTraits/StdString.hpp @@ -43,6 +43,10 @@ struct StdStringTraits { str += c; } + static void append(TString& str, const char* s) { + str += s; + } + static const bool has_append = true; static const bool has_equals = true; static const bool should_duplicate = true; diff --git a/test/JsonWriter/writeFloat.cpp b/test/JsonWriter/writeFloat.cpp index 8e3de8c5..1560fd2f 100644 --- a/test/JsonWriter/writeFloat.cpp +++ b/test/JsonWriter/writeFloat.cpp @@ -17,7 +17,7 @@ using namespace ArduinoJson::Internals; void check(const std::string& expected, double input, uint8_t digits = 2) { char output[1024]; StaticStringBuilder sb(output, sizeof(output)); - JsonWriter writer(sb); + JsonWriter writer(sb); writer.writeFloat(input, digits); REQUIRE(output == expected); REQUIRE(writer.bytesWritten() == expected.size()); diff --git a/test/JsonWriter/writeString.cpp b/test/JsonWriter/writeString.cpp index 8ba4164c..5a6cd58a 100644 --- a/test/JsonWriter/writeString.cpp +++ b/test/JsonWriter/writeString.cpp @@ -15,7 +15,7 @@ using namespace ArduinoJson::Internals; void check(const char* input, std::string expected) { char output[1024]; StaticStringBuilder sb(output, sizeof(output)); - JsonWriter writer(sb); + JsonWriter writer(sb); writer.writeString(input); REQUIRE(expected == output); REQUIRE(writer.bytesWritten() == expected.size()); diff --git a/test/Misc/StringBuilder.cpp b/test/Misc/StringBuilder.cpp index 7859f3f9..78279fe2 100644 --- a/test/Misc/StringBuilder.cpp +++ b/test/Misc/StringBuilder.cpp @@ -10,20 +10,12 @@ using namespace ArduinoJson::Internals; -TEST_CASE("StringBuilder") { - char output[20]; - StaticStringBuilder sb(output, sizeof(output)); - +template +void common_tests(StringBuilder& sb, const String& output) { SECTION("InitialState") { REQUIRE(std::string("") == output); } - SECTION("OverCapacity") { - REQUIRE(19 == sb.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); - REQUIRE(0 == sb.print("ABC")); - REQUIRE(std::string("ABCDEFGHIJKLMNOPQRS") == output); - } - SECTION("EmptyString") { REQUIRE(0 == sb.print("")); REQUIRE(std::string("") == output); @@ -40,3 +32,22 @@ TEST_CASE("StringBuilder") { REQUIRE(std::string("ABCDEFGH") == output); } } + +TEST_CASE("StaticStringBuilder") { + char output[20]; + StaticStringBuilder sb(output, sizeof(output)); + + common_tests(sb, static_cast(output)); + + SECTION("OverCapacity") { + REQUIRE(19 == sb.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + REQUIRE(0 == sb.print("ABC")); + REQUIRE(std::string("ABCDEFGHIJKLMNOPQRS") == output); + } +} + +TEST_CASE("DynamicStringBuilder") { + std::string output; + DynamicStringBuilder sb(output); + common_tests(sb, output); +}