Removed Print class and converted printTo() to a template method (issue #276)

This commit is contained in:
Benoit Blanchon
2017-04-22 11:33:40 +02:00
parent c3e1677b7d
commit 9afa05e2f4
19 changed files with 147 additions and 173 deletions

View File

@ -9,6 +9,8 @@ HEAD
* Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)` * Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)`
* Renamed folder `include/` to `src/` * Renamed folder `include/` to `src/`
* Fixed warnings `floating constant exceeds range of float`and `floating constant truncated to zero` (issue #483) * 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 v5.8.4
------ ------

View File

@ -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 <ArduinoJson.h>
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
}

View File

@ -7,8 +7,6 @@
#pragma once #pragma once
#include "../Print.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {

View File

@ -40,8 +40,8 @@ class JsonObject;
// - a string (const char*) // - a string (const char*)
// - a reference to a JsonArray or JsonObject // - a reference to a JsonArray or JsonObject
class JsonVariant : public JsonVariantBase<JsonVariant> { class JsonVariant : public JsonVariantBase<JsonVariant> {
friend void Internals::JsonSerializer::serialize(const JsonVariant &, template <typename Print>
JsonWriter &); friend class Internals::JsonSerializer;
public: public:
// Creates an uninitialized JsonVariant // Creates an uninitialized JsonVariant

View File

@ -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 <stddef.h>
#include <stdint.h>
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<uint8_t>(*s++));
}
return n;
}
size_t println() {
size_t n = 0;
n += write('\r');
n += write('\n');
return n;
}
};
}
#else
#include <Print.h>
#endif

View File

@ -7,17 +7,19 @@
#pragma once #pragma once
#include "../Print.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
// A dummy Print implementation used in JsonPrintable::measureLength() // A dummy Print implementation used in JsonPrintable::measureLength()
class DummyPrint : public Print { class DummyPrint {
public: public:
virtual size_t write(uint8_t) { size_t print(char) {
return 1; return 1;
} }
size_t print(const char* s) {
return strlen(s);
}
}; };
} }
} }

View File

