class public type declaration

Moin,

ich möchte einen Sketch in eine Class umschreiben, damit funktionen aus dem Sketch verschwinden.

Folgende Declaration habe ich im allemeinen Teil des Sketches (oberhalb von Setup):

//ESP8266
  WiFiClient espClient;
//MQTT
  PubSubClient mqtt_client(espClient);

Wo muss ich diese Declaration in der Class platzieren?

Im public der Class:

class ESP8266_Basic{

public:
//main-------------------------------------------------------------------------
  ESP8266_Basic();
  

//Objects----------------------------------------------------------------------


//variables--------------------------------------------------------------------

//ESP8266
  WiFiClient espClient;
//MQTT
  PubSubClient mqtt_client(espClient);

private:

/  

};

Bekomme ich folgenden Fehler :

error: 'espClient' is not a type

Im Contructor führ das auch zu nix:

ESP8266_Basic::ESP8266_Basic(){	
//ESP8266
  WiFiClient espClient;
//MQTT
  PubSubClient mqtt_client(espClient);

}

Da fehlem mir ect noch C++ Grundlagen.

Ich komme aus der Pascal und Basic Ecke.....mit C++ habe ich erst vor 2 Monaten wegen der Arduino-Umgebung begonnen.
Habt bitte Verständnis für einen Newbie..... :roll_eyes:

Schau dir Post #6 in dem anderen Thread an!

Da habe ich sowas gemacht. Im Header wird eine Variable erstellt:

private:
  WiFiManager wifiManager;

Dann muss man das Objekt im Konstruktor (aber nicht im Körper selbst!!) instantiieren:

ESP8266() : wifiManager()    //Konstruktor
{
}

das : wifiManager() ruft dann den Konstruktor der anderen Klasse auf. Da kann man dann auch Variablen durchreichen die man dem ersten Konstruktor übergibt. Weitere Konstruktor-Aufrufe werden durch Kommas getrennt

Der Grund weshalb das nicht im Körper selbst geht ist folgender. Alle Variablen darin haben nur lokalen Scope. Wenn du also darin ein Objekt erstellst ist das temporär im Konstruktor und hört danach auf zu existieren.

Wo liegt jetzt genau der Körper...... :o

ESP_Basic.h

class ESP8266_Basic{

public:
  ESP8266_Basic();

private:
  WiFiClient wifiClient;
  PubSubClient mqtt_client(wifiClient);

};

ESP_Basic.cpp

ESP8266_Basic::ESP8266_Basic(){	

}


};

Wo müssen die Objekte jetz Instanziert werden?
Im Konstuktor der Grundklasse oder muss je Objekt eine eigene Class aufgemacht werden?

private:
  WiFiClient wifiClient;
  PubSubClient mqtt_client(wifiClient);

Falsch!

So:

private:
  WiFiClient wifiClient;
  PubSubClient mqtt_client;

Hier die Variable deklarieren. Nichts erstellen oder aufrufen mit ().

Der Konstruktor Körper ist wie bei einer normalen Funktion auch alles innerhalb der geschweiften Klammern. Ab dem Zeitpunkt existiert auch ein Objekt. Davor noch nicht

Hier mal allgemein in Visual C++ und ohne Header. Was dich interessiert ist in der Klasse C!

class A
{
public:
  A()
  {
    cout << "A ctor" << endl;
  }
};

class B
{
public:
  B(A& a) //Konstruktor von B mit Referenz auf A als Parameter
  {
    cout << "B ctor" << endl;
  }
};

class C
{
public:
 C() : a(), b(a) //Konstruktor von C. Erstellt erst a, und dann b (wobei a übergeben wird)
  {
    cout << "C ctor" << endl;
  }
private:
  A a; //Objekte hier nur deklarieren
  B b;
};

int _tmain(int argc, _TCHAR* argv[])
{ 
  C c;

  getchar();
}

Die Ausgabe ist wie folgt:

A ctor
B ctor
C ctor

Wobei C als erstes erstellt wird. Aber der Körper des Konstruktors wird erst am Ende ausgeführt.

