Go Down

Topic: Using a class instance as a class global. How? (Read 986 times) previous topic - next topic

bildr

Im writing a library, but I hit a brick wall right at the beginning.

I need to include an instance of the NewSoftSerial in the class, and I need that instance to be a class global.

In other languages you can define a variable as a class type, then later call the constructor. But in C++ it looks like both happen at the same time.

Under private globals I defined:
NewSoftSerial _printer;

Then in the constructor, I have
_printer(_RX_Pin, _TX_Pin);

But this dosnt seem to work one bit. I get an error of "error: no matching function for call to 'NewSoftSerial::NewSoftSerial()'"

I completely understand why because NewSoftSerial has no function called NewSoftSerial with no parameters.

But how does one get around this?


The library code is below.

Code: [Select]

#ifndef Thermal_h
#define Thermal_h

#include <NewSoftSerial.h>
#include <WProgram.h>
#include <WConstants.h>

class Thermal{
  public:

    Thermal(int RX_Pin, int TX_Pin);

  private:
int _RX_Pin;
  int _TX_Pin;
NewSoftSerial _printer;
};

#endif






Code: [Select]


#include <NewSoftSerial.h>

#include <WProgram.h>
#include <WConstants.h>
#include "Thermal.h"


Thermal::Thermal(int RX_Pin, int TX_Pin){

_RX_Pin = RX_Pin;
_TX_Pin = TX_Pin;

_printer(_RX_Pin, _TX_Pin);
}


Nick Gammon

One approach is to make it a pointer, then call the constructor at runtime. For example:

Code: [Select]
#include <NewSoftSerial.h>

#include <WProgram.h>
#include <WConstants.h>
#include "Thermal.h"

Thermal::Thermal(int RX_Pin, int TX_Pin){

_RX_Pin = RX_Pin;
_TX_Pin = TX_Pin;

_printer = new NewSoftSerial (_RX_Pin, _TX_Pin);
        _printer->begin (9600);
}

Thermal::~Thermal(){
delete _printer;
}


// testing

// new and delete operators
void *operator new(size_t size_) { return malloc(size_); }
void* operator new(size_t size_,void *ptr_) { return ptr_; }
void operator delete(void *ptr_) { free(ptr_); }

Thermal * foo;

void setup ()
{
  foo = new Thermal (2, 3);
}

void loop () {
  foo->_printer->println ("hello, world");
}


and:

Code: [Select]
#ifndef Thermal_h
#define Thermal_h

#include <NewSoftSerial.h>
#include <WProgram.h>
#include <WConstants.h>

class Thermal{
 public:

   Thermal(int RX_Pin, int TX_Pin);  // constructor
   ~Thermal();  // destructor

   NewSoftSerial * _printer;

 private:
int _RX_Pin;
  int _TX_Pin;
};

#endif


I tested this and it did output data from pin 3.
http://www.gammon.com.au/electronics

bildr

Perfect!
Thank you!

Do I need to do anything to mark this as solved?

crimony

#3
Aug 11, 2011, 01:51 am Last Edit: Aug 11, 2011, 01:56 am by crimony Reason: 1
Or use a reference member and an initialization list:

Code: [Select]
#ifndef Thermal_h
#define Thermal_h

#include <NewSoftSerial.h>
#include <WProgram.h>
#include <WConstants.h>

class Thermal{
  public:

    Thermal(int RX_Pin, int TX_Pin);

  private:
int _RX_Pin;
  int _TX_Pin;
NewSoftSerial& _printer;
};

#endif


Code: [Select]
#include <NewSoftSerial.h>

#include <WProgram.h>
#include <WConstants.h>
#include "Thermal.h"


Thermal::Thermal(int RX_Pin, int TX_Pin):
_printer(RX_Pin, TX_Pin), _RX_Pin(RX_Pin), _TX_Pin(TX_Pin) {}


Nick Gammon


Or use a reference member and an initialization list: ...


Yes, that gets rid of pointers, but I can't get it to compile:

Code: [Select]
Thermal.cpp: In constructor 'Thermal::Thermal(int, int)':
Thermal:8: error: member initializer expression list treated as compound expression
Thermal:8: error: invalid initialization of reference of type 'NewSoftSerial&' from expression of type 'int'


A bit of fiddling around to remove the error gives this:

Code: [Select]
#ifndef Thermal_h
#define Thermal_h

#include <NewSoftSerial.h>
#include <WProgram.h>
#include <WConstants.h>

class Thermal{
  public:

    Thermal(int RX_Pin, int TX_Pin);
NewSoftSerial _printer;

  private:
int _RX_Pin;
  int _TX_Pin;
};
#endif


Code: [Select]
#include <NewSoftSerial.h>

#include <WProgram.h>
#include <WConstants.h>
#include "Thermal.h"

Thermal::Thermal(int RX_Pin, int TX_Pin):
_printer(RX_Pin, TX_Pin), _RX_Pin(RX_Pin), _TX_Pin(TX_Pin) {
       _printer.begin (9600);
}

// testing

Thermal foo (2, 3);

void setup ()
{
}

void loop () {
  foo._printer.println ("hello, world");
}


That works, although I caution you that this is using a static constructor. Although it worked this time, static constructors can sometimes give weird results because of the order the constructors are called during program initialization.
http://www.gammon.com.au/electronics

crimony



Yes, that gets rid of pointers, but I can't get it to compile:


My bad, I didn't test at all, and my C++ is very rusty.

The point was supposed to be that you need to use an initialization list if there is no default constructor for a member variable (in this case class NewSoftSerial).

Quote

That works, although I caution you that this is using a static constructor. Although it worked this time, static constructors can sometimes give weird results because of the order the constructors are called during program initialization.


I expect undefined behaviour if the constructor for NewSoftSerial is called in the global variable initialization stage.

Nick Gammon


I expect undefined behaviour if the constructor for NewSoftSerial is called in the global variable initialization stage.


Where were you planning to put the declaration for Thermal then? In my case:

Code: [Select]
Thermal foo (2, 3);

I had it at global scope, so it would be constructed at global initialization time, and thus the constructor for NewSoftSerial likewise.

If you put it into setup, it goes out of scope once setup ends.

If you put it into loop, it is constructed every time through the loop.

If you put it into loop, declared static, like this:

Code: [Select]
void loop () {
  static Thermal foo (2, 3);

  foo._printer.println ("hello, world");
}


Then you get the error:

Code: [Select]
Thermal.cpp.o: In function `loop':
Thermal.cpp:23: undefined reference to `__cxa_guard_acquire'
Thermal.cpp:23: undefined reference to `__cxa_guard_release'
Thermal.cpp:23: undefined reference to `atexit'
http://www.gammon.com.au/electronics

crimony



I expect undefined behaviour if the constructor for NewSoftSerial is called in the global variable initialization stage.


Where were you planning to put the declaration for Thermal then?


My expectations are unrealistic, as it turns out. Global scope is the right thing to do.

On closer inspection of the NewSoftSerial Library, it appears they must have ensured that they are immunized from the kind of initialisation issues covered here.


Go Up