Adding format control to the Print class methods

128 views
Skip to first unread message

Michael Jonker

unread,
Jan 18, 2015, 6:02:07 PM1/18/15
to devel...@arduino.cc

Hello,

I have been working on an improvement of the Print class that provides control of the output format. I would like to get your comments and feedback.

The work is available in https://github.com/MichaelJonker/Arduino in the branch Print_fieldControl (or more precisely https://github.com/MichaelJonker/Arduino/tree/Print_fieldControl/hardware/arduino)

Essentially it consists of a backward compatible rewrite of the files Print.h and Print.cpp.

The print methods in my version of the Print class accept an optional third parameter to control the output in terms of minimum field width, filling character and sign forcing.

/* fieldControl enhancements

Enhanced the formatting capabilities of the print methods. The optional third parameter of the print() methods allows to control the print output format in terms of minimum field width, filling character selection and sign forcing.

The recommended way of creating the fieldControl parameter is to use the fieldControl method, which has the following signature :

Print::fieldControl(unsigned char size, bool fillZeroes=false, bool forceSign=false);

Examples:

Serial.print("answer=");   Serial.println( 42);                                          // answer=42
Serial.print("answer=");   Serial.println( 42, 0, Print::fieldControl(4));               // answer=  42
Serial.print("answer=");   Serial.println(137, 0, Print::fieldControl(4));               // answer= 137
Serial.print("answer=");   Serial.println(137, 0, Print::fieldControl(4,false, true));   // answer= +137
 
Serial.print("answer=0x"); Serial.println((unsigned) 0xcafe, Print::Radix_HEX, Print::fieldControl(8, true) ); // answer=0x0000cafe
Serial.print("answer=");   Serial.println((unsigned) 0xcafe, Print::Radix_HEX, Print::fieldControl(6) );       // answer=  cafe
Serial.print("answer=");   Serial.println(  (signed) 0xcafe, Print::Radix_HEX, Print::fieldControl(6) );       // answer= -3502

const unsigned char myFieldControl = Print::fieldControl(6, false, true );
Serial.print("answer=");   Serial.println(  (signed) 0xcafe, Print::Radix_HEX, myFieldControl);                // answer=  -3502
Serial.print("answer=");   Serial.println((unsigned) 0xcafe, Print::Radix_HEX, myFieldControl);                // answer=  +cafe
 
The field control parameter can also be used for floating numbers to control the format of the integral part:

const unsigned char myFieldControl = Print::fieldControl(4, true, true );
Serial.print("answer=");   Serial.println(41.987654321, 0, myFieldControl);   // answer=+0042
Serial.print("answer=");   Serial.println(41.987654321, 1, myFieldControl);   // answer=+0042.0
Serial.print("answer=");   Serial.println(41.987654321, 2, myFieldControl);   // answer=+0041.99
Serial.print("answer=");   Serial.println(41.987654321, 3, myFieldControl);   // answer=+0041.988
Serial.print("answer=");   Serial.println(41.987654321, 4, myFieldControl);   // answer=+0041.9877
Serial.print("answer=");   Serial.println(41.987654321, 5, myFieldControl);   // answer=+0041.98765

*/


 

Notes :

My branch on github is a (fully functional) preview. I am working on a new version that further reduces the footprint of the software (and a minor bug fix). The footprint of my version tested with a mix of various print statements is already smaller than the arduino version.

There is one difference with the current version: floating numbers d with abs(d) > 2147483392.0 (=(double)0x7FFFFF00) will be printed as ovf while in the current version this cut-off happens for values where (d) > 4294967040.0 (= (double)0xFFFFFF00ul).

I intend to also add support for scientific format to print(float) in order to resolve the ovl aspect.

 

This enhancement is a spin-off of the project https://github.com/MichaelJonker/Arduino_HardwareSerial_RS485, a project to support an RS485 transceiver connected to the USART (Tx/Rx pins) in a half-duplex, concurrent multi-drop (i.e. multi-master, multi-slave) environment. The software suite provides capabilities for message addressing and filtering as well as collision detection and avoidance. I will bring this up for discussion in a separate post.

Kind regards ,
Michael Jonker

Rich Obermeyer

unread,
Jan 19, 2015, 5:15:43 PM1/19/15
to devel...@arduino.cc
Well done Michael.  Much needed overhaul.


--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.

Paul Stoffregen

unread,
Jan 19, 2015, 5:45:19 PM1/19/15
to devel...@arduino.cc
While there is a need, I wonder how intuitive these lines are?

Serial.println(137, 0, Print::fieldControl(4));

Serial.println(137, 0, Print::fieldControl(4,false, true));

Am I the only one who's first impression is overly complex, underly descriptive syntax?

Tom Igoe

unread,
Jan 19, 2015, 5:50:34 PM1/19/15
to devel...@arduino.cc
I didn’t comment because I couldn’t immediately imagine an example using this code. So, yes. Are there full examples available? I’m on the move, so not in a position where I can look at the repo today, sorry, or I’d look before asking.

t.

Andrew Kroll

unread,
Jan 19, 2015, 5:58:31 PM1/19/15
to devel...@arduino.cc
I'll just use printf :-)

--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.



--
Visit my github for awesome Arduino code @ https://github.com/xxxajk

Tom Igoe

unread,
Jan 19, 2015, 6:00:23 PM1/19/15
to devel...@arduino.cc
printf suffers from a terseness and use of arbitrary symbols  that I’d like to avoid in a core Arduino API if we can. 

t.

dray...@gmail.com

unread,
Jan 19, 2015, 6:03:18 PM1/19/15
to devel...@arduino.cc
Yes, it is terse and seemingly arbitrary, but it’s very well documented all over the place and extremely well understood.

I’ll stick with printf() also.

dave

Tom Igoe

unread,
Jan 19, 2015, 6:09:40 PM1/19/15
to devel...@arduino.cc
If we were going to stick with C standards, we might as well scrap Arduino entirely. You’re welcome to stick with printf, but if we’re talking about something that wants to be in the core, I want something clearer for beginners. I think printf() is only extremely well understood by experienced programmers.

t.

Andrew Kroll

unread,
Jan 19, 2015, 6:11:52 PM1/19/15
to devel...@arduino.cc
So, instead, teach bad habits and dismiss reuse... :-)

Tom Igoe

unread,
Jan 19, 2015, 6:13:30 PM1/19/15
to devel...@arduino.cc
If that is your opinion, then why use Arduino at all?  Why don’t you just use the std lib in your work?

