简体   繁体   中英

Handling of const char* on ESP32

I'm working on making some Spotify API calls on an ESP32. I'm fairly new to C++ and while I seem to got it working how I wanted it to, I would like to know if it is the right way/best practice or if I was just lucky. The whole thing with chars and pointers is still quite confusing for me, no matter how much I read into it.

I'm calling the Spotify API, get a json response and parse that with the ArduinoJson library. The library returns all keys and values as const char* The library I use to display it on a screen takes const char* as well. I got it working before with converting it to String, returning the String with the getTitle() function and converting it back to display it on screen. After I read that Strings are inefficient and best to avoid, I try to cut out the converting steps.

void getTitle()
{
// I cut out the HTTP request and stuff
        DynamicJsonDocument doc(1024);
        DeserializationError error = deserializeJson(doc, http.getStream(), );
        JsonObject item = doc["item"];

        title = item["name"];  //This is a const char*

}
const char* title = nullptr;

void loop(void) {

    getTitle();

    u8g2.clearBuffer();
    u8g2.setDrawColor(1);
    u8g2.setFont(u8g2_font_6x12_tf);
    u8g2.drawStr(1, 10, title);
    u8g2.sendBuffer();
}

Is it okay to do it like that?

This is not fine.

When seeing something like this, you should immediately become suspicious.

This is because in getTitle , you are asking a local object ( item ) for a pointer-- but you use the pointer later, when the item object no longer exists .

That means your pointer might be meaningless once you need it-- it might no longer reference your data, but some arbitrary other bytes instead (or even lead to crashes).

This problem is independent of what exact library you use, and you can often find relevant, more specific information by searching your library documentation for "lifetime" or "object ownership".

FIX

Make sure that item (and also DynamicJsonDocument , because the documentation tells you so,) both still exist when you use the data. eg: like this:

void setTitle(const char *title)
{
    u8g2.clearBuffer();
    u8g2.setDrawColor(1);
    u8g2.setFont(u8g2_font_6x12_tf);
    u8g2.drawStr(1, 10, title);
    u8g2.sendBuffer();
}

void updateTitle()
{
    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, http.getStream(), );
    JsonObject item = doc["item"];

    setTitle(item["name"]);
}

See also: https://arduinojson.org/v6/how-to/reuse-a-json-document/#the-best-way-to-use-arduinojson

Edit: If you want to keep parsing/display update decoupled

You could keep the JSON document "alive" for when the parsed data is needed:

/* "static" visibility, so that other c/cpp files ("translation units") can't 
 * mess mess with our JSON doc directly
 */
static DynamicJsonDocument doc(1024);
static const char *title;

void parseJson()
{
    [...]
    // super important to avoid leaking memory!!
    doc.clear();
    DeserializationError error = deserializeJson(doc, http.getStream(), );

    // TODO: robustness/error handling (e.g. inbound JSON is missing "item")
    title = doc["item"]["name"];
}

// may be nullptr when called before valid JSON was parsed
const char* getTitle()
{
    return title;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM