0

I have a Firmware project, developed in C++, where all driver are made with a class and there isn't a simply way to modify them.

the drivers are for internal peripheral of the uP and are implemented by a global instance of the class; now I must modify that function and allow a " re init" of the driver in a exception case or similar.

tha initialization of the driver is made in the constructor of the driver (is implemented in this way and I can't modify it) and there isn't an explicit way (a specific method or similar) to re call that funtions. So I need to force to recall the constructor of the class. It is not a problem to lose all information of the instance, so it is possible to delete the instance and remake it.

for example a part of the code is similar to that (from Mbed library):

class SPI {

public:

    SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel=NC);

    void format(int bits, int mode = 0);
[.....]

  ~SPI()

}

in other part of the code there is a global instance of that:

SPI SPI_Master(P0_9, P0_8, P0_7);

void funcA(int b){
}

so there is a way in a function to do something similar:

void SPIException(){
   delete SPI_Master;
   SPI_Master = new SPI (P0_9, P0_8, P0_7);
}

So to force constructor to be recalled?

and a little clarification:

SPI SPI_Master(P0_9, P0_8, P0_7);

is perfect identical to:

SPI SPI_Master = new SPI(P0_9, P0_8, P0_7);

?

Mat
  • 188,820
  • 38
  • 367
  • 383
holyhope
  • 23
  • 5
  • The destructor is almost a normal function that you can explicitly call. In fact, if you use placement new it's *required* that you call the destructor explicitly (and placement new could be a hint on how to "recreate" the objects). – Some programmer dude Aug 02 '19 at 08:32
  • 1
    Other ways to possible solve this, is to have a "clean" or "deinit" type of function, called by the destructor and that you also could call explicitly. Or possibly not use global variables and let the objects be destructed the natural way when they go out of scope or when their life-time ends, or as part of an exception stack unwind. – Some programmer dude Aug 02 '19 at 08:33
  • 1
    Use a placement constructor. https://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new – Johannes Aug 02 '19 at 08:33
  • a couple of options come to my mind: 1) instead of using a global object, can you use a global unique pointer? such that the underlying SPI can be reset easily 2) explicitly invoke destructor and use placement new to construct SPI in the same memory address – SPD Aug 02 '19 at 09:17

4 Answers4

1

A destructor is just a function with a special name. Call it like instance.~T().

void destroy()
{
  SPI_Master.~SPI():
}

You can forcibly construct an object at a given location through placement new.

void reinit()
{
  new(&SPI_Master) SPI(/*arguments go here*/);
}
Tanveer Badar
  • 4,541
  • 2
  • 27
  • 31
0

To elaborate the hint of SPD:

2) explicitly invoke destructor and use placement new to construct SPI in the same memory address

I made a small sample:

#include <iostream>

struct Global {
  int a1, a2;
  Global(int a1, int a2): a1(a1), a2(a2)
  {
    std::cout << "Global::Global(): a1: " << a1 << " a2: " << a2 << '\n';
  }
  ~Global()
  {
    std::cout << "Global::~Global()\n";
  }
  Global(const Global&) = delete;
  Global& operator=(const Global&) = delete;
};

std::ostream& operator<<(std::ostream &out, const Global &global)
{
  return out
    << "&global: " << &global << '\n'
    << "global: global.a1: " << global.a1 << " global.a2: " << global.a2 << '\n';
}

Global global(123, 456);

int main()
{
  std::cout << "Initially: " << global;
  global.a1 = 321; global.a2 = 654;
  std::cout << "Changed: " << global;
  global.~Global();
  new(&global) Global(123, 456);
  std::cout << "Re-Inited: " << global;
  std::cout << "Exiting...\n";
}

Output:

Global::Global(): a1: 123 a2: 456
Initially: &global: 0x6013d8
global: global.a1: 123 global.a2: 456
Changed: &global: 0x6013d8
global: global.a1: 321 global.a2: 654
Global::~Global()
Global::Global(): a1: 123 a2: 456
Re-Inited: &global: 0x6013d8
global: global.a1: 123 global.a2: 456
Exiting...
Global::~Global()

Live Demo on coliru

Please, note:

A global instance has its limitations but it might be there and this may not be changed for any reason. (For singletons, the Singleton Pattern should be mentioned which additionally helps to solve the static initialization order ‘fiasco’ (problem)?.)

In general, something created by new is constructed into memory allocated on heap usually (i.e. if no custom new is used) which is in opposition to a static instance which is created somewhere else.

Placement new can do the construction without allocation i.e. into the storage provided by caller, i.e. independently of how storage was allocated.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
0

SPI SPI_Master(P0_9, P0_8, P0_7); is not identical to SPI SPI_Master = new SPI(P0_9, P0_8, P0_7);

last line will not be compiled so far as new returns pointer to the located in heap object, but not an object. Meanwhile SPI SPI_Master(P0_9, P0_8, P0_7); are located in stack. You could read additionaly about memory types. Correct form will be: SPI * SPI_Master = new SPI(P0_9, P0_8, P0_7); Yes it's one of the solution you could use.

and somewhere after function you mentioned will works as expected:

void SPIException(){
   delete SPI_Master;
   SPI_Master = new SPI (P0_9, P0_8, P0_7);
}

But it will call a changes in all code where SPI_Master is used: from SPI_Master.anyCall() to SPI_Master->anyCall().

In case you cannot change line: SPI SPI_Master(P0_9, P0_8, P0_7);

You could try to explicitly call destructor and than overwrite the same variable:

SPI_Master.~SPI(); 

SPI_Master = SPI(P0_9, P0_8, P0_7);

But be carefull: 1) It's depends on what really do constructor and destructor body. 2) Order of destruction and construction during assigment which will generate your compiller: - create new, remove old and assign new - remove old, create new, assign new

So this solution could be verry dangerous.

  • Your last bits are incorrect. You need to explicitly construct another object at the same location as `SPI_Master` using placement new. `operator =()` may or may not work correctly since the lhs is an object which was destructed. – Tanveer Badar Aug 02 '19 at 12:26
0

Use a global pointer to SPI_Master. Like this:

SPI* SPI_Master;

// in some init code:
SPI_Master = new SPI .....

Then your SPIException can be used as written .

Alex E
  • 73
  • 4