@ -7,7 +7,6 @@
#pragma once #pragma once
#include "../Print.hpp"
#include "../StringTraits/StringTraits.hpp" #include "../StringTraits/StringTraits.hpp"
namespace ArduinoJson { namespace ArduinoJson {
@ -15,15 +14,21 @@ namespace Internals {
// A Print implementation that allows to write in a String // A Print implementation that allows to write in a String
template <typename TString> template <typename TString>
class DynamicStringBuilder : public Print { class DynamicStringBuilder {
public: public:
DynamicStringBuilder(TString &str) : _str(str) {} DynamicStringBuilder(TString &str) : _str(str) {}
virtual size_t write(uint8_t c) { size_t print(char c) {
StringTraits<TString>::append(_str, static_cast<char>(c)); StringTraits<TString>::append(_str, c);
return 1; return 1;
} }
size_t print(const char *s) {
size_t initialLen = _str.length();
StringTraits<TString>::append(_str, s);
return _str.length() - initialLen;
}
private: private:
DynamicStringBuilder &operator=(const DynamicStringBuilder &); DynamicStringBuilder &operator=(const DynamicStringBuilder &);

View File

@ -7,15 +7,14 @@
#pragma once #pragma once
#include "../Print.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
// Decorator on top of Print to allow indented output. // Decorator on top of Print to allow indented output.
// This class is used by JsonPrintable::prettyPrintTo() but can also be used // This class is used by JsonPrintable::prettyPrintTo() but can also be used
// for your own purpose, like logging. // for your own purpose, like logging.
class IndentedPrint : public Print { template <typename Print>
class IndentedPrint {
public: public:
explicit IndentedPrint(Print &p) : sink(&p) { explicit IndentedPrint(Print &p) : sink(&p) {
level = 0; level = 0;
@ -23,14 +22,21 @@ class IndentedPrint : public Print {
isNewLine = true; isNewLine = true;
} }
virtual size_t write(uint8_t c) { size_t print(char c) {
size_t n = 0; size_t n = 0;
if (isNewLine) n += writeTabs(); if (isNewLine) n += writeTabs();
n += sink->write(c); n += sink->print(c);
isNewLine = c == '\n'; isNewLine = c == '\n';
return 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 // Adds one level of indentation
void indent() { void indent() {
if (level < MAX_LEVEL) level++; if (level < MAX_LEVEL) level++;
@ -54,7 +60,7 @@ class IndentedPrint : public Print {
size_t writeTabs() { size_t writeTabs() {
size_t n = 0; 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; return n;
} }

View File

@ -31,9 +31,12 @@ namespace Internals {
template <typename T> template <typename T>
class JsonPrintable { class JsonPrintable {
public: public:
size_t printTo(Print &print) const { template <typename Print>
JsonWriter writer(print); typename TypeTraits::EnableIf<!TypeTraits::IsString<Print>::value,
JsonSerializer::serialize(downcast(), writer); size_t>::type
printTo(Print &print) const {
JsonWriter<Print> writer(print);
JsonSerializer<JsonWriter<Print> >::serialize(downcast(), writer);
return writer.bytesWritten(); return writer.bytesWritten();
} }
@ -62,8 +65,9 @@ class JsonPrintable {
return printTo(sb); return printTo(sb);
} }
size_t prettyPrintTo(IndentedPrint &print) const { template <typename Print>
Prettyfier p(print); size_t prettyPrintTo(IndentedPrint<Print> &print) const {
Prettyfier<Print> p(print);
return printTo(p); return printTo(p);
} }
@ -77,8 +81,11 @@ class JsonPrintable {
return prettyPrintTo(buffer, N); return prettyPrintTo(buffer, N);
} }
size_t prettyPrintTo(Print &print) const { template <typename Print>
IndentedPrint indentedPrint = IndentedPrint(print); typename TypeTraits::EnableIf<!TypeTraits::IsString<Print>::value,
size_t>::type
prettyPrintTo(Print &print) const {
IndentedPrint<Print> indentedPrint(print);
return prettyPrintTo(indentedPrint); return prettyPrintTo(indentedPrint);
} }

View File

@ -20,14 +20,15 @@ class JsonVariant;
namespace Internals { namespace Internals {
template <typename Writer>
class JsonSerializer { class JsonSerializer {
public: public:
static void serialize(const JsonArray &, JsonWriter &); static void serialize(const JsonArray &, Writer &);
static void serialize(const JsonArraySubscript &, JsonWriter &); static void serialize(const JsonArraySubscript &, Writer &);
static void serialize(const JsonObject &, JsonWriter &); static void serialize(const JsonObject &, Writer &);
template <typename TKey> template <typename TKey>
static void serialize(const JsonObjectSubscript<TKey> &, JsonWriter &); static void serialize(const JsonObjectSubscript<TKey> &, Writer &);
static void serialize(const JsonVariant &, JsonWriter &); static void serialize(const JsonVariant &, Writer &);
}; };
} }
} }

View File

@ -14,8 +14,9 @@
#include "../JsonVariant.hpp" #include "../JsonVariant.hpp"
#include "JsonSerializer.hpp" #include "JsonSerializer.hpp"
inline void ArduinoJson::Internals::JsonSerializer::serialize( template <typename Writer>
const JsonArray& array, JsonWriter& writer) { inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
const JsonArray& array, Writer& writer) {
writer.beginArray(); writer.beginArray();
JsonArray::const_iterator it = array.begin(); JsonArray::const_iterator it = array.begin();
@ -31,13 +32,15 @@ inline void ArduinoJson::Internals::JsonSerializer::serialize(
writer.endArray(); writer.endArray();
} }
inline void ArduinoJson::Internals::JsonSerializer::serialize( template <typename Writer>
const JsonArraySubscript& arraySubscript, JsonWriter& writer) { inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
const JsonArraySubscript& arraySubscript, Writer& writer) {
serialize(arraySubscript.as<JsonVariant>(), writer); serialize(arraySubscript.as<JsonVariant>(), writer);
} }
inline void ArduinoJson::Internals::JsonSerializer::serialize( template <typename Writer>
const JsonObject& object, JsonWriter& writer) { inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
const JsonObject& object, Writer& writer) {
writer.beginObject(); writer.beginObject();
JsonObject::const_iterator it = object.begin(); JsonObject::const_iterator it = object.begin();
@ -55,14 +58,16 @@ inline void ArduinoJson::Internals::JsonSerializer::serialize(
writer.endObject(); writer.endObject();
} }
template <typename Writer>
template <typename TKey> template <typename TKey>
inline void ArduinoJson::Internals::JsonSerializer::serialize( inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
const JsonObjectSubscript<TKey>& objectSubscript, JsonWriter& writer) { const JsonObjectSubscript<TKey>& objectSubscript, Writer& writer) {
serialize(objectSubscript.template as<JsonVariant>(), writer); serialize(objectSubscript.template as<JsonVariant>(), writer);
} }
inline void ArduinoJson::Internals::JsonSerializer::serialize( template <typename Writer>
const JsonVariant& variant, JsonWriter& writer) { inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
const JsonVariant& variant, Writer& writer) {
switch (variant._type) { switch (variant._type) {
case JSON_UNDEFINED: case JSON_UNDEFINED:
return; return;

View File

@ -7,13 +7,13 @@
#pragma once #pragma once
#include <stdint.h>
#include "../Data/Encoding.hpp" #include "../Data/Encoding.hpp"
#include "../Data/JsonFloat.hpp" #include "../Data/JsonFloat.hpp"
#include "../Data/JsonInteger.hpp" #include "../Data/JsonInteger.hpp"
#include "../Polyfills/attributes.hpp" #include "../Polyfills/attributes.hpp"
#include "../Polyfills/math.hpp" #include "../Polyfills/math.hpp"
#include "../Polyfills/normalize.hpp" #include "../Polyfills/normalize.hpp"
#include "../Print.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
@ -25,6 +25,7 @@ namespace Internals {
// - JsonVariant::writeTo() // - JsonVariant::writeTo()
// Its derived by PrettyJsonWriter that overrides some members to add // Its derived by PrettyJsonWriter that overrides some members to add
// indentation. // indentation.
template <typename Print>
class JsonWriter { class JsonWriter {
public: public:
explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {} explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {}
@ -150,7 +151,7 @@ class JsonWriter {
_length += _sink.print(s); _length += _sink.print(s);
} }
void writeRaw(char c) { void writeRaw(char c) {
_length += _sink.write(c); _length += _sink.print(c);
} }
protected: protected:

View File

@ -13,19 +13,27 @@ namespace ArduinoJson {
namespace Internals { namespace Internals {
// Converts a compact JSON string into an indented one. // Converts a compact JSON string into an indented one.
class Prettyfier : public Print { template <typename Print>
class Prettyfier {
public: public:
explicit Prettyfier(IndentedPrint& p) : _sink(p) { explicit Prettyfier(IndentedPrint<Print>& p) : _sink(p) {
_previousChar = 0; _previousChar = 0;
_inString = false; _inString = false;
} }
virtual size_t write(uint8_t c) { size_t print(char c) {
size_t n = _inString ? handleStringChar(c) : handleMarkupChar(c); size_t n = _inString ? handleStringChar(c) : handleMarkupChar(c);
_previousChar = c; _previousChar = c;
return n; return n;
} }
size_t print(const char* s) {
// TODO: optimize
size_t n = 0;
while (*s) n += print(*s++);
return n;
}
private: private:
Prettyfier& operator=(const Prettyfier&); // cannot be assigned Prettyfier& operator=(const Prettyfier&); // cannot be assigned
@ -33,15 +41,15 @@ class Prettyfier : public Print {
return _previousChar == '{' || _previousChar == '['; return _previousChar == '{' || _previousChar == '[';
} }
size_t handleStringChar(uint8_t c) { size_t handleStringChar(char c) {
bool isQuote = c == '"' && _previousChar != '\\'; bool isQuote = c == '"' && _previousChar != '\\';
if (isQuote) _inString = false; 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) { switch (c) {
case '{': case '{':
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; size_t n = 0;
n += unindentIfNeeded(); n += unindentIfNeeded();
n += _sink.write(c); n += _sink.print(c);
return n; return n;
} }
size_t writeBlockOpen(uint8_t c) { size_t writeBlockOpen(char c) {
size_t n = 0; size_t n = 0;
n += indentIfNeeded(); n += indentIfNeeded();
n += _sink.write(c); n += _sink.print(c);
return n; return n;
} }
size_t writeColon() { size_t writeColon() {
size_t n = 0; size_t n = 0;
n += _sink.write(':'); n += _sink.print(": ");
n += _sink.write(' ');
return n; return n;
} }
size_t writeComma() { size_t writeComma() {
size_t n = 0; size_t n = 0;
n += _sink.write(','); n += _sink.print(",\r\n");
n += _sink.println();
return n; return n;
} }
@ -97,14 +103,14 @@ class Prettyfier : public Print {
_inString = true; _inString = true;
size_t n = 0; size_t n = 0;
n += indentIfNeeded(); n += indentIfNeeded();
n += _sink.write('"'); n += _sink.print('"');
return n; return n;
} }
size_t writeNormalChar(uint8_t c) { size_t writeNormalChar(char c) {
size_t n = 0; size_t n = 0;
n += indentIfNeeded(); n += indentIfNeeded();
n += _sink.write(c); n += _sink.print(c);
return n; return n;
} }
@ -112,18 +118,18 @@ class Prettyfier : public Print {
if (!inEmptyBlock()) return 0; if (!inEmptyBlock()) return 0;
_sink.indent(); _sink.indent();
return _sink.println(); return _sink.print("\r\n");
} }
size_t unindentIfNeeded() { size_t unindentIfNeeded() {
if (inEmptyBlock()) return 0; if (inEmptyBlock()) return 0;
_sink.unindent(); _sink.unindent();
return _sink.println(); return _sink.print("\r\n");
} }
uint8_t _previousChar; char _previousChar;
IndentedPrint& _sink; IndentedPrint<Print>& _sink;
bool _inString; bool _inString;
}; };
} }

View File

@ -7,31 +7,33 @@
#pragma once #pragma once
#include "../Print.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
// A Print implementation that allows to write in a char[] // A Print implementation that allows to write in a char[]
class StaticStringBuilder : public Print { class StaticStringBuilder {
public: public:
StaticStringBuilder(char *buf, size_t size) StaticStringBuilder(char *buf, size_t size) : end(buf + size - 1), p(buf) {
: buffer(buf), capacity(size - 1), length(0) { *p = '\0';
buffer[0] = '\0';
} }
virtual size_t write(uint8_t c) { size_t print(char c) {
if (length >= capacity) return 0; if (p >= end) return 0;
*p++ = c;
buffer[length++] = c; *p = '\0';
buffer[length] = '\0';
return 1; return 1;
} }
size_t print(const char *s) {
char *begin = p;
while (p < end && *s) *p++ = *s++;
*p = '\0';
return p - begin;
}
private: private:
char *buffer; char *end;
size_t capacity; char *p;
size_t length;
}; };
} }
} }

View File

@ -11,22 +11,25 @@
#if ARDUINOJSON_ENABLE_STD_STREAM #if ARDUINOJSON_ENABLE_STD_STREAM
#include "../Print.hpp"
#include <ostream> #include <ostream>
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
class StreamPrintAdapter : public Print { class StreamPrintAdapter {
public: public:
explicit StreamPrintAdapter(std::ostream& os) : _os(os) {} explicit StreamPrintAdapter(std::ostream& os) : _os(os) {}
virtual size_t write(uint8_t c) { size_t print(char c) {
_os << static_cast<char>(c); _os << c;
return 1; return 1;
} }
size_t print(const char* s) {
_os << s;
return strlen(s);
}
private: private:
// cannot be assigned // cannot be assigned
StreamPrintAdapter& operator=(const StreamPrintAdapter&); StreamPrintAdapter& operator=(const StreamPrintAdapter&);

View File

@ -43,6 +43,10 @@ struct StdStringTraits {
str += c; str += c;
} }
static void append(TString& str, const char* s) {
str += s;
}
static const bool has_append = true; static const bool has_append = true;
static const bool has_equals = true; static const bool has_equals = true;
static const bool should_duplicate = true; static const bool should_duplicate = true;

View File

@ -17,7 +17,7 @@ using namespace ArduinoJson::Internals;
void check(const std::string& expected, double input, uint8_t digits = 2) { void check(const std::string& expected, double input, uint8_t digits = 2) {
char output[1024]; char output[1024];
StaticStringBuilder sb(output, sizeof(output)); StaticStringBuilder sb(output, sizeof(output));
JsonWriter writer(sb); JsonWriter<StaticStringBuilder> writer(sb);
writer.writeFloat(input, digits); writer.writeFloat(input, digits);
REQUIRE(output == expected); REQUIRE(output == expected);
REQUIRE(writer.bytesWritten() == expected.size()); REQUIRE(writer.bytesWritten() == expected.size());

View File

@ -15,7 +15,7 @@ using namespace ArduinoJson::Internals;
void check(const char* input, std::string expected) { void check(const char* input, std::string expected) {
char output[1024]; char output[1024];
StaticStringBuilder sb(output, sizeof(output)); StaticStringBuilder sb(output, sizeof(output));
JsonWriter writer(sb); JsonWriter<StaticStringBuilder> writer(sb);
writer.writeString(input); writer.writeString(input);
REQUIRE(expected == output); REQUIRE(expected == output);
REQUIRE(writer.bytesWritten() == expected.size()); REQUIRE(writer.bytesWritten() == expected.size());

View File

@ -10,20 +10,12 @@
using namespace ArduinoJson::Internals; using namespace ArduinoJson::Internals;
TEST_CASE("StringBuilder") { template <typename StringBuilder, typename String>
char output[20]; void common_tests(StringBuilder& sb, const String& output) {
StaticStringBuilder sb(output, sizeof(output));
SECTION("InitialState") { SECTION("InitialState") {
REQUIRE(std::string("") == output); REQUIRE(std::string("") == output);
} }
SECTION("OverCapacity") {
REQUIRE(19 == sb.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
REQUIRE(0 == sb.print("ABC"));
REQUIRE(std::string("ABCDEFGHIJKLMNOPQRS") == output);
}
SECTION("EmptyString") { SECTION("EmptyString") {
REQUIRE(0 == sb.print("")); REQUIRE(0 == sb.print(""));
REQUIRE(std::string("") == output); REQUIRE(std::string("") == output);
@ -40,3 +32,22 @@ TEST_CASE("StringBuilder") {
REQUIRE(std::string("ABCDEFGH") == output); REQUIRE(std::string("ABCDEFGH") == output);
} }
} }
TEST_CASE("StaticStringBuilder") {
char output[20];
StaticStringBuilder sb(output, sizeof(output));
common_tests(sb, static_cast<const char*>(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<std::string> sb(output);
common_tests(sb, output);
}