Die erste Zeile ist der virtuell definierte Dekonstruktor.
Die zweite Zeile ist scheinbar eine andere Schreibweise für den Defaultkonstruktor.
Nur was bedeuten die letzten beiden Zeilen?
Was machen diese genau? Wofür verwendet man sowas?
default bedeutet dass man explizit die vom Compiler generierte Version verwenden will. Es gibt Situationen in denen die nicht generiert werden, aber man es trotzdem gerne möchte:
Das vorletzte ist der Copy Konstruktor und das letzte der Copy Assignment Operator:
das sollte noch zum Thema Konstruktor passen. Hintergrund, ich bin immer noch dran am bauen meiner SPI Lib. Ich versuche die CombiePin Lib zum schalten des SlaveSelect Pins mittels Zeigerzugriff zu verwenden. Soll unabhängig von digitalWrite() sein. Dazu benötige ich den Datentyp “OutputPin” für den Zeiger aber ohne neues Objekt. Das habe ich nun realisiert indem ich eine Basisklasse in der CombiePin Lib “vorschalte” und die benötigten Methoden virtuell deklariere. Jetzt kann ich mittels Zeiger schalten und walten.
@ Serenifly: es war vorher protected und ich musste es public machen. Funktioniert zwar mit public, ist aber bestimmt nicht Sinn der Sache wegen meiner "Quernutzung" den gesamten Schutz aufzugeben. Deswegen meine Unsicherheit.
@ combie: ich werde die Idee testen
@ all:
danach werde ich mal alles bis jetzt von mir neu funktionierende, aber "nur" in Einzelteilen getestete zusammensetzen damit man einen Gesamtüberblick bekommt. Das kann etwas dauern, falls neue Stolpersteine lauern. Dann erkläre ich auch das Endziel warum ich den Aufwand mit dem Zeiger mache.
über Irrwege wurde wieder alles anders und damit einfacher. Die CombiePin.h kann unverändert bleiben. Die Basisklasse wird an die eigentliche SPI Klasse vererbt und diese nutzt Methoden der CombiePin Klasse. Die SPI ISR steht für jeden sichbar im Hauptsketch. Damit entfällt das Gewürge mit den Interrupt Optionangaben und damit ist man frei von Compilerabhängigkeiten. Und das ermöglicht eben überhaupt erst die Nutzung von template Klassen.
Der aktuelle Stand zeigt den neuen Grundstock.
Schaut euch das einmal bitte an.
Ist das als Basis zum Ausbau weiterer Funktionalitäten brauchbar?
Was würdet ihr vielleicht ändern?
In der Funktion 'transferGoOut()' habe ich bewusst keine Atomic Blocks drin, weil die Funktion laut meiner Einschätzung nur aufgerufen werden kann, wenn der Bus nicht benutzt wird und damit die ISR inaktiv ist. Die ISR wird ja erst durch diese Funktion immer wieder erneut in Gang gesetzt.
Die 'messPin's im Sketch und Lib hab ich für mich dringelassen. Die dienen mir am Oszi bzw. Datalogger zum prüfen ob der Ablauf stimmt bzw. ob es generell funktioniert. Oder für euch zum nachvollziehen des Ablaufes.
Desweiteren bin ich dabei als 3. neuen Objektparameter die Buffergröße anzugeben. Vorbereitungen dazu stecken im Code. Klappt aber noch nicht. virtual kann man nur Funktionen deklarieren, leider keine Member. Soll aber irgendwie mit virtueller return Funktion möglich sein. Hier hänge ich gerade fest. Gleichzeitig virtual deklarieren und aufrufen in der Basisklasse geht nicht. Der Objektparameter soll die FiFoBuffergröße des Array angeben. Das struct muss in der Basisklasse sein, weil ich damit im Sketch mittels Zeiger über alle Objekte hinweg damit zugreifen kann. Ansonsten müßte ich das 'struct Buffer' aufgeben und alle struct Member einzeln schreiben. Das wäre meine letzte Option die ich derzeit hätte.
Wo ich mir nicht sicher bin ist ob man den Dekonstruktor wirklich benötigt. Wir zerstören doch im Allgemeinen keine einmal instanzierten Objekte auf unseren µC.
Desweiteren müßte doch der Zeiger 'BasisDevice *ptrDevice' volatile sein, oder? Wird im Hauptprogramm und in der ISR ständig geändert. Nur wenn ich den volatile mache meckert der Compiler.
Du kannst in der Basisklasse eine Konstruktor definieren der die Größe setzt und diesen Konstruktor per constructor delegation in der Kindklasse aufrufen
Dekonstruktor braucht man nur wenn man in der Klasse was mit dynamischen Speicher macht. Dann muss man das da aufräumen
ich habe noch nicht verstanden wie ich in der abgeleiteten Klasse den Basis Konstruktor aufrufen muss. Alle Bsp. arbeiten immer nur mit ein und der selben Klasse und dann “intern” mit mehreren Konstruktoren.
Meine Logik scheitert das auf 2 Klassen anzuwenden. Es kompiliert bei mir wenn ich den Member ‘bufferSize’ im Array ‘Buffer’ nicht verwende. Ersetze ich BUFFERSIZE durch bufferSize erhalte ich wie bei allen anderen Versuchen immer “error: invalid use of non-static data member ‘BasisDevice::bufferSize’”
// Size of the circular buffer, must be power of 2.
constexpr uint8_t BUFFERSIZE = 16;
// Size of buffer
constexpr uint8_t BUFFER_MASK = BUFFERSIZE - 1;
// stellt Datentyp für Device-Zeiger zur Verfügung
class BasisDevice
{
protected:
uint8_t bufferSize;
public:
BasisDevice(uint8_t bs) : bufferSize{bs} {}
BasisDevice(const BasisDevice&) = default;
BasisDevice& operator = (const BasisDevice&) = default;
virtual void setHigh(); // derzeit wird nur diese Methode mittels Zeiger im Hauptsketch benötigt
struct Buffer
{
volatile uint8_t fifo[bufferSize]; // [BUFFERSIZE]
volatile uint8_t head;
volatile uint8_t tail;
volatile uint8_t lastError;
volatile bool startNewTransfer;
} buffer; // create a FiFo-Buffer
};
// ------ here begins the template class -----------------------------------
template<uint8_t const csPin, uint8_t const divider, uint8_t fifoSize>
class SpiSlave : public BasisDevice
{
private:
OutputPin<csPin> slaveSelectPin;
public:
// ****** Konstruktor ******
SpiSlave () :
BasisDevice{fifoSize}
{ }
template<uint8_t const csPin, uint8_t const divider, uint8_t fifoSize>
class SpiSlave : public BasisDevice
{
private:
OutputPin<csPin> slaveSelectPin;
public:
// ****** Konstruktor ******
SpiSlave ()
{ }
using BasisDevice::BasisDevice;
Das hier erzeugt gleiche Fehlermeldung, immer sobald ich BUFFERSIZE mit bufferSize im struct ersetze.
Vom Gefühl her wird zu bufferSize zu spät initialisiert.
Wobei das fifoSize aus der Template-Deklaration kommt
Normal geht das. Das gibt es seit C++11. Vorher musste man da mit Hilfsmethoden arbeiten
using arbeitet so ähnlich. Auch da muss der Konstruktor in der Basisklasse existieren und durch using sagst du dass er von der Kindklasse geerbt wird. Dadurch kann man es sich u.U. sparen einen weiteren Konstruktor explizit hinzuschreiben
Ich vermute, ihm möchte fifoSize als Array Größe verwenden.
Aber nicht das Array dynamisch allozieren.
Vermeiden möchte er, vermutlich, die fifoSize als Template Parameter an die Basisklasse durchzureichen, da ihm dann das Template(Typisierung) an anderer Stelle ein Beinchen stellt.
richtig erkannt. Der template Parameter ‘fifoSize’ soll in der BasisDevice die Arraygröße im struct festlegen. Dort soll er seine endgültige alleinige Verwendung finden.
Das funktioniert leider nicht. Hatte ich schon in folgender Form probiert.
Jetzt habe ich deins verwendet, was im Grunde 1:1 zu meinem ist, kompiliert immer noch nicht.
error: invalid use of non-static data member ‘BasisDevice::fifoSize’
volatile uint8_t fifo[fifoSize]; // [BUFFERSIZE]
Ja, das Problem mit dem Arrays ist mir auch gerade aufgefallen. Ich hatte das etwas zu allgemein betrachtet. Deshalb geht es mit einer Konstante, aber sobald Variablen im Spiel sind passt es nicht. Das geht wiederum mit Templates, wobei dann halt jede Instanz mit einer anderen Größe eigentlich eine andere Klasse ist
template <size_t N>
class Base
{
public:
size_t getSize()
{
return sizeof(data) / sizeof(data[0]);
}
protected:
Base() {}
unsigned int data[N];
};
template <size_t N>
class Derived : public Base<N>
{
public:
private:
};
das funktioniert alles für sich, jedoch kann ich dann von Base keinen Zeiger erstellen bzw. nicht allgemein verwenden. Den Zeiger benötige ich um darüber vom aktuell zugewissen SPI Slave die Member vom struct benutzen zu können. Base (BasisDevice) habe ich erstellt um den passenden Datentyp für den Zeiger zu erhalten.
Ist das Problem lösbar?
Oder gibt es eine andere Idee statt über Zeiger auf die struct Member vom aktuellen SPI Slave zugreifen zu können. Wird in der ISR benötigt um diese allgemein zu halten.
template <uint8_t N>
class Base
{
public:
uint8_t getSize()
{
return sizeof(buffer.data) / sizeof(buffer.data[0]);
}
Base() {}
struct Buffer
{
unsigned int data[N];
} buffer;
};
template <uint8_t N>
class Derived : public Base<N>
{
public:
private:
};
Base <0> *ptrBase = nullptr;
void setup()
{
Serial.begin(9600);
Derived<10> obj1;
Derived<5> obj2;
Serial.println(obj1.getSize());
Serial.println(obj2.getSize());
ptrBase = &obj1; // error: cannot convert 'Derived<10>*' to 'Base<0>*' in assignment
}
void loop (void)
{
}