String.format help

Hey Guys, im trying to work out the new String.format(); that was added in 0.4.6

im trying to simplify this

        String voltage = String(lipo.getVoltage(), 2);
        String soc = String(lipo.getSOC(), 1);
        Particle.publish("Battery", String(voltage + "V " + soc + "%"), 60, PRIVATE);

Any idea’s?
this gives an error (pointing to a line 5 lines above)

        Particle.publish("Battery", String.format("%1.2fV %2.1f%%" , lipo.getVoltage(), lipo.getSOC()), 60, PRIVATE);

@Hootie81,

there might be some issue there. I tested using the unit-testing example and it failed as well:

1 Like

Something like this worked:

void setup(){
        String oldstr = "";
        String newstr = oldstr.format("%1.2f %s %s please", 3.120102, "hey", "hey");
        Particle.publish("new","newstr");
}

Not sure why the String.format is throwing an error.

Ahhhh silly me! it took me half a day the other day to figure out why i couldn’t get String.toInt() to work… then i realized “String” refers to the String variable name rather than typing String!

And this is exactly the same!

String data = "";

Particle.publish("Event", data.format("Yay %s", "it works"), 60, PRIVATE);

But it might also be useful to have a static method format() (and others) in String to allow what you tried to do.

Particle.publish("Battery", String.format("%1.2fV %2.1f%%" , lipo.getVoltage(), lipo.getSOC()), 60, PRIVATE);

How about this @mdma?

2 Likes

@Hootie81 You need to use the double colon to call the method on the String class.

Particle.publish("Battery", String::format("%1.2fV %2.1f%%", lipo.getVoltage(), lipo.getSOC()), 60, PRIVATE);

@kennethlimcp how did the unit test example fail for you?

As far as I know @ScruffR it wouldn’t be possible to make String.format do the right thing in C++. It has to be String::format.

1 Like

@jvanier, I guess I’ve confused it with C# :blush: (day job language)

Anyways the example for the docs: https://docs.particle.io/reference/firmware/photon/#format-

Doesn’t work.

Unit test runs fine but not when i use the same line in the Web IDE.

String newStr = String.format("%d %s %s please", 3, "lemon", "curries")==String("3 lemon curries please"));

Try

String newStr = String::format("%d %s %s please", 3, "lemon", "curries");
bool equal = (newStr == String("3 lemon curries please"));

Notice the :: in String::format

My point is, the docs is giving a wrong example that everyone can simply copy, compile and stab themselves in the eye.

Issue opened here: https://github.com/spark/docs/issues/203

Thanks @jvanier ill set up another photon to test this… way too much awesome data coming in from my water monitor now :slight_smile: my last shower was 70L at a rate of 15L/min hrmm time for a new water saving shower head i think!

1 Like

There was a typo in the docs - now fixed. I’d really prefer to avoid the :: but not sure how to do that short of declaring a global instance that folks can use. E.g.

static String string;
string.format("hello %s","world");

And I think this would cause further confusion if someone mistypes String for string.

The easiest workaround might be to document a free function FormattedString(const char *format, ...) that calls the static String::format

Since format is static it’s actually has the surprising behavior right now that you can do String x; x.format("%d", 10"); but that doesn’t use x in any way. It doesn’t mutate x and assign it the formatted value. I’m not saying it should, but it’s puzzling.

Providing a free standing format function and not documenting the static String::format would solve both of these issues: how to create a new formatted string and avoid the surprise that calling format on an instance doesn’t mutate that instance.

1 Like

these are good points. I also wonder if format could be an instance method, so

String().format(...);

and

String msg;
msg.format(...);

work as expected.

Since format hasn’t been in the firmware for that long it should be OK to change it to an instance method. String().format(...) does look a little weird however…

From a memory management perspective, it makes a lot of sense because in many use cases you can create a global String then use format on it in the loop(). You don’t need to re-allocate the buffer if it’s big enough.

The other possibility is to make a varargs contructor to String (I’m not sure if that’s even possible) and ditch format altogether.

2 Likes

Im getting some crashes now using String::format(), well i think its that anyway… SOS and 5 flashes - Usage Fault hrmmm any ideas?