it's probably a better idea to let the user of your class configure the stream and give you a reference to that stream for your class
that would let the user of your class use standard and known process for defining the pins, the baud rate, possibly use something else than 8N1 etc and will be more portable likely
what is a TC_SWI ?
does it make sense for the TC_SWI to own the Serial interface ?
Serial exists outside your class whereas the SoftwareSerial instance can only exist (if you want to pass it as a pointer to the constructor) if you create it outside your class.
you could have two constructors, one for HardwareSerial and one for SoftwareSerial and the class' instance variable to store that would be a Stream pointer
It is a our class for controlling TMC stepper drivers so the interface is only used from within the class and should never be used from outside.
We have everything in the class working for HW serial and I am trying to implement the SW serial component because one of our historical boards does not have a spare serial port available
I know there is already a commonly used library TMC Lib but it was deemed not the right choice prior to me inheriting the project.
Still not a compelling or cogent reason to attempt initializing hardware and software Serial inside the class. It's much cleaner to let the user code choose the interface and initialize it. Then pass a Stream reference into the class keeping it generic and agnostic to the physical interface.
ABSTRACTION is a key characteristic of Object Oriented Programming.
Points taken about it possibly not being the best way.
If I was wanting to understand how I would implement the overloaded function from within the class, is anyone able to help me understand what I am doing wrong and why the SW serial version does not work?
it's not an overload if you use a Stream pointer to reference the port
you would do something like this, when creating the Serial handle outside the class. (remember a Stream does not have a begin function for example and you don't want to call begin in the constructor)
#include <SoftwareSerial.h>
class SerialHandler : public Print {
public:
SerialHandler(SoftwareSerial &ss) : serialInstance(&ss) {}
SerialHandler(HardwareSerial &hs) : serialInstance(&hs) {}
virtual size_t write(uint8_t data) {return serialInstance->write(data);}
using Print::write; // Use other write functions from Print class
int available() {return serialInstance->available();}
int read() {return serialInstance->read();}
int peek() {return serialInstance->peek();}
void flush() {serialInstance->flush();}
private:
Stream *serialInstance; // Pointer to a Stream object (either SoftwareSerial or HardwareSerial)
};
SoftwareSerial softSerial(10, 11);
SerialHandler softHandler(softSerial);
SerialHandler hardHandler(Serial);
void setup() {
softSerial.begin(9600);
Serial.begin(115200);
}
void loop() {}
This can't work because SWSerial is a local variable that goes out of scope at the end of the begin function. (Assuming you meant m_serialPort = &SWSerial.) You'll just end up with a dangling pointer, and your code will probably crash horribly.
While I do agree that it's best to have the user provide the serial port object, there are ways to still be able to call begin on it, even though that function is not part of the Stream interface.
For example, if you have a reasonably recent standard library:
class MyWrapper {
public:
MyWrapper(HardwareSerial &s)
: stream(&s), begin_func(make_begin_func(&s)) {}
MyWrapper(SoftwareSerial &s)
: stream(&s), begin_func(make_begin_func(&s)) {}
void write() { stream->write(); }
void begin(unsigned long long baud) { begin_func(stream, baud); }
private:
Stream *stream;
using begin_func_t = void(Stream *, unsigned long long baud);
begin_func_t *begin_func;
// This function returns a pointer to a function that takes a
// pointer to base (Stream) and performs an unchecked downcast
// to the given type S. It then invokes S's begin method with
// the given baud rate.
// Only do this kind of unchecked casting if well encapsulated,
// so no mismatch between the stream pointer and the begin_func
// function can arise.
template <class S>
static begin_func_t *make_begin_func(S *) {
return +[](Stream *s, unsigned long long baud) {
return static_cast<S *>(s)->begin(baud); // Note: unsafe downcast
};
}
};
The first one requires C++17 (for std::variant), but the second one works with C++11 as well (and can even be made to work in C++98 with some minor tweaks: GCC 4.6.4 - https://godbolt.org/z/KhWf9sz7G).