Danke, das du nicht aufgibst!

Zu meinem Verständnis:
Class a und b sind meine beiden "externen" Klassen.
Class c ist meine Klasse.

  #include <WiFiManager.h>
  #include <PubSubClient.h>

class ESP8266_Basic{

public:
  ESP8266_Basic() : wifi_client(), mqtt_client(wifi_client){
  } 

private:
  WiFiClient wifi_client;
  PubSubClient mqtt_client;

};

liefert mir folgenden Fehler:

C:\Users\Admin\Documents\Arduino\libraries\ESP8266_Basic\ESP8266_Basic.cpp:3:1: error: redefinition of 'ESP8266_Basic::ESP8266_Basic()'

ESP8266_Basic::ESP8266_Basic(){

^

In file included from C:\Users\Admin\Documents\Arduino\libraries\ESP8266_Basic\ESP8266_Basic.cpp:1:0:

C:\Users\Admin\Documents\Arduino\libraries\ESP8266_Basic/ESP8266_Basic.h:33:3: error: 'ESP8266_Basic::ESP8266_Basic()' previously defined here

ESP8266_Basic() : wifi_client(), mqtt_client(wifi_client){

^

exit status 1
Fehler beim Kompilieren.

Da hab ich immer noch Verständnislücken, sorry.....

Class a und b sind meine beiden "externen" Klassen.
Class c ist meine Klasse.

Korrekt. Wobei A, B und C Klassen sind. a, b und c sind Objekte dieser Klassen.

Du musst das bei dir noch richtig auf Header und .cpp verteilen. Möglich dass das nicht passt. Das ist eine etwas nervige Angelegenheit in C++

Das Beispiel oben damit und für den Arduino:

Der Einfachheit halber ein Header für alles (normal hat man für jede Klasse einen extra Header!), hier mal "Test.h" genannt:

#ifndef TEST_H      //Include Guard um Mehrfach-Inkludierungen zu verhindern
#define TEST_H

class A
{
public:
  A();
};

class B
{
public:
  B(A& a);
};

class C
{
public:
  C();    //hier keine weiteren Angaben! Der Rest ist in der .cpp Datei
  
private:
  A a;
  B b;
};

#endif

Dann die Implementierung in "Test.cpp":

#include "Test.h"
#include "Arduino.h"   //nur für Serial.println()!

A::A()
{
  Serial.println("A ctor");
}

B::B(A& a)
{
  Serial.println("B ctor");
}

C::C() : a(), b(a)
{
  Serial.println("C ctor");
}

Dann der eigentliche Sketch:

#include "Test.h"

void setup()
{
  Serial.begin(9600);

  C c;
}

void loop()
{
}

Wenn es für die OK ist, würde ich gerne bei den Bezeichnungenfür "meine" Librarys bleiben,
ich baue da sonst nur Übersetzungsfehler ein..... :roll_eyes:

Die Header

  #include <WiFiManager.h>
  #include <PubSubClient.h>

sind ja fertig und sollten nicht angepasst werden müssen. Das Object "WiFiManager" ist ja in "PubSubClient" bereits bekannt.

Meine Klasse

ESP8266_Basic.h

  #include <WiFiManager.h>
  #include <PubSubClient.h>


class ESP8266_Basic{

public:
  ESP8266_Basic();
  void start_mqtt();

private:
  WiFiClient wifi_client;
  PubSubClient mqtt_client;

};

ESP8266_Basic.cpp

#include <ESP8266_Basic.h>


WiFiClient::WiFiClient(){
}
PubSubClient::PubSubClient(WiFiClient& wifi_client){
}


ESP8266_Basic::ESP8266_Basic() : wifi_client(), mqtt_client(wifi_client){	
}


void ESP8266_Basic::start_mqtt(){

  mqtt_client.setServer("192.168,1,203", 1883);

}

Und noch der Sketch

#include <ESP8266_Basic.h>

ESP8266_Basic espClient;

void setup() {
  Serial.begin(115200); 

  espClient.start_wifi();
}

void loop() {

}

Hier bekomme ich jetzt reichlich Fehler....

C:\Users\Admin\Documents\Arduino\libraries\ESP8266_Basic\ESP8266_Basic.cpp:7:1: error: prototype for 'PubSubClient::PubSubClient(WiFiClient&)' does not match any in class 'PubSubClient'

PubSubClient::PubSubClient(WiFiClient& wifi_client){

^

In file included from C:\Users\Admin\Documents\Arduino\libraries\ESP8266_Basic/ESP8266_Basic.h:12:0,

from C:\Users\Admin\Documents\Arduino\libraries\ESP8266_Basic\ESP8266_Basic.cpp:1:

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:83:7: error: candidates are: PubSubClient::PubSubClient(PubSubClient&&)

class PubSubClient {

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:83:7: error: PubSubClient::PubSubClient(const PubSubClient&)

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:116:4: error: PubSubClient::PubSubClient(const char*, uint16_t, std::function<void(char*, unsigned char*, unsigned int)>, Client&, Stream&)

PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:115:4: error: PubSubClient::PubSubClient(const char*, uint16_t, std::function<void(char*, unsigned char*, unsigned int)>, Client&)

PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:114:4: error: PubSubClient::PubSubClient(const char*, uint16_t, Client&, Stream&)

PubSubClient(const char*, uint16_t, Client& client, Stream&);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:113:4: error: PubSubClient::PubSubClient(const char*, uint16_t, Client&)

PubSubClient(const char*, uint16_t, Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:112:4: error: PubSubClient::PubSubClient(uint8_t*, uint16_t, std::function<void(char*, unsigned char*, unsigned int)>, Client&, Stream&)

PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:111:4: error: PubSubClient::PubSubClient(uint8_t*, uint16_t, std::function<void(char*, unsigned char*, unsigned int)>, Client&)

PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:110:4: error: PubSubClient::PubSubClient(uint8_t*, uint16_t, Client&, Stream&)

PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:109:4: error: PubSubClient::PubSubClient(uint8_t*, uint16_t, Client&)

PubSubClient(uint8_t *, uint16_t, Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:108:4: error: PubSubClient::PubSubClient(IPAddress, uint16_t, std::function<void(char*, unsigned char*, unsigned int)>, Client&, Stream&)

PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:107:4: error: PubSubClient::PubSubClient(IPAddress, uint16_t, std::function<void(char*, unsigned char*, unsigned int)>, Client&)

PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:106:4: error: PubSubClient::PubSubClient(IPAddress, uint16_t, Client&, Stream&)

PubSubClient(IPAddress, uint16_t, Client& client, Stream&);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:105:4: error: PubSubClient::PubSubClient(IPAddress, uint16_t, Client&)

PubSubClient(IPAddress, uint16_t, Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:104:4: error: PubSubClient::PubSubClient(Client&)

PubSubClient(Client& client);

^

C:\Users\Admin\Documents\Arduino\libraries\PubSubClient\src/PubSubClient.h:103:4: error: PubSubClient::PubSubClient()

PubSubClient();

^

exit status 1
Fehler beim Kompilieren.

Was soll denn das sein?

WiFiClient::WiFiClient()
{
}

PubSubClient::PubSubClient(WiFiClient& wifi_client)
{
}

Ich dachte du hattest verstanden dass diese zwei Klassen und damit die Konstruktoren bei dir schon wo anders vorhanden sind

Ich habe A und B nur als Test per Hand geschrieben, damit ich in C was zum Erstellen habe

Wenn es für die OK ist, würde ich gerne bei den Bezeichnungenfür "meine" Librarys bleiben

Natürlich. So ist es auch gedacht. Mir ging es darum dass ich dir mit dem Beispiel Code das Prinzip aufzeige. Wenn du das auf deine Klassen mit deinen Namen umsetzen kannst, dann hast du verstanden was du machen musst. Ansonsten schreibe ich dir den korrekten Code hin, du kopierst ihn und beim nächsten mal geht es wieder nicht.

Ich dachte du hattest verstanden dass diese zwei Klassen und damit die Konstruktoren bei dir schon wo anders vorhanden sind

Oh man..... hast natürlich recht.
Ich habe mich zu sehr auf das Umsetzen von a,b,c fixiert.....

Letztendlich:
Kaum macht man es richtig, funktioniert es auch!

Ich kann jetzt in der function "void ESP8266_Basic::start_mqtt(){" auf das Objekt "mqtt_client." zugreifen.

Ich mache dann mal bis zum nächsten Problem weiter. Ich hoffe, ich kann dich weiter "belatschern"..... :slight_smile:

Ich wollte eigentlich "nur mal eben" meine gesammelten Demo-Sketche in einer eigenen Library verhaften.
Mir war mal wieder nicht klar, dass das ohne tiefere Ahnung nicht mal eben so ist.

Danke dir nochmal.

Natürlich. So ist es auch gedacht. Mir ging es darum dass ich dir mit dem Beispiel Code das Prinzip aufzeige. Wenn du das auf deine Klassen mit deinen Namen umsetzen kannst, dann hast du verstanden was du machen musst. Ansonsten schreibe ich dir den korrekten Code hin, du kopierst ihn und beim nächsten mal geht es wieder nicht.

Das ist natürlich das Ziel!

Vielleicht fasse ich es nochmal zusammen, bitte korrigiere die Begriffe wenn sie Müll sind:

In der Deklaration des Headers werden nur die Variablen deklariert:

private:
  WiFiClient wifi_client;
  PubSubClient mqtt_client;

Das Object wifi_client wird als Instanz der Class WiFiClient declariert (also nur bekannt gemacht und angemeldet)

Mein Object wifi_client vom Typ WiFiClient , ist genauso zu sehen wie "int i" ,

Die neuen Objekte müssen jetzt aber noch im Konstuktor instantiert werden.
Das geschiet im Konstuktor meiner Class:

ESP8266_Basic::ESP8266_Basic() : wifi_client(), mqtt_client(wifi_client){	
}

Die Doppelpunkte habe ich noch nicht ganz verstanden, Prinzip ist aber wohl:
"::" bei der eigenen Instanz, hinter einem weiteren Doppelpunkt die vorher deklarierten Objete mit Kommas getrennt.

Korrekt :slight_smile:

In C/C++ wird zwischen Deklaration und Definition (oder Implementierung) unterschieden. Das hast du auch bei normalen Funktionen außerhalb von Klassen (schon seit C), aber die Arduino IDE übernimmt dir (meistens) das Erstellen der Funktions-Protoypen ab. Wenn man Klassen mit Headern schreibt kommt man aber nicht darum herum.

Der Doppelpunkt ist der "scope resolution operator":

Gibt hier einfach an zu welcher Klasse die Methode gehört. Beim Konstruktor steht halt zwei mal der gleiche Name da, weil der Konstruktor nun mal so wie die Klasse heißen muss.

Aber er wird auch für namespaces verwendet. Oder um auf die globale Version einer lokalen Variable gleichen Namens zuzugreifen (Variablen-Überdeckung). Bei Vererbung gibt es einen ähnlichen Fall um auf Attribute einer Basis-Klasse zuzugreifen wenn Namen gleich sind.

Nachtrag:
Der Scope Operator ist :: (also zwei Doppelpunkte)
Der einfache Doppelpunkt gibt nur den Anfang der Initialisierungsliste an

Wobei es genau genommen nicht stimmt, dass die Objekte an der Stelle erst erstellt werden. Die existieren schon rein wenn man sie im Header angibt. Wie ein int auch. wifi_client() müsste man daher nicht extra hinschreiben, da nur der Default Konstruktor aufgerufen wird. Bei mqtt_client() braucht man es aber, da hier ein anderes Objekt mit dem Konstruktor übergeben wird. Die Initialisierungsliste ist wie der Name sagt u.a. dazu da die internen Variablen schon bei der Konstruktion richtig initialisiert werden.
Wenn man das mqtt_client(wifi_client) weglässt würde der Compiler das Objekt über den Konstruktor ohne Parameter erstellen und wenn der nicht existiert kommt ein Fehler.