Return struct[n] da funzione

Buongiorno,
ho una funzione da cui mi aspetto dati struct per un numero n di elementi.

    struct outGpio 
    {
        int     gpio  ;
        String  stato ; 
        int     time  ;
    };


la funzione riceve una stringa json dal server tipo :

{
          "wrt":false
         ,"pin":[
                  {"gpio":5,"stato":"on","time":0}
                 ,{"gpio":9,"stato":"on","time":5}
                ]
}

all'interno della funzione, ho definito :

        outGpio retGpio[pinSize] ;

dove pinSize è il numero di elementi che andrò a memorizzare (in questo caso 2)
e valorizzo con :

          int i = 0; 
          for (JsonObject aa : pin) 
          {
              retGpio[i].gpio   = aa["gpio"] ;
              retGpio[i].stato  = aa["stato"].as<String>(); ;
              retGpio[i].time   = aa["time"] ;
              i++; 
          }

ora sono in difficoltà su come restituire i dati.
immagino di non poter definire la funzione come outGpio (nome struct) visto che sono più elementi, ma dover utilizzare i puntatori, quindi outGpio*

outGpio* nomeFunzione()
{

}

ma nel return ???

Ma neppure cosi puoi.
La variabile/array "retGpio" che crei nella funzione, ha vita solo dentro alla funzione (non è in memoria globale). Quando la funzione esce, tutto quel blocco di dati viene distrutto. Se fai return di quel puntatore, quello punterà... a una zona di memoria distrutta (deallocata).
Secondo me puoi:

  1. creare l'array "retGpio" fuori dalla funzione e passarlo alla funzione come parametro (ma forse a priori non sai quanti elementi ci sono)
  2. dentro la funzione usi allocazione dinamica usando new (e quindi allochi l'array in spazio memoria globale) e poi fai return del puntatore.

confermo che a priori non so quanti elementi possano esserci.

ho provato la soluzione che dell'allocazione dinamica, ma probabilmente ho sbagliato qualcosa.
ora ci riprovo.
(normalmente utilizzo PHP, ho bisogno di tempo per entrare nell'ottica dei dati C)

grazie

Io ti suggerisco di cambiare la struttura.

  1. le String sono oggetti ed è difficile con new calcolare lo spazio corretto della struct di cui un elemento è un oggetto.
  2. le String possono portare a problemi di spezzettamento di memoria.

Mi pare che se il tuo "stato" è un intero (dove ogni valore ha un significato) semplifichi di molto..
Poi, quando stampi, allora converti quel valore in testo adeguato.
dovrai cambiare qualcosa qui:
retGpio[i].stato = aa["stato"].as<String>();
Tu sai quali valori testo possono esserci ? sono "on" e "off" ?

...
String str=aa["stato"].as<String>();
if(str=="on")
  retGpio[i].stato  = 0;
else
  retGpio[i].stato  = 1;

Con sizeof(struct outGpio) hai dimensione di un elemento, moltiplicalo per quanti elementi hai e hai quanto spazio allocare usando new

"stato" attualmente ha come valori "on" e "off"
ma posso tranquillamente cambiarli dal server in 0 e 1.

Ma ora ho difficoltà nel dato da restituire in return.

ho definito la funzione in :

  outGpio* outputTemp()

e la variabile in :

 outGpio* retGpio = new outGpio[pinSize] ;

valorizzati i campi come prima.

Ma rimane l'errore nel return

  return retGpio*;  // errato

error: 'retGpio' was not declared in this scope; did you mean 'outGpio'?

  return retGpio;  // errato

error: 'retGpio' was not declared in this scope; did you mean 'outGpio'?

Non ho ben capito perché impelagarti nel restituire una struct, o un suo puntatore, o fare allocazioni dinamiche. Sarà che per mia formazione cerco sempre la soluzione più semplice e pratica... :wink:

Definisci l'array come globale (come ha suggerito anche @nid69ita) nel quale come numero di elementi magari imposti il numero massimo previsto (non sono mica infiniti...), e senza usare le famigerate "String" (basterebbe anche un solo char tipo "0" o "1" o direttamente come byte, in 0 o 1 per off e on ad esempio), poi alla funzione passi solo il json e con questo ti valorizza l'array e fai restituire il numero di elementi ricevuti (col quale poi estrarre i dati e fare quello che ti serve).

Esempio:

const int PINSIZE = 8; // Massimo numero di elementi

struct outGpio 
{
  int     gpio  ;
  bool    stato ; 
  int     time  ;
} retGpio[PINSIZE] ;
...
int caricaDati()
{
  int i = 0; 
  for (JsonObject aa : pin) 
  {
    retGpio[i].gpio   = aa["gpio"] ;
    retGpio[i].stato  = (aa["stato"] == 1);
    retGpio[i].time   = aa["time"] ;
    i++;
    if (i == PINSIZE) break;
  }
  return i;
}

OK di strade se ne possono intraprendere più di una.

Ma se ad ogni difficoltà cambio strada,
e quando imparo?

Sto vedendo centinaia di esempi su internet, che restituiscono puntatori da una funzione,
Perchè la mia va in errore?

error: 'retGpio' was not declared in this scope; did you mean 'outGpio'?

p.s: il campo "stato" l'ho modificato in 0 e 1

{
          "wrt":false
         ,"pin":[
                  {"gpio":5,"stato":0,"time":0}
                 ,{"gpio":9,"stato":1,"time":5}
                ]
}

E' bene iniziare sempre dalle cose più semplici e che si sanno gestire. Altrimenti rischi di scadere nella "Sindrome di Gundam" :wink:
Prova il metodo che ti ho indicato e vedrai che è tutto più semplice, poi potrai provare altro.

Per dirtelo dovresti mostrarci il tuo codice o almeno un codice che fa qualcosa del genere (es. il Json lo inserisci in una stringa costante invece di leggerlo) e che possiamo prendere, copiare e provare autonomamente.
Senza questo, stiamo parlando di cose talmente generiche che non ci sono possibili risposte per te utili.

Posta almeno un pezzo di codice.
return retGpio*; Non ha senso
E' normale return retGpio; a meno che retGpio non è fuori dalla funzione oppure stai usando nomi di variabili diverse (ti ricordo che il C è case-sensitive, Pippo è diverso da pippo)

Inoltre una cosa... ma pinSize da dove esce ? E' fisso ?
Se viene calcolato dal json dentro alla tua funzione e quindi è variabile di volta in volta, e quindi viene "calcolato" dentro a outputTemp(), beh... dovrai restituire anche questo valore (puoi avere un parametro modificabile ad esempio)

int numpin=0;
...
array=outputTemp(&numpin);   // numpin modificato dalla funzione, passato come puntatore a int

boh!
ho riscritto il codice interessato come indicato da nid69ita, estraendolo dall'intero sketch e funziona tutto.
domani lo ricontrollo.

#include <ArduinoJson.h>

    struct outGpio
    {
        int     id    ; 
        int     gpio  ;
        int     stato ; 
        int     time  ;
    };


void setup() {
  Serial.begin(115200);
  delay(1000);

   outGpio* data = letturaDati();
    int i = 0 ; 
  
    while (data[i].id > 0 )
    {
        Serial.print("Elemento ");
        Serial.print(i);
        Serial.print(": ");
        Serial.print(data[i].gpio);
        Serial.print(", ");
        Serial.print(data[i].stato);
        Serial.print(", ");
        Serial.println(data[i].time);        
     //   Serial.println(data[i].id);
        i++;
    }
  
  //serializeJson(data, Serial);
  
   delete[] data;           
}

void loop() {
  // put your main code here, to run repeatedly:

}


outGpio* letturaDati()
{
  StaticJsonDocument<200> doc;  
  String jsn = "{\"wrt\":false,\"pin\":[{\"id\":1,\"gpio\":5,\"stato\":0,\"time\":0},{\"id\":0,\"gpio\":9,\"stato\":1,\"time\":5}]}" ; 
  Serial.println(jsn); 


  deserializeJson(doc, jsn);          
  JsonArray  pin          = doc["pin"].as<JsonArray>();  
  int pinSize             = pin.size();
  Serial.print  (" numero cmd : ");
  Serial.println( pinSize);


  outGpio* retGpio        = new outGpio[pinSize] ;
  int i = 0; 
  for (JsonObject aa : pin) 
      {
        retGpio[i].id     = pinSize - i;        
        retGpio[i].gpio   = aa["gpio"] ;
        retGpio[i].stato  = aa["stato"]; //   .as<String>(); ;
        retGpio[i].time   = aa["time"] ;
        i++; 
        serializeJson(aa, Serial);
        Serial.println();               
      }

  return retGpio; 
  
}

grazie per essermi stati vicino!

1 Like

Vuoi restituire un array di struct da una funzione in C++ (Arduino). Ci sono vari modi per farlo, ma bisogna fare attenzione alla memoria, specialmente su microcontrollori con RAM limitata come ESP32 o AVR.

Allocazione dinamica + puntatore

  1. Definisci la struct fuori dalla funzione:
Struct outGpio {
  int gpio;
  String stato;
  int time;
};
  1. Scrivi la funzione che restituisce un outGpio* (array dinamico):
outGpio* parseJsonAndReturnArray(String jsonStr, size_t& size) {
  // Parsing JSON (usiamo ArduinoJson)
  StaticJsonDocument<512> doc;
  DeserializationError error = deserializeJson(doc, jsonStr);
  if (error) {
    Serial.println("Errore nel parsing JSON");
    size = 0;
    return nullptr;
  }

  JsonArray pinArray = doc["pin"];
  size = pinArray.size();  // numero di elementi

  // Allochiamo dinamicamente un array
  outGpio* result = new outGpio[size];

  int i = 0;
  for (JsonObject obj : pinArray) {
    result[i].gpio = obj["gpio"];
    result[i].stato = obj["stato"].as<String>();
    result[i].time = obj["time"];
    i++;
  }

  return result;
}
  1. Esempio d’uso nella funzione loop() o setup():
void setup() {
  Serial.begin(115200);
  String json = R"({
    "wrt": false,
    "pin": [
      {"gpio": 5, "stato": "on", "time": 0},
      {"gpio": 9, "stato": "on", "time": 5}
    ]
  })";

  size_t n;
  outGpio* result = parseJsonAndReturnArray(json, n);

  if (result != nullptr) {
    for (size_t i = 0; i < n; i++) {
      Serial.printf("GPIO: %d, Stato: %s, Time: %d\n", result[i].gpio, result[i].stato.c_str(), result[i].time);
    }
    delete[] result; // IMPORTANTE: liberare la memoria!
  }
}

Fammi sapere grazie

stamattina dovevo scegliere tra computer o mare.
Sinceramente ho optato per il secondo.
Chiedo scusa se ti leggo solo ora.
OK per le tue indicazioni.

ti ringrazio per avermi incuriosito anche con il size_t che non conoscevo,
e

non sono riuscito a trovare definizioni, ma penso serva ad accettare caratteri (") all'interno di una stringa senza dover inserire .

Si si esattamente. Buon mare è sempre un buon compromesso soprattutto in estate

Si tratta di una "raw literal string".
La differenza principale è che quando il compilatore la incontra, ignora tutti i caratteri che necessitano dell'escape come le virgolette \", ma anche ad esempio i caratteri di fine riga e ritorno a capo (\n \r).

Questo ti consente (tra le altre cose) di definire delle stringhe anche andando a capo senza doverti curare di quello che c'è in mezzo aumentando cosi la leggibilità del codice.

ok
Grazie