And please stop with the smiley faces unless you’re trying to troll me here.

t.

Andrew Kroll

unread,
Jan 19, 2015, 6:17:30 PM1/19/15
to devel...@arduino.cc
Use template with variable args, that would be the right way in C++...

I bother with it because it has a lot of stuff that is written for me. I don't use the IDE though.

Andrew Kroll

unread,
Jan 19, 2015, 6:26:18 PM1/19/15
to devel...@arduino.cc
This is what would be actually very acceptable:

FancyPrint(class Klass, ...)

so you could write something like this:
FancyPrint(Serial, "this is the value of foo: ", foo, " and the value of bar: ",bar);
FancyPrint(Serial1, "Debug message: this is the value of foo: ", foo, " and the value of bar: ",bar);

With templates what each arg actually is doesn't really matter. You iterate over the types, and each in turn will call the correct sub-template. C++ takes care of the details, and you get to keep your sanity.

dray...@gmail.com

unread,
Jan 19, 2015, 6:32:14 PM1/19/15
to devel...@arduino.cc
Goodness, there’s no insult or belittlement intended here.  The reasons for using Arduinos are myriad and have been described all over the place, that’s why people use them.  The reason for using printf is similar, it only takes a few seconds to find examples and tutorials on it.  It may be weird, but it works and doesn’t need a special example to be made.

Plus, what’s learned is applicable to the next project and the ones after that.  It’s so common, it’s almost ubiquitous.


dave

Tom Igoe

unread,
Jan 19, 2015, 6:40:46 PM1/19/15
to devel...@arduino.cc
Those look like good examples to me, thanks much.

But to be clear, when you say “what each arg is doesn’t really matter”, you mean “as long as it’s a type that’s currently printable using the Print (or FancyPrint) class, right?  Or am I missing something?

Andrew Kroll

unread,
Jan 19, 2015, 6:42:52 PM1/19/15
to devel...@arduino.cc
As long as the class (first arg you would pass) has a print method for a given type, it will call that method.

Andrew Kroll

unread,
Jan 19, 2015, 6:48:14 PM1/19/15
to devel...@arduino.cc

Tom Igoe

unread,
Jan 19, 2015, 6:49:41 PM1/19/15
to devel...@arduino.cc
I’m assuming there is some sense to making it a template that’s not part of the main Print class, to keep memory footprint down when you’re not using it, right?

What I like about this is that it also sets up a precedent for some other templates for other classes that use Print, like the Ethernet library, the Bridge library, the SPI and I2C libraries, and more. Correct me if I’m wrong. Sorry I didn’t understand where you’re coming from before (I’m weak on templates), but your example below has helped me a lot. This could get really useful done that way. 

Tom

Paul Stoffregen

unread,
Jan 19, 2015, 6:49:56 PM1/19/15
to devel...@arduino.cc
On 01/19/2015 03:42 PM, Andrew Kroll wrote:
As long as the class (first arg you would pass) has a print method for a given type, it will call that method.

Maybe I missed something in all the shouting about printf().... how exactly would FancyPrint to be able to control how many characters print for variables foo & bar, what radix is used, whether left padding is zeros or spaces, and whether to always print a sign?

Tom Igoe

unread,
Jan 19, 2015, 6:50:29 PM1/19/15
to Arduino Developer's List
Thanks!

t.

Matthijs Kooijman

unread,
Jan 19, 2015, 7:09:23 PM1/19/15
to devel...@arduino.cc
Hey Paul & others

> Maybe I missed something in all the shouting about printf().... how
> exactly would FancyPrint to be able to control how many characters
> print for variables foo & bar, what radix is used, whether left
> padding is zeros or spaces, and whether to always print a sign?
>
> FancyPrint(Serial, "this is the value of foo: ", foo, " and the
> value of bar: ",bar);

I think that with this syntax, you'd just get the defaults (which might
actually be fine in a lot of cases - you can revert to a more verbose
syntax when you need more control).

So this suggestion helps to make multiple prints less verbose, but it
doesn't help to add more control over the print output (which was the
original goal of this thread).


This reminds me of a library I once started on, I dubbed TStreaming (for
template streaming). It uses the standard C++ << syntax for printing,
but also uses type-based annotating of values-to-be-printed to control
output.

As an example: Serial << "Value: " << x << " (0x" << V<Hex>(x) << ")";

This would first print x using default options, and then print x in
hexadecimal format. This also allows custom output formatting, or
aliases for commonly used formatting options.

https://github.com/matthijskooijman/TStreaming

It's been a while since I worked on this and I don't quite remember how
polished it was, but the point is that annotating values in a way such
as this one might be a useful API to offer?

Gr.

Matthijs
signature.asc

Matthijs Kooijman

unread,
Jan 19, 2015, 7:14:01 PM1/19/15
to devel...@arduino.cc
Hey Tom,

> I’m assuming there is some sense to making it a template that’s not
> part of the main Print class, to keep memory footprint down when
> you’re not using it, right?
Normally, templates are only instantiated when they're used, so just
defining a template shouldn't add any overhead.

In this particular case, I think you could manage to implement a
generic and variadic Print::print() and Print::println() function that
essentially produces the same code as calling the single-argument print
multiple times manually.

We probably cannot use Print::print for this, though, since
print(10, HEX) would be ambigous: Do you want to print "a" or "1016" (10
followed by HEX == 16)?

> What I like about this is that it also sets up a precedent for some
> other templates for other classes that use Print, like the Ethernet
> library, the Bridge library, the SPI and I2C libraries, and more.
If we implement this based on Print (either as part of Print, or
externally using a Print argument), then indeed, this is available for
all Print implentations directly.

Gr.

Matthijs
signature.asc

Andrew Kroll

unread,
Jan 19, 2015, 7:19:48 PM1/19/15
to devel...@arduino.cc
On Mon, Jan 19, 2015 at 6:49 PM, Paul Stoffregen <pa...@pjrc.com> wrote:
On 01/19/2015 03:42 PM, Andrew Kroll wrote:
As long as the class (first arg you would pass) has a print method for a given type, it will call that method.

Maybe I missed something in all the shouting about printf().... how exactly would FancyPrint to be able to control how many characters print for variables foo & bar, what radix is used, whether left padding is zeros or spaces, and whether to always print a sign?


Use a type template hint for radix/formatting, and pass the type hint..

--

Michael Jonker

unread,
Jan 20, 2015, 1:54:25 PM1/20/15
to devel...@arduino.cc
Thanks for all the comments. However, I find this discussion a bit confusing, it is not always clear who is addressing who..

And a large part of the discussion is also, to my opinion, off-track with a discussion between print, printf or something fancier. Do not get me wrong, such a discussion is good but I propose to continue it in a different thread.

Before I started my mods, I had considered printf. Possibly there exist printf-like implementations compatible with (or implemented over) the Arduino’s SerialHardware class. But I also feared that these implementations will put an extra resource load in top of print methods already resident. (Are there any printf implementations available compatible with the F(“string”) macro?)

Here the question is, do we want an enhancement of the Print class? I do not think that this question should be rejected out of worries that such an improvement might weaken the case for a full blown printf-like-or-better implementation. Besides is very likely that the more fancy methods are better implemented in top of the primitive print methods, maybe even happily using its format control capabilities.

For the fancy print methods the c++ syntax as suggested by Matthijs is much more appropriate. A full printf implementation has to deal with runtime format specifications, hence all code to handle the fancy format specification will be resident and contribute to the footprint, whether it is used or not. This is not the case for the c++ style implementation. Personally I think that resource usage on the Atmega is of concern.

Having said all this (and proposing that someone opens a fancy_print discussion thread, to which I am happy to contribute), let me refocus on the core of this thread: the format control enhancement of the print methods.

The modification proposed of the Print class can be adopted transparently, without even mentioning the third parameter. Also the footprint will not dramatically increase, for some large test cases I have seen it diminishing. (I will post more info on this). So there is no obligation to use it, but it is available and then, why not advertising it…

There was a relevant remark from Paul:
While there is a need, I wonder how intuitive these lines are?
Serial.println(137, 0, Print::fieldControl(4));
Serial.println(137, 0, Print::fieldControl(4,false, true));
I agree: there is a case for making the syntax more intuitive to the novice user. I reckon that formatControl is a better name than fieldControl. Then one could define values (and types) for the formatControl parameters. We will get something like:
Serial.println(137, Print::Radix_DEC, Print::formatControl(0, Print::FillSpace, Print::NoForceSign));
Serial.println(137, Print::Radix_DEC, Print::formatControl(0, Print::FillSpace);
Serial.println(137, Print::Radix_DEC, Print::formatControl(0));
Serial.println(137, Print::Radix_DEC);
Serial.println(137);
// Note that the above examples are, because of the default values, all identical.
// A non-trivial example not using default values is:
Serial.println(137, Print::Radix_HEX, Print::formatControl(8,Print::FillZero,Print::ForceSign));

Although I curse macros, one may define some convenience macros to shorten the code, i.e. add something like :
#define FILL_SPACE   Print::FillSpace
#define FILL_ZERO    Print::FillZero
#define FORCE_SIGN   Print::ForceSign
#define PRINT_FORMAT Print::formatControl
I left out a #define FILL_SPACE as this is the default value of the last parameter and hence not required.

Or does anyone has a better proposal for formatControl which is a static method returning an unsigned char
static const unsigned char formatControl(unsigned char size, bool fillZeroes=false, bool forceSign=false)
{
 
return ( (size==0? 0: size>m_fieldSizeMask ? m_fieldSizeMask:size-1) | (fillZeroes ? m_fillZeroes:0) | (forceSign ? m_forceSign:0) );
}

We may even consider to combine the radix and formatControl in a single (two byte int) parameter. This can also be made backward compatible. We would get
Serial.println(137, DEC);
Serial.println(137, Print::FormatControl(8, Print::Raxid_HEX, Print::FillZero, Print::ForceSign) );

// or maybe:
Serial.println(137, Print::FormatControl(8, Print::Raxid_HEX | Print::FillZero | Print::ForceSign) );
Beware, a combined parameter gets more confusing if we also consider floats.

For me, most of the work in under the hood and already done, now it is just a matter defining a nice appealing and coherent interface.
Michael



Tom Igoe

unread,
Jan 21, 2015, 7:00:48 AM1/21/15
to devel...@arduino.cc

There was a relevant remark from Paul:
While there is a need, I wonder how intuitive these lines are?
Serial.println(137, 0, Print::fieldControl(4));
Serial.println(137, 0, Print::fieldControl(4,false, true));

I don’t think these are intuitive for the beginner at all. The problem isn’t formatControl vs. fieldControl, it’s the Print::commandName(param, param, param) in the midst of a print statement,

I think the template-based approach that was suggested by Andrew Kroll is far more readable.

Tom

Paul Stoffregen

unread,
Jan 21, 2015, 7:11:06 AM1/21/15
to devel...@arduino.cc
On 01/21/2015 04:00 AM, Tom Igoe wrote:
I don’t think these are intuitive for the beginner at all. The problem isn’t formatControl vs. fieldControl, it’s the Print::commandName(param, param, param) in the midst of a print statement,

Yes, my thoughts exactly!

In concept, I like this idea:


We may even consider to combine the radix and formatControl in a single (two byte int) parameter. This can also be made backward compatible.

But as a matter of syntax, this is incredibly un-Arduino:



Serial.println(137, Print::FormatControl(8, Print::Raxid_HEX, Print::FillZero, Print::ForceSign) );

Perhaps this can be done without the complex use of C++ static class syntax?  Maybe simple addition would be intuitive?  Tom?

Serial.println(137, HEX+DIGITS(8)+FILLZERO+PLUSMINUS);


Matthijs Kooijman

unread,
Jan 21, 2015, 7:30:58 AM1/21/15
to devel...@arduino.cc
Hey Michael,

> And a large part of the discussion is also, to my opinion, off-track with a
> discussion between print, printf or something fancier. Do not get me wrong,
> such a discussion is good but I propose to continue it in a different
> thread.
Apologies, I also got sidetracked by replies, before I took the time to
properly go over your original post.

> Having said all this (and proposing that someone opens a fancy_print
> discussion thread, to which I am happy to contribute), let me refocus on
> the core of this thread: the format control enhancement of the print
> methods.
Right. If we get the format control done right, we might be able to
re-use the same primitives for multi-value print as well.

> The modification proposed of the Print class can be adopted transparently,
> without even mentioning the third parameter.
Without mentioning it where?

> user. I reckon that formatControl is a better name than fieldControl. Then
> one could define values (and types) for the formatControl parameters. We
> will get something like:
> Serial.println(137, Print::Radix_DEC, Print::formatControl(0, Print::FillSpace, Print::NoForceSign));
I like the explicitness of this syntax (though it's probably a bit _too_
verbose at times).

Two remarks:
- shouldn't the radix also just be a format option? If we make
FormatControl a proper type (see below), I think C++ overloading
should be able to distinguish between a radix as the second argument
and a format control argument, so this would not introduce any
ambiguity?
- I'd like to remove the implicitness of the "0" argument here too. How
about:

Serial.println(137, Print::Radix_DEC, Print::formatControl(Print::MinDigits(0), Print::FillSpace, Print::NoForceSign));

> Although I curse macros, one may define some convenience macros to shorten
> the code, i.e. add something like :
> #define FILL_SPACE Print::FillSpace
> #define FILL_ZERO Print::FillZero
> #define FORCE_SIGN Print::ForceSign
> #define PRINT_FORMAT Print::formatControl
Right, that could actually make sense.

> Or does anyone has a better proposal for formatControl which is a static
> method returning an unsigned char
> static const unsigned char formatControl(unsigned char size, bool fillZeroes
> =false, bool forceSign=false)
> {
> return ( (size==0? 0: size>m_fieldSizeMask ? m_fieldSizeMask:size-1) | (fillZeroes
> ? m_fillZeroes:0) | (forceSign ? m_forceSign:0) );
> }
I would consider making a FormatControl struct, which can contain just a
single integer, or perhaps multiple bitfields for easy access. In any
case, passing such a struct by value compiles to the same code as
passing the fields directly, so this shouldn't have any runtime cost
(but makes it easier to leverage C++ overloading at compiletime).

> We may even consider to combine the radix and formatControl in a single
> (two byte int) parameter. This can also be made backward compatible. We
> would get
> Serial.println(137, DEC);
> Serial.println(137, Print::FormatControl(8, Print::Raxid_HEX, Print::
> FillZero, Print::ForceSign) );
Hah, I should have read your entire mail first ;-p

I'm all for it - and if we make FormatControl a proper type, we can
implement backward compatibility by implementing print(int, int) in
terms of print(int, formatControl).

What I don't quite like about your proposal is that you have to specify
all parameters up to the first one that is non-default. E.g. if you want
to pass Print::ForceSign, you have to pass all others as well.

This could partly be fixed by making the Print::ForceSign etc. just
bitmask values and (probably using templates) make formatControl accept
different number of arguments and just or'ing them together.

Somewhatmore complicated, but also more powerful, would be to give
ForceSign and NoForceSign their own struct type, allowing more
flexibility at compiletime in handling these values. This would allow a
syntax like:

Serial.println(137, Print::formatControl(Print::MinDigits(4), Print::ForceSign));

but probably even:

Serial.println(137, Print::MinDigits(4), Print::ForceSign);

I think it would also open up the way for custom output, so something
like:

struct PrintAbs {
int print(Print &p, int v) {
p.print(abs(v));
}
}
Serial.println(-100, PrintAbs());

Would print the absolute value of a number.

Finally, I think this could be used to add more type-safety. E.g. have:

Serial.println("foo", Print::ForceSign);

result in an error, instead of the argument being silently ignored
(though I'm entirely sure if we can actually tweak the error message, or
wether it will be a ugly C++ template error).

How does this look?

Gr.

Matthijs
signature.asc

Tom Igoe

unread,
Jan 21, 2015, 9:12:14 AM1/21/15
to devel...@arduino.cc


But as a matter of syntax, this is incredibly un-Arduino:


Serial.println(137, Print::FormatControl(8, Print::Raxid_HEX, Print::FillZero, Print::ForceSign) );

Perhaps this can be done without the complex use of C++ static class syntax?  Maybe simple addition would be intuitive?  Tom?

Serial.println(137, HEX+DIGITS(8)+FILLZERO+PLUSMINUS);

I think if you’re doing something like that, I’d prefer this:

Serial.println(137, HEX(8)+FILLZERO+SIGNED);

But I’d Iike to hear others weigh in on it too. 


c.ma...@bug.st

unread,
Jan 21, 2015, 11:19:55 AM1/21/15
to devel...@arduino.cc
just want to add a +1 to Paul and Tom here, I prefer to avoid to expose
complex C++ syntax to the final user unless it's really really needed,
in particular the namespace operator :: like in Print::Raxid_HEX is very
arduino-unlike, so those are better hidden behind a macro.

C

c.ma...@bug.st

unread,
Jan 21, 2015, 11:31:52 AM1/21/15
to devel...@arduino.cc
Il 21/01/2015 13:30, Matthijs Kooijman ha scritto:
> Two remarks:
> - shouldn't the radix also just be a format option? If we make
> FormatControl a proper type (see below), I think C++ overloading
> should be able to distinguish between a radix as the second argument
> and a format control argument, so this would not introduce any
> ambiguity?
> - I'd like to remove the implicitness of the "0" argument here too. How
> about:
>
> Serial.println(137, Print::Radix_DEC, Print::formatControl(Print::MinDigits(0), Print::FillSpace, Print::NoForceSign));

Both remarks sounds right to me.


>> Although I curse macros, one may define some convenience macros to shorten
>> the code, i.e. add something like :
>> #define FILL_SPACE Print::FillSpace
>> #define FILL_ZERO Print::FillZero
>> #define FORCE_SIGN Print::ForceSign
>> #define PRINT_FORMAT Print::formatControl
> Right, that could actually make sense.

+1 here too


>> Or does anyone has a better proposal for formatControl which is a static
>> method returning an unsigned char
>> static const unsigned char formatControl(unsigned char size, bool fillZeroes
>> =false, bool forceSign=false)
>> {
>> return ( (size==0? 0: size>m_fieldSizeMask ? m_fieldSizeMask:size-1) | (fillZeroes
>> ? m_fillZeroes:0) | (forceSign ? m_forceSign:0) );
>> }
>
> What I don't quite like about your proposal is that you have to specify
> all parameters up to the first one that is non-default. E.g. if you want
> to pass Print::ForceSign, you have to pass all others as well.

[...CUT...]

> This could partly be fixed by making the Print::ForceSign etc. just
> bitmask values and (probably using templates) make formatControl accept
> different number of arguments and just or'ing them together.

and +1 here too. This looks like to move towards Paul's proposal to
"sum" options, but with a more clear syntalx like:

Serial.println(137, HEX(8));
Serial.println(137, HEX(8), FILLZERO, SIGNED);


> I think it would also open up the way for custom output, so something
> like:
>
> struct PrintAbs {
> int print(Print &p, int v) {
> p.print(abs(v));
> }
> }
> Serial.println(-100, PrintAbs());
>
> Would print the absolute value of a number.
>
> Finally, I think this could be used to add more type-safety. E.g. have:
>
> Serial.println("foo", Print::ForceSign);
>
> result in an error, instead of the argument being silently ignored
> (though I'm entirely sure if we can actually tweak the error message, or
> wether it will be a ugly C++ template error).
>
> How does this look?

Overall I like it, it's really well thought. What's missing now is an
implementation to see if everything works once put together.

Michael, are you willing to try it out? What do you think?

C



Mike

unread,
Jan 21, 2015, 2:36:41 PM1/21/15
to devel...@arduino.cc
I like Paul's syntax: Serial.println(137, HEX+DIGITS(8)+FILLZERO+PLUSMINUS);

Although you could possibly  combine Arduino and a more C like syntax and have Serial.printf class taking more traditional printf arguments.  I don't see that as too "unArduino", others may think otherwise.

Michael Jonker

unread,
Jan 21, 2015, 4:32:38 PM1/21/15
to devel...@arduino.cc
Thanks for all your comments! I will digest it and try out a few things to come up with a few more solid proposal to choose from.
There are a few constraint, i.e. the code should be
  • backward compatible, hence Print(3.14159, 3) should still output 3.142 and print(42, 4) should output 222 (Eh oui!)
  • intuitive Arduino like (but what is Arduino like, do we have a standard)
  • efficient and resource economic, this excludes any printf like format control (see my previous posting).
  • and last not but not least, possible to implement.
  • not depend on macros if possible (my own personal taste)
Please give me a couple of days...
stay tuned
M



Paul Stoffregen

unread,
Jan 21, 2015, 4:37:25 PM1/21/15
to devel...@arduino.cc
On 01/21/2015 01:32 PM, Michael Jonker wrote:
  • backward compatible, hence Print(3.14159, 3) should still output 3.142 and print(42, 4) should output 222 (Eh oui!)

Would you believe I was thinking earlier this morning about asking if this is really necessary?

If there really any need to keep supporting radix other than DEC, BIN, OCT and HEX?

Tom Igoe

unread,
Jan 21, 2015, 4:42:50 PM1/21/15
to devel...@arduino.cc
On Jan 21, 2015, at 4:32 PM, Michael Jonker <michael....@gmail.com> wrote:

Thanks for all your comments! I will digest it and try out a few things to come up with a few more solid proposal to choose from.
There are a few constraint, i.e. the code should be
  • backward compatible, hence Print(3.14159, 3) should still output 3.142 and print(42, 4) should output 222 (Eh oui!)

Yes, definitely

  • intuitive Arduino like (but what is Arduino like, do we have a standard)

We do have an API style guide:   http://arduino.cc/en/Reference/APIStyleGuide 
  • efficient and resource economic, this excludes any printf like format control (see my previous posting).
Yes, and it seems doesn’t exclude templates.

  • and last not but not least, possible to implement.

That seems wise.

  • not depend on macros if possible (my own personal taste)

No preference here. 


Tom Igoe

unread,
Jan 21, 2015, 4:44:08 PM1/21/15
to devel...@arduino.cc
Taking back what I said in the last mail, I could be convinced that there’s not. I’ll admit I never use it. But it’s Cristian and Federico you really want to convince. 


Rob Tillaart

unread,
Jan 22, 2015, 6:42:04 AM1/22/15
to Arduino Developers
imho there is not.

Users can write there own base13 or base26 (whatever) outputted format, when school assign it.
.


Alexander Brevig

unread,
Jan 22, 2015, 8:07:49 AM1/22/15
to Arduino Developers
Here's my $0.02;

Use a fluent API. As an example:

Serial.print(format(15).asBinary().withSign().withLength(8)); //> +0b00001111
Serial.print(format(15).asHex().withLength(4)); //> 0x000F
Serial.print(format(0b1111).asDesimal().withLeadingZeros().withPrecision(4).withLength(8)); //> 0015.0000 (or 015.0000 if . should be concidered part of length)

format could then be a function which returns a new object with as* methods. Those methods in turn return a new object with the formatting with* methods. These methods return the this* pointer, and when it is evaluated it should have an operator for converting to a string/char* (or it could be Printable?).

Just another option, is all.
//Alexander / AlphaBeta

Tom Igoe

unread,
Jan 22, 2015, 8:14:18 AM1/22/15
to devel...@arduino.cc
Interesting idea. What are the memory and computational burdens relative to the other proposal?

T

Mikael Patel

unread,
Jan 22, 2015, 9:28:02 AM1/22/15
to devel...@arduino.cc
Hum, how is this defined in the C++ standard iostream? Cheers! Mikael

David Mellis

unread,
Jan 22, 2015, 5:15:31 PM1/22/15
to Arduino Developer's List
On Wed, Jan 21, 2015 at 11:31 AM, c.ma...@bug.st <c.ma...@bug.st> wrote:
Il 21/01/2015 13:30, Matthijs Kooijman ha scritto:
This could partly be fixed by making the Print::ForceSign etc. just
bitmask values and (probably using templates) make formatControl accept
different number of arguments and just or'ing them together.

and +1 here too. This looks like to move towards Paul's proposal to "sum" options, but with a more clear syntalx like:

    Serial.println(137, HEX(8));
    Serial.println(137, HEX(8), FILLZERO, SIGNED);

I like this. It seems much more familiar / Arduino-like than Serial.println(137, HEX(8) + FILLZERO + SIGNED);

Yes, it's weird that you can pass the arguments in any order, but the code looks much more Arduino like (to me, at least) than adding together multiple numbers in a single argument.

Also, it might be good to add some prefix to the option names, e.g. "PRINT_SIGNED" or "FORMAT_SIGNED". Just to avoid current / future name conflicts. 

Michael Jonker

unread,
Jan 26, 2015, 6:22:41 PM1/26/15
to devel...@arduino.cc
Dear all, please find a status update in the attached PDF file.
 
(My apologies for including a pdf. The web based editor is too primitive for writing large text files).
 
For Summary/Recommendation/Discussion/Questions please see the section with the same name.
 
Michael Jonker
 
Arduino Print API.pdf

Alexander Brevig

unread,
Jan 26, 2015, 7:34:36 PM1/26/15
to Arduino Developers
Hi all!

Just wanted to chime in again:

I like the HEX & FORCESIGN option best and I completely agree with all the arguments given in the pdf.

One more up-side: This allows library authors to provide formatters for type they provide i.e IPADDRESS or maybe even MORSE (just toying now, but having an instance per formatter is good for extensibility).

Final nit-picky comment. If a fluent API were to be considered it would be important to differ between as* and with* I think.
HEX.withSize(6).withLeadingZeros() reads better then asHEX().asSize(6).asLeadingZeros().

Best regards,
//Alexander 

--

Michael Jonker

unread,
Jan 27, 2015, 2:32:56 AM1/27/15
to devel...@arduino.cc


On Tuesday, January 27, 2015 at 1:34:36 AM UTC+1, alexanderbrevig wrote:
Hi all!

Just wanted to chime in again:

I like the HEX & FORCESIGN option best and I completely agree with all the arguments given in the pdf.

One more up-side: This allows library authors to provide formatters for type they provide i.e IPADDRESS or maybe even MORSE (just toying now, but having an instance per formatter is good for extensibility).

Final nit-picky comment. If a fluent API were to be considered it would be important to differ between as* and with* I think.
HEX.withSize(6).withLeadingZeros() reads better then asHEX().asSize(6).asLeadingZeros().

Best regards,
//Alexander

Nice suggestions,
I thought already of implementing a PrintFormat hierarchy, but  defining something like a PrintFormatable interface sounds interesting. Have to see if the compiler does not choke when defining an interface class with template stubs.

I also agree with your nit-picky comment: .asHEX().withLeadingZeros() reads better. Actually as there is a predefined HEX PrintFormat instance, HEX.withLeadingZeros() will work also. The asHex() should remain, because you want to say myPrintFormat.asHex()  (if you don't like the also possible myPrintFormat & HEX).
I am getting in favor of also including the .asHex() .withLeadingZeros() methods (while keeping the operator based methods in as well.

M

Matthijs Kooijman

unread,
Jan 27, 2015, 4:06:57 AM1/27/15
to devel...@arduino.cc
Hey Michael,

your proposal looks nice. Just a nitpick: it looks like you could have
written the same thing using markdown on a github issue or gist, without
any need for a PDF. Perhaps something to consider for a future version.

Looking at your document, it looks like a very sane approach.

You say there are two bytes worth of formatting options, but I count 21
bits. What's up with that?

You've defined SIZE and STRICT, where just SIZE indicates a minimum size
and the combination defines an exact size. Wouldn't it make more sense
to have MINSIZE and SIZE for that? A bit more verbose for the common
case though. Perhaps SIZE and EXACTSIZE (or STRICTSIZE)? In either case,
I guess the implementation should remain the same, just the API changes.

> > One more up-side: This allows library authors to provide formatters for
> > type they provide i.e IPADDRESS or maybe even MORSE (just toying now, but
> > having an instance per formatter is good for extensibility).
> I thought already of implementing a PrintFormat hierarchy, but defining
> something like a PrintFormatable interface sounds interesting. Have to see
> if the compiler does not choke when defining an interface class with
> template stubs.
I'd really want to see something like this as well. My initial thought
was mostly to allow printing of custom datatypes, but now I'm thinking
that you might want to print existing datatypes in a different format as
well (e.g. print uint32_t as an IP address).


An idea I had was to make a distinction between a formatter, and
format options. For example, IPADDRESS would be a formatter, while
FILLZERO would be an option. The operator& (or whatever we'd use) would
be something like:

NumFormatter operator&(NumFormatOption opt);

Then you'd probably also need an operator& defined on NumFormatOption,
or perhaps a cast-to-NumFormatter operator, to allow passing just a
format option.

Alternatively, you'd just have an operator& that merges NumFormatters
together and not have the distinction - this might actually be fine as
well.

One thing that I'm not sure how to implement is to limit some options to
specific types (e.g only allow precision for float/double) and share
other options between types (size applies to float/double/int/char*).
With "limit" I mean to throw an error at compiletime.

This limiting is probably easy if you explicitly specify a formatter
(e.g. FloatFormatter) and then append options to it using &. You could
use different types for different options (IntOptions, FloatOptions, or
even more specific like RadixOption, LeftAdjustOption) and then only
implement operator& on the formatter for the options supported. However,
when no explicit formatter is specified, the compiler has to deduce it
from the value type passed to print, which happens only after resolving
the operator & stuff.

I _can_ see a way to implement this with the multiple-argument approach.
E.g. you'd have:

Serial.print(some_float, SIZE(10), PRECISION(2)); // OK
Serial.print(some_int, SIZE(10), PRECISION(2)); // Error



In any case, any formatter class would then have ::print methods for all
types it knows how to print. A simple template implementation of
Print::print would then be:

template <typename T, typename F>
size_t Print::print(T val, F formatter) {
return F::print(val);
}

This allows passing any formatter object that has an applicable print
method, to print an object of any type. Note that this does _not_ use
virtual methods and a common "Formatter" superclass, since that requires defining a limited set of types in advance that can be printed.

Since you'd also want to print things without explicitly choosing a
formatter to use, you'd also want something like:

template <typename T>
size_t Print::print(T val) {
return T::print(val);
}

This allows defining a _static_ print function on a user-defined class,
which will be called to print objects of that type. Note that this is
different from the current Printable interface, which uses a virtual
method to define how to print objects. The downside of a virtual method
is that it adds extra memory overhead, which might be a problem if you
have hundreds of small objects that you also want to print. By
statically calling a T::print method, you resolve the print method to
use at compiletime, without any runtime overhead. The downside is that
you have to actually _know_ the object type at compiletime - if you have
a big hierarchy of classes and only have a superclass reference to an
object, the virtual Printable approach is still useful.

This might need some clever use of auto return types an SFINAE to make
the compiler not try the above implementation for primitive types like
int.

Alternatively, you could also go through a Formatter. e.g.:

template <typename T> struct DefaultFormatter;

template <typename T>
size_t Print::print(T val) {
return DefaultFormatter<T>::print(val);
}

This would require explicit specialization of the DefaultFormatter
class. E.g. for numbers you'd write:

struct NumFormatter {
size_t print(int val);
... print methods for other num types ...
};

template<> struct DefaultFormatter<int> : public NumFormatter { }
... idem dito for other num types ...

Then, for a custom type you'd do the same:

struct MyType { };
struct MyTypeFormatter {
size_t print(const MyType& val);
}

template<> struct DefaultFormatter<MyType> : public MyTypeFormatter { };

This might be a bit more complex, but could be a bit more consistent as
well.


Looking at development - it seems that it's good to focus on number
formatting first. However, it makes sense to also implement something
generic like above, and have number formatting use that generic
mechanism, so we open up the way for more formatting control later on.

I'm sorry if this post became a bit too lengthy and complicated, but
hopefully my thoughts help figuring this out. I'm aware that these
aren't clear-cut suggestions, since I haven't figured everything out
myself yet :-)

Gr.

Matthijs
signature.asc

Michael Jonker

unread,
Jan 27, 2015, 7:52:34 AM1/27/15
to devel...@arduino.cc

Hoi Matthijs,

PDF, point taken, I will look into copy pasting markdown text into this window or finding an alternative interface to this forum.

You counted up to 21 because 5 bits or radix overlap with 5 bits of float. I think of formalizing this by specialization of the PrintFormat class into FloatPrintFormat and IntegerPrintFormat, such that print(3.14, HEX) or print(42 PRECISION(5)) will produce an error. If someone want to print float in hex he will have to define a PrintFormatable (or whatever name) to do this.

I only scanned you lengthy message diagonally, will read in detail when I have time.

But what came to me (maybe you suggested this), that I can implement the so called formatting engines PrintNumber, PrintFloat, as Formatters of the specialized PrintFormat class.
Will look into that.

Doei, M

Tom Igoe

unread,
Jan 27, 2015, 11:29:21 AM1/27/15
to devel...@arduino.cc
Could you post some examples of what it would look like when implemented in an Arduino sketch please?

And +1 to Mattjs’ point about using a gist, that way it could stay in the issues list as well.

t.

Bill Perry

unread,
Apr 8, 2015, 3:51:18 AM4/8/15
to devel...@arduino.cc
Why oh why is there such a desire to re-create the wheel in Arduino and do things in a non standard proprietary way instead of using and building on existing tools?
Why not just add printf() support to the Print class and be done with it instead of inventing something new that isn't as powerful?
printf() support exists today, it works, and is already debugged and is fully documented all over the place.
I added information quite some time ago (couple of years ago) to the playground Printf page:
http://playground.arduino.cc/Main/Printf
showing users how to add printf() support to the Print class themselves.

Once added it is really nice as you can then do things like:

Serial.printf("Hello World\n");
lcd.printf(F("hello");

Serial.printf(F("The time is: %02d:%02d:%02d\n"), hr, min, sec);

It works for anything that uses the Print class and supports putting the format strings in flash on the AVR.

Also, after talking with Paul, Print class printf() support is now part of the teensyduino releases for Teensy products.
So when using Teensy products, you get Print class printf()  support "out of the box".
(For the other boards, you will still have to modify the Print class in the core library)

In the grand scheme of things, I believe that using these type of proprietary ways of doing things really don't buy much
since the user has to learn something new either way. And when using these types of proprietary things the functionality is
often more limited and less documented than the existing standard C tools.

In my view going down these sorts of proprietary ways of doing things is simply
corrupting young inexperienced minds rather than elevating them by teaching them information that can help them not only in Arduino but
outside of Arduino as well.

--- bill

Roger Clark

unread,
Apr 8, 2015, 4:12:54 AM4/8/15
to devel...@arduino.cc
Bill,

I totally agree with you. In fact sprintf is already implemented and available.

So its probably not a huge leap to printf, not that its needed if you can use sprintf

The only major drawback of both printf and sprintf, is that the generic versions of these libs are very large and hence why they've not been part of the default build, as they'd take a up a substantial part if the available flash memory on a ATMega328P

I'm not sure how big the cut down version of sprintf is. All I know that it doesn't display floating point eg. %f or %g correctly (or at all)

if people really need proper sprintf etc, they need to think about moving to a newer processor like the Due or Zero or 3rd party hardware like STM32 based Maple mini (my $4 favorite), or the PIC32 or the Teensy, all of which are Arduino compatible (or mostly compatible) now.

--

Bill Perry

unread,
Apr 8, 2015, 4:24:02 AM4/8/15
to devel...@arduino.cc

the default underlying printf support on the AVR  which does not have the floating point support is just under 2k. i would not call that "substantial". adding the floating point about doubles that. so often even using the full floating point version is still acceptable.  I use it all the time. there are so many other areas of the core libraries that can be tweaked to get back a few k, so that when those changes are made the extra 2k for printf support can be "free".

You received this message because you are subscribed to a topic in the Google Groups "Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/a/arduino.cc/d/topic/developers/7KpdLDgFsO0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to developers+...@arduino.cc.

Brian Cook

unread,
Apr 8, 2015, 4:24:39 PM4/8/15
to devel...@arduino.cc

> The only major drawback of both printf and sprintf, is that the
> generic versions of these libs are very large...

Is it?

- Brian

Álvaro Lopes

unread,
Apr 8, 2015, 4:55:42 PM4/8/15
to devel...@arduino.cc, Brian Cook
Considering that you have to support "%f", which will bring in most of floating-point routines, *even if you don't use it*, yes it is.

Alvie

Brian Cook

unread,
Apr 8, 2015, 5:00:47 PM4/8/15
to devel...@arduino.cc

The only major drawback of both printf and sprintf, is that the
generic versions of these libs are very large...
Is it?
Considering that you have to support "%f", which will bring in most of floating-point routines, *even if you don't use it*, yes it is.

The size is a major drawback.

It is not the *only* major drawback.

The question was meant to get Roger Clark (and bill) to think about the assumptions they were making (like the one about "only major drawback").  Assumptions that led them to make claims that may not be correct.

- Brian

Bill Perry

unread,
Apr 8, 2015, 6:20:38 PM4/8/15
to devel...@arduino.cc, bc...@rowdydogsoftware.com
I just did some tests on 1.6.1 and Print class printf support uses around 1230 to 1240 bytes more than the equivalent print routines.
i.e.
obj.print(intval) vs obj.printf("%d", intval)
obj.print(floatval) vs obj.printf("%f", floatval)
obj.printf(floatval, num) vs obj.printf("%6.numf", floatval)

The printf() support code usage is actually smaller than it used to be.

The arduino Print class proprietary routines drag in the same floating point math routines when floats are used so there is no real savings in floating point over printf() once you pay the initial 1.2k price of entry to use printf() support.
It is the floating point math support that eats most of the code space not the actual printf() support routines.

So overall, I don't really view the additional code size as a "major" drawback since the size of the additional code is not that big of factor for most projects.

yes there are some additional draw backs that may not work for some projects like some additional stack space usage over what the proprietary Print class routines use.
The biggest issue is really for floating point support since the way the AVR guys did their printf() support was to create separate libraries with different functionality that have to be manually specified vs automatically determining which library provides what is needed. This makes things difficult since the Arduino environment has never embraced using printf() support, so there is no way to set the linker options to enable printf() floating support from the IDE. With the 1.5/6 IDE environment it is at least possible without having to rebuild the IDE code itself. The user would have to go in and modify his platform.txt file to turn it on.
While not particularly difficult, it is a stumbling block that may be beyond many novice entry level users.
(It would be nice if the IDE had a way to control this)
And then there is the issue that once you turn on the floating point support you do pay the price for it regardless of whether you are using it whenever you use any of the printf() support routines. This means that if floating point is enabled and you don't  use you pay a size price of 1550 bytes.
But by default, when printf() floating point is not available, the floating point will not be dragged in.


The reason why I brought this up is that I wanted to start to examine the philosophy of
continuing to use and expand on proprietary Arduino ways of doing things vs starting to embrace standardized ways of doing things which are already available.

Even if Arduino does expand the format control to the proprietary routines in the Print class I see no reason why the IDE can't also add printf() support to the Print class.
Yes there are times when either one is more appropriate by why make that decision for the user?
I much prefer the approach that was taken by Teensduino in that printf() support in the Print class is available "out of the box" and it is your option whether or not to use it. If you use it, it comes with the understanding that there is a bit of code size and ram stack price to pay for it.

I also include printf() support in my openGLCD library text class since I want to enable users to have access to easy text formatting and I didn't want to require them to have to modify the IDE Print class to get it.

--- bill

Paul Stoffregen

unread,
Apr 8, 2015, 7:02:25 PM4/8/15
to devel...@arduino.cc
On 04/08/2015 03:20 PM, Bill Perry wrote:
Even if Arduino does expand the format control to the proprietary routines in the Print class I see no reason why the IDE can't also add printf() support to the Print class.
Yes there are times when either one is more appropriate by why make that decision for the user?

We ought to have a FAQ, or Frequently Requested Features (FRF), or Frequently Rehashed Discussions (FRD).  Bill, I'm sure you've been around long enough to have seen the consistent answer.  Maybe David Mellis will reply again?

Every time this has been asked, the concern has always been that making printf() part of the published Arduino API will lead to experienced programmers to publishing cryptic examples, which novices can't understand.  You asked "why make that decision for the user", and as I recall, that exact question has been answered many times, better than I just summarized in this paragraph.


I much prefer the approach that was taken by Teensduino in that printf() support in the Print class is available "out of the box" and it is your option whether or not to use it. If you use it, it comes with the understanding that there is a bit of code size and ram stack price to pay for it.

Yes, I added this for Teensy.  But Teensyduino is used by only a very tiny portion of the Arduino community.  Extra features for Teensy have almost no impact on the wider Arduino community.  Often features I implement for Teensy get contributed back to Arduino.  If Serial.printf() is desired, I'd be happy to prepare a pull request.  My implementation is well tested for both AVR and ARM architecture.

Just because you can do a thing does not necessarily mean you should do that thing.  Just because I do a thing doesn't necessarily mean Arduino should.

However, I personally find Serial.printf() to be very convenient.  But I have 25 years of C programming experience, which I recognize limits my ability to see how novices perceive source code.

Bill Perry

unread,
Apr 8, 2015, 8:52:36 PM4/8/15
to devel...@arduino.cc
On 2015-04-08 18:02, Paul Stoffregen wrote:

Just because you can do a thing does not necessarily mean you should do that thing.  Just because I do a thing doesn't necessarily mean Arduino should.
Completely agree. However, the same is true for Arduino.
I guess what I find the most frustrating about the Arduino environment is that it almost seems to go out of its way at times to avoid reusing existing tools by re-inventing the wheel. In some cases what we end up with is not necessarily "better" but just different.

Tom Igoe

unread,
Apr 9, 2015, 8:52:52 AM4/9/15
to devel...@arduino.cc
Thanks for summarizing the standard response on this, Paul. Having posted on it recently, I’ll let that, and your summary, speak for me this time.

This idea of a FRF or FRD is interesting, though. I’ve been mulling it over since reading your post last night thinking of ways to make it most constructive. For example, summarizing the standard answer is necessary, but perhaps it would also be useful to summarize the most common arguments against the standard answer, and when possible, the arguments that are most potentially convincing?  I can think of a couple of FRDs where I’ve offered suggestions of things that would change my mind, if not others, but that never get picked up on. Would that be useful, or would it be overkill?

Victor Aprea

unread,
Apr 9, 2015, 9:04:01 AM4/9/15
to Arduino Developers
Paul, Tom,

I think a dev list summary historical reference of some kind is generally a good idea too. I think references to the group threads surrounding topics would be good to include in such a thing as well. 

The only cautionary thing I have to say on it is that it may lend itself to in-group behaviors on the list. Just as we ought not to default to RTFM responses to new-comers to Arduino, likewise we should not default to RTFRD for newcomers to the dev list either.

Kind Regards,
Vic

Victor Aprea // Wicked Device

Tom Igoe

unread,
Apr 9, 2015, 9:07:42 AM4/9/15
to devel...@arduino.cc
I agree, Vic, thanks. That’s why I want to make sure it stays constructive. I’d want to edit it for as neutral a tone as possible too, maybe even ask a colleague who’s not from this list or this field to take an editing pass from time to time. It’s easy for us to get jaded in our answers and that’s not gonna help newcomers.

William Westfield

unread,
Apr 10, 2015, 7:01:36 PM4/10/15
to devel...@arduino.cc
> I’d want to edit it for as neutral a tone as possible

There’s the layout used in “Voter information” for ballot measures in the US (or in California, anyway.)

Summary of the proposal.
Actual text of the proposal.
Argument FOR the proposal.
Rebuttal to Argument FOR the proposal.
Argument AGAINST the proposal.
Rebuttal to Argument AGAINST the proposal.

I like it as a format, although it sometimes breaks down in practice.

BillW
WestfW


Reply all
Reply to author
Forward
0 new messages