Pointer "Guru Meditation Error" (local object....stored statically)

Hi there.

I have a problem with a pointer (local object), which i save statically in a class.

In the first run, the pointer is checked (is NULL) and no further action is taken.
In the second pass, the pointer is checked (is not NULL) and the static object is accessed.
However, this was local and appears to have already been removed from memory. so there is a guru error.
how can i work around this problem?

class:

#ifndef Test_h
#define Test_h

#include <Arduino.h>

class Test {
  public:
    static Test * _test;
    Test(String description){ Serial.print("constructing '" + description + "'..."); Description = description; Serial.println("done"); }
    String Description = "";
    String Info = "";
    void        SerialPrintLn(){ Serial.println("object: " + Description + (Info.length()>0 ? " " + Info : "")); };
    static void SerialPrintLnStatic(){ if(_test){ Serial.println("static: " + _test->Description + (_test->Info.length()>0 ? " " + _test->Info : "")); } else { Serial.println("static: test is null"); } };
    
    static void StoreReference(Test * test){
      _test = test;
      _test->Info = " (has static reference)";
    }
};
Test * Test::_test;
#endif

Source code which I execute in a loop (according to serial command “test”):

        Test::SerialPrintLnStatic();
        Test test1("Test Object One");
        Test test2("Test Object Two");
        test1.SerialPrintLn();
        test2.SerialPrintLn();
        Serial.println("");
        Test::StoreReference(&test1);
        Test::SerialPrintLnStatic();
        test1.SerialPrintLn();
        test2.SerialPrintLn();

I run the source code “test” twice. the output is as follows:

14:03:39.754 -> ### Setup ###
14:03:39.754 ->  - CPU Clock: 240 Mhz
14:03:39.787 ->  - Heap: 362820 byte (free)
14:03:39.787 ->  - Flash: 4 MB | 80 Mhz
14:03:39.787 ->  - Chip Revision: 1
14:03:39.787 ->  - SDK Version: v3.2.3-14-gd3e562907
14:03:39.787 ->  - Serial
14:03:39.787 ->    Buffer: 0 bytes
14:03:39.787 ->  - Pin Modes
14:03:39.787 ->  - Set Pins High/Low
14:03:39.787 -> ### Setup finished ###
14:03:43.168 -> ### TEST ### (1. run)
14:03:43.168 -> static: test is null
14:03:43.168 -> constructing 'Test Object One'...done
14:03:43.168 -> constructing 'Test Object Two'...done
14:03:43.168 -> object: Test Object One
14:03:43.168 -> object: Test Object Two
14:03:43.168 -> 
14:03:43.168 -> static: Test Object One  (has static reference)
14:03:43.168 -> object: Test Object One  (has static reference)
14:03:43.201 -> object: Test Object Two
14:03:43.201 -> Test done
14:03:44.681 -> ### TEST ### (2. run)
14:03:44.681 -> Guru Meditation Error: Core  1 panic'ed (LoadStoreError). Exception was unhandled.

Please provide a full code that actually compiles.

Your code is insufficient for a proper answer. "Test::_test" is never set to NULL, so you may access a bad pointer. You are assigning a local/static instance of Test to _test in the snippet which is a bad idea. You should look up on how to implement a "singleton" which is apparently what you are trying.

gfvalvo:
Please provide a full code that actually compiles.

Small working version of code (in addition to the class test.h from my first post.):

#include "Test.h"

const long SERIAL_BAUD  = 115200;//Serial

String serialBuffer = "";
char   serialTerminatorChar = '\n';

void setup() {
  Serial.begin(SERIAL_BAUD);
}

void loop() {
  //### SERIAL ###
  if (Serial.available() > 0) {
    char serialChar = Serial.read();
    if(serialChar!=serialTerminatorChar){
      serialBuffer += serialChar;
    } else {
      if(serialBuffer=="test"){
        Serial.println("### TEST ###");
        
        Test::SerialPrintLnStatic();
        Test test1("Test Object One");
        Test test2("Test Object Two");
        test1.SerialPrintLn();
        test2.SerialPrintLn();
        Serial.println("");
        Test::StoreReference(&test1);
        Test::SerialPrintLnStatic();
        test1.SerialPrintLn();
        test2.SerialPrintLn();

        Serial.println("Test done");
      } else { Serial.println("command unknown"); }
      serialBuffer = "";
    }
  }
}

After I pass “test” twice on the command line, a guru meditation is triggered:

### TEST ### (1. run)
static: test is null
constructing 'Test Object One'...done
constructing 'Test Object Two'...done
object: Test Object One
object: Test Object Two

static: Test Object One  (has static reference)
object: Test Object One  (has static reference)
object: Test Object Two
Test done
### TEST ### (2. run)
Guru Meditation Error: Core  1 panic'ed (LoadStoreError). Exception was unhandled.

By the way, it makes no difference whether I specify the static pointer with Test * Test :: _ test; or Test * Test :: _ test = 0;.

When loop() exits after first loop, both test1 and test2 are destroyed, but test1 is still assigned to Test::_test. When loop() runs the second time, you call "Test::SerialPrintLnStatic()" which accesses the pointer to the now destroyed test1. Before "Serial.println("Test done")" add "Test::StoreReference(NULL)" to remove the invalid reference. Another bugger in your code is the line "Test * Test::_test;" which should have been "Test * Test::_test = NULL;".

the code should only be an example.
the question that arises is: how can you ensure with a pointer that the object has not already been discarded?

DrDooom:
the question that arises is: how can you ensure with a pointer that the object has not already been discarded?

You can’t except by writing the program correctly. This will always happen when you use a pointer to an object whose lifetime has expired.

The problem remains when I write a class and make it available to others .... in the class I cannot assume a valid reference to a corresponding object (e.g. SdFile, Stream, etc). This is a pity. i thought there was a way to check the validity.
Nevertheless many thanks.

DrDooom:
The problem remains when I write a class and make it available to others .... in the class I cannot assume a valid reference to a corresponding object (e.g. SdFile, Stream, etc).

That would indicate that you are using a bad approach to the task. Usually, you would use dynamically created objects to pass as arguments which will live until they are explicitly destroyed. If you would post a sample which shows an actual usecase, it would be possible to help you.

SomeClass* Wrong() {
  SomeClass result(1,2,3);
  return &result;
}

SomeClass* Right() {
  return new SomeClass(1,2,3);
}

SomeClass* ptr = Wrong();
ptr->DoSomeThing() //Error, ptr is useless

ptr = Right();
ptr->DoSomething(); //Yeah, that's right

delete ptr; //Remember to destroy ptr

The problem with this is that is uses dynamic memory which should be avoided on MCU's with no MMU.