Forum: Mikrocontroller und Digitale Elektronik GCC für ARM Probleme mit "new" ?


von Holger T. (holger1979)


Lesenswert?

Hallo,

ich habe hier ein größeres Problem der OOP und komme einfach nicht 
weiter.
Da ich vermute, dass es mit dem GCC Compiler für ARM zusammenhängen 
könnte, schreibe ich es unter Mikrocontroller.
Ich versuche das mal vereinfacht darzustellen.

Ich habe eine Klasse, die von mehreren Basisklassen erbt. Von dieser 
Klasse möchte ich mehrere Objekte erzeugen können.
Sowohl meine Klasse als auch die Basisklassen benötigen alle ein Handle 
für die einzelnen Methoden.
Dieses Handle wird global erzeugt und wird im Konstruktor als Pointer an 
meine Klasse übergeben und von dort aus wird der Pointer des Handle an 
die Basisklassen weitergegeben.

Hier mal eine Basisklasse:
1
#include <Namespace/Basisklasse.h>
2
3
namespace Namespace
4
{
5
6
Basisklasse::Basisklasse(TIM_HandleTypeDef *handle)
7
{
8
       pHandle = handle;
9
       // do Something
10
}
11
12
} /* namespace Namespace */

Dazu die meine Klasse:
1
#include <Namespace/MeineKlasse.h>
2
3
namespace Namespace
4
{
5
6
MeineKlasse::MeineKlasse(TIM_HandleTypeDef *handle) :
7
             Basisklasse(handle), Basisklasse2(handle), ... , BasisklasseN(handle)
8
{
9
       pHandle = handle;
10
}
11
12
} /* namespace Namespace */

pHandle ist jeweils ein Pointer auf den Handle. Dieser wird 
durchgereicht.
Soweit so gut.

Jetzt muss ich die Handle anlegen. Hier mache ich eine Deklaration im 
Header-File:
1
class Configuration
2
{
3
private:
4
TIM_HandleTypeDef htim1_switch;
5
...

Dieser Handle wird dann in einem Konstruktor initialisiert:
1
Configuration::Configuration()
2
{
3
       htim1_switch.Instance = TIM1;
4
...

Das Problem:
Ich muss jetzt Objekte der Timerklassen erzeugen, wobei ich den bereits 
konfigurierten Handle übergeben muss:
switch(&htim1_switch) ergibt: no match for call to 
'(Namespace::MeineKlasse) (TIM_HandleTypeDef*)'
switch = new MeineKlasse(&htim1_switch) ergibt: no match for 'operator=' 
(operand types are 'Namespace::MeineKlasse' and 
'Namespace::MeineKlasse*')

Weiterhin sollen die Timerobjekte außerhalb des Konstruktors natürlich 
erhalten bleiben.
Als Compiler verwende ich einen GCC für ARM. Kennt der vielleicht kein 
"new" ?

Kann mir da Jemand weiterhelfen?

Vielen Dank!!!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Holger T. schrieb:
> Kennt der vielleicht kein "new" ?

Der Compiler kennt es, aber du musst (je nach Umgebung, die dir ggf.
durch IDE etc. vorgegeben wird) u. U. das Backend für malloc()
selbst bereitstellen (die Funktion _sbrk()).

"No match" stammt vom Compiler, da fehlt ihm also irgendeine passende
Deklaration (in einem Headerfile).

Bist du dir sicher, dass du innerhalb von Namespace {} nochmal
"Namespace::" schreiben wolltest?  Übermäßig gut kenne ich micht mit
C++ auch nicht aus, aber das sieht mir suspekt aus.

von Holger T. (holger1979)


Lesenswert?

Oh, vielen Dank. Sollte natürlich Basisklasse heißen und nicht 
Namespace. Habs geändert.  ;)

Ich suche mal, wo ich das "new" her bekomme.
Du könntest Recht haben, dass der das vermisst.

Vielen Dank schonmal!

von Oliver S. (oliverso)


Lesenswert?

Die Fehlermeldung klingt nicht nach fehlendem new. Da gäbs eine 
Fehlermeldung vom linker.

Allerdings ist switch ein Schlüsselwort in C++ ...

Oliver

von Bolko (Gast)


Lesenswert?

Kannst Du ein Mickeymausbeispiel wie unten bauen? Was 
handle/timer/switch/Timerklasse usw. sind, wird aus der Beschreibung 
nicht ganz klar und deine Fehlerzeilen fehlen in den Codeausschnitten. 
Und warum man unbedingt new braucht, ist auch nicht klar.
1
#include <iostream>
2
3
class TIM_HandleTypeDef
4
{
5
public:
6
    TIM_HandleTypeDef(int ix) : name(ix)
7
    {
8
    }
9
10
    void do_thing(int i)
11
    {
12
        std::cout << "handle #" << name << " sagt " << i << "\n";
13
    }
14
private:
15
    int name;
16
};
17
18
19
class Basisklasse1
20
{
21
public:
22
    Basisklasse1(TIM_HandleTypeDef *handle)
23
    {
24
           pHandle = handle;
25
    }
26
    virtual void doit()
27
    {
28
        pHandle->do_thing(111);
29
    }
30
private:
31
    TIM_HandleTypeDef *pHandle;
32
};
33
34
35
class Basisklasse2
36
{
37
public:
38
    Basisklasse2(TIM_HandleTypeDef *handle)
39
    {
40
           pHandle = handle;
41
    }
42
43
    virtual void whatever()
44
    {
45
        pHandle->do_thing(222);
46
    }
47
48
private:
49
    TIM_HandleTypeDef *pHandle;
50
};
51
52
53
class MeineKlasse : public Basisklasse1, public Basisklasse2
54
{
55
public:
56
    MeineKlasse(TIM_HandleTypeDef *handle) : Basisklasse1(handle), Basisklasse2(handle)
57
    {
58
    }
59
};
60
61
class Configuration
62
{
63
public:
64
    Configuration() : htim1_switch(42)
65
    {
66
    }
67
68
    TIM_HandleTypeDef* get_handle()
69
    {
70
        return &htim1_switch;
71
    }
72
73
private:
74
    TIM_HandleTypeDef htim1_switch;
75
};
76
77
78
int main()
79
{
80
    Configuration config;
81
    MeineKlasse mainz(config.get_handle());
82
    mainz.whatever();
83
    mainz.doit();
84
}

von Holger T. (holger1979)


Lesenswert?

Der Quellcode ist noch wesentlich komplexer mit vielen Schaltern und 
vielen Timern. Den Namen "switch" habe ich als Synonym für einen 
beliebigen "Schalter" verwendet. Im Originalcode gibt es aber den Namen 
"switch" nicht. Daran liegt es nicht.

Es gibt im Prinzip einige Basisklassen und meine Klassen (Es sind Timer 
die Pins schalten). Wobei abhängig vom Timer jede meiner Klasse von 
anderen Basisklassen erbt.

Dadurch wird das Ganze so kompliziert.

Was nicht geht ist ein Objekt von "MeineKlasse" anzulegen.
Ganz grob gibt es eine Singletonklasse, diese erstellt im Konstruktor 
mehrere Instanzen von mehreren verschiedenen Klassen. Diese Klassen 
unterscheidet, von welchen Basisklassen diese erben.

Hier gibt es zwei Probleme. Wenn der Konstruktor durch ist sind die 
Objekte wider weg. Und das Aufrufen der Konstruktoren führt zu 
Fehlermeldungen.

Am liebsten würde ich die Struktur jetzt Global anlegen. Da gibt es aber 
das Problem, dass bei der Deklaration im Headerfile bereits ein 
Konstruktor ohne Parameter aufgerufen wird.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Holger T. schrieb:
> dass bei der Deklaration im Headerfile bereits ein Konstruktor ohne
> Parameter aufgerufen wird.

Nein, eine Deklaration "ruft" keinen Konstruktor.

Ein globales (oder statisches) Objekt ruft einen Konstruktor während
der Initialisierung der Applikation, also noch vor main().  Aber das
muss man erstmal definieren, nicht nur seine Klasse deklarieren.

> Wenn der Konstruktor durch ist sind die Objekte wider weg.

Kann auch so nicht sein, da ist noch was anderes foul.

von A. H. (ah8)


Lesenswert?

Eine Sache vorneweg: Wie viele pHandle hast Du in Deiner 
Klassenhierarchie? Hat jede Klasse ihr eigenes Handel oder nur die 
Basisklasse? Wenn es nur die Basisklasse ist musst Du es auch nur dort 
initialisieren, und das mach man besser mit der initializer-Syntax:

1
Basisklasse::Basisklasse(TIM_HandleTypeDef *handle) : pHandle(handle)
2
{
3
       // do Something
4
}
5
MeineKlasse::MeineKlasse(TIM_HandleTypeDef *handle) : Basisklasse(handle) ...
6
{
7
       // do Something
8
}

In der abgeleiteten Klasse genügt es, die Basisklasse zu initialisieren, 
es sei denn, diese definiert ihr eigenes pHandle Objekt, in dem Fall 
dann

1
MeineKlasse::MeineKlasse(TIM_HandleTypeDef *handle) : Basisklasse(handle)  , pHandle(handle)
2
{
3
       // do Something
4
}

Des weiteren fehlt, wie oben schon gesagt wurde, die vollständige 
Code-Zeile, die den Fehler verursacht. Ich unterstelle mal, sie sieht 
irgendwie so aus:

1
Namespace::MeineKlasse switch(&htim1_switch);

Wie auch schon gesagt ist switch ein Schlüsselwort und das würde ich 
grundsätzlich vermeiden (zumindest testweise), auch wenn es hier nicht 
das Problem zu sein scheint. Für mich klingt die Fehlermeldung so, als 
findet der Compiler den passenden Konstruktor nicht. Du hast zwar oben 
eine Definition angegeben, aber ist er auch innerhalb der 
Klassendefinition deklariert?

1
class MeineKlasse
2
{
3
        public:
4
                MeineKlasse(TIM_HandleTypeDef *); // ist diese Zeile da?
5
};

Falls diese Deklaration fehlt müsste allerdings auch die Übersetzung der 
von Dir angegebenen Definition mit einer ähnlichen Fehlermeldung 
scheitern.

von A. H. (ah8)


Lesenswert?

Holger T. schrieb:
> Hier gibt es zwei Probleme. Wenn der Konstruktor durch ist sind die
> Objekte wider weg.

Das ist natürlich klar. In einer Funktion (und ein Konstruktor ist 
letztlich auch nur eine Funktion) lokal angelegte Objekte existieren nur 
bis zum Verlassen der Funktion. Das Verwenden von Zeigern oder 
Referenzen auf lokale Objekte über die Laufzeit der Funktion hinaus 
führt definitiv zu undefiniertem Verhalten. Es sei denn, die Objekte 
werden statisch angelegt. Das würde ich aber nicht unbedingt im 
Konstruktor machen. Ein static class member wäre in diesem Fall 
vielleicht eine geeignete Lösung.

von Vincent H. (vinci)


Lesenswert?

Ich glaub immer noch, dass die erste Antwort richtig is...
Wenn der Threadersteller von _sbrk und Co nichts weiß, dann hats was. 
Die newlib-nano enthält halt schlichtweg keine Standard C++ 
Implementierung der ganzen System Calls.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Vincent H. schrieb:
> Wenn der Threadersteller von _sbrk und Co nichts weiß, dann hats was.

Das bringt aber einen Linkerfehler, keinen des Compilers.

von Holger T. (holger1979)


Lesenswert?

Ich habe jetzt folgendes versucht:

In der Headerdatei der Singleton-Klasse "Configuration" deklariere ich 
für jedes zukünftige Objekt einen Handle und das Objekt selbst.
1
TIM_HandleTypeDef handle_Schalter1;
2
MeineKlasse schalter1;

In der C-Datei in einer Init-Funktion (Configuration::Init()) 
initialisiere ich das Handle und das Objekt.
1
handle_Schalter1.Instance = TIM1;
2
MeineKlasse schalter1(&handle_Schalter1);

Für jedes Objekt, welches ich anlege gibt es eine Fehlermeldung im 
Konstruktor "Configuration".
1
Configuration::Configuration() // Hier werden die Fehler angezeigt
2
{
3
}

Die Meldung lautet:
no matching function for call to 'Namespace::MeineKlasse::MeineKlasse()'
 Configuration::Configuration()

Also irgendwie will der Compiler im Konstruktor Configuration() meine 
Klassen jeweils mit einem Standardkonstruktor aufrufen. Ich kann mir 
aber leider nicht erklären warum.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Zimmere doch bitte mal ein minimales compilierbares (bzw. eben
nicht compilierbares) Beispiel.

Irgendwie werde ich das Gefühl auch nicht los, dass das alles mächtig
„von hinten durch die Brust ins Auge“ gezimmert ist, aber warum du
die Architektur so gewählt hast, wirst du sicher selbst wissen.

von Holger T. (holger1979)


Lesenswert?

Ich habe jetzt mal den Code auf ein Minimum reduziert.

Hier ist die CPP-Datei der Klasse Configuration:
1
#include "Namespace/Configuration.h"
2
3
namespace Namespace
4
{
5
6
// Getting instance of class
7
Configuration& Configuration::getInstance()
8
{
9
  static Configuration instance;
10
  return instance;
11
}
12
13
Configuration::Configuration() // Hier im Konstruktor wird der Fehler angezeigt
14
{
15
16
}
17
18
void Configuration::TimerInit()
19
{
20
  htim1_highSideA.Instance = TIM1;
21
  htim2_stimTimeBase.Instance = TIM2;
22
  htim3_lowSideA.Instance = TIM3;
23
  htim4_lowSideB.Instance = TIM4;
24
  htim5_adcControl.Instance = TIM5;
25
  htim8_highSideB.Instance = TIM8;
26
  htim9_rangeSelect.Instance = TIM9;
27
28
// Folgende Zeilen gehen nicht, weil der Compiler "new" nicht moechte
29
//  highSideA = new TimerSlaveOCBreak(&htim1_highSideA);
30
//  stimTimeBase = new TimerUseMaster(&htim2_stimTimeBase);
31
//  lowSideA = new TimerMasterSlaveOC(&htim3_lowSideA);
32
//  lowSideB = new TimerMasterSlaveOC(&htim4_lowSideB);
33
//  adcControl = new TimerMasterSlaveOC(&htim5_adcControl);
34
//  highSideB = new TimerSlaveOCBreak(&htim8_highSideB);
35
//  rangeSelect = new TimerBase(&htim9_rangeSelect);
36
37
// Darum alternativ so:
38
  TimerSlaveOCBreak highSideA(&htim1_highSideA);
39
  TimerUseMaster stimTimeBase(&htim2_stimTimeBase);
40
  TimerMasterSlaveOC lowSideA(&htim3_lowSideA);
41
  TimerMasterSlaveOC lowSideB(&htim4_lowSideB);
42
  TimerMasterSlaveOC adcControl(&htim5_adcControl);
43
  TimerSlaveOCBreak highSideB(&htim8_highSideB);
44
  TimerBase rangeSelect(&htim9_rangeSelect);
45
}
46
47
} /* namespace Namespace */

Hier der dazugehörige Header:
1
#ifndef __CONFIGURATION_H
2
#define __CONFIGURATION_H
3
4
#include "stm32f2xx_hal.h"
5
#include "stm32f2xx_hal_tim.h"
6
#include "STM32F215xx.h"
7
8
#include "Namespace/MeineKlasse.h"
9
10
namespace Namespace
11
{
12
  
13
class Configuration
14
{
15
private:
16
TIM_HandleTypeDef handle;
17
MeineKlasse schalter;
18
19
public:
20
Configuration();
21
22
static Configuration& getInstance();
23
24
void TimerInit(void);
25
};
26
27
} /* namespace Namespace */
28
29
#endif /* __CONFIGURATION_H */

Hier die CPP einer Basisklasse:
1
#include <Namespace/Basisklasse1.h>
2
3
namespace Namespace
4
{
5
6
Basisklasse1::Basisklasse1(TIM_HandleTypeDef *handle)
7
{
8
  pHandle = handle;
9
}
10
11
Basisklasse1::~Basisklasse1()
12
{
13
}
14
15
} /* namespace Namespace */

Der dazugehörige Header:
1
#ifndef INCLUDE_BASISKLASSE1_H_
2
#define INCLUDE_BASISKLASSE1_H_
3
4
#include "stm32f2xx_hal.h"
5
#include "stm32f2xx_hal_tim.h"
6
7
namespace Namespace
8
{
9
10
class Basisklasse1
11
{
12
private:
13
  TIM_HandleTypeDef *pHandle;
14
15
public:
16
  Basisklasse1(TIM_HandleTypeDef *handle);
17
18
  virtual ~Basisklasse1();
19
};
20
21
} /* namespace Namespace */
22
23
#endif /* INCLUDE_BASISKLASSE1_H_ */

Meine Klasse, die von der Basisklasse erbt (CPP):
1
#include <Namespace/MeineKlasse1.h>
2
3
namespace Namespace
4
{
5
6
MeineKlasse1::MeineKlasse1(TIM_HandleTypeDef *handle) :
7
    Basisklasse1(handle)
8
{
9
  pHandle = handle;
10
}
11
12
MeineKlasse1::~MeineKlasse1()
13
{
14
}
15
16
} /* namespace Namespace */

Und der zugehörige Header:
1
#ifndef INCLUDE_MEINEKLASSE1_H_
2
#define INCLUDE_MEINEKLASSE1_H_
3
4
#include "stm32f2xx_hal.h"
5
#include "stm32f2xx_hal_tim.h"
6
7
#include <Namespace/Basisklasse1.h>
8
9
namespace Namespace
10
{
11
12
class MeineKlasse1 : public Basisklasse1
13
{
14
private:
15
  TIM_HandleTypeDef *pHandle;
16
17
public:
18
  MeineKlasse1(TIM_HandleTypeDef *handle);
19
20
  virtual ~MeineKlasse1();
21
};
22
23
} /* namespace Namespace */
24
25
#endif /* INCLUDE_MEINEKLASSE1_H_ */

Der Fehler wird im Konstruktor Konfiguration() angezeigt:
no matching function for call to 
'Namespace::MeineKlasse1::MeineKlasse1()'
 Configuration::Configuration()

von Ralf G. (ralg)


Lesenswert?

Jörg W. schrieb:
> Zimmere doch bitte mal ein minimales compilierbares (bzw. eben
> nicht compilierbares) Beispiel.

... soll hier im mikrocontroller.net bedeuten: Ein vollständiges, 
minimalistisches Programm, welches fast nichts weiter als den Fehler 
enthält. Am besten nur eine Datei als Anhang.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Holger T. schrieb:
> Ich habe jetzt mal den Code auf ein Minimum reduziert.

Da fehlen mir jetzt immer noch all die STM32-Headers dafür.

Kannst du die nicht irgendwie rauskicken und durch irgendwas
abstraktes "simulieren"?


> // Folgende Zeilen gehen nicht, weil der Compiler "new" nicht moechte
> //  highSideA = new TimerSlaveOCBreak(&htim1_highSideA);
> //  stimTimeBase = new TimerUseMaster(&htim2_stimTimeBase);

Jetzt weiß ich zumindest, woher deine Überschrift des Threads kommt.

Aber deine blumige Ausdrucksweise hilft nicht weiter.  Warum sollte
der Compiler "new" denn "nicht mögen"?  Welche Fehlermeldung bekommst
du denn?

Wie schon geschrieben worden ist, das Backend für new ist ein
schnödes malloc(), und dieses wiederum setzt in der newlib typisch
auf _sbrk() auf, welches du als Anwender liefern musst.

Mit der newlib nano habe ich noch nicht viel gemacht, aber musst du
die denn benutzen, kannst du nicht auch die Standard-newlib nehmen?

> Der Fehler wird im Konstruktor Konfiguration() angezeigt:

Bei welcher Compiler-Kommandozeile?

Sorry, es sind zwar schon viele Stückchen da, aber nichts, was ich
hier unabhängig von deiner Umgebung auch mal bei mir ausprobieren
könnte.

von Eric B. (beric)


Lesenswert?

Holger T. schrieb:
> // Folgende Zeilen gehen nicht, weil der Compiler "new" nicht moechte
> //  highSideA = new TimerSlaveOCBreak(&htim1_highSideA);

Wo und wie ist highSideA in diesem Fall deklariert?

> // Darum alternativ so:
>   TimerSlaveOCBreak highSideA(&htim1_highSideA);

Jörg W. schrieb:
> Irgendwie werde ich das Gefühl auch nicht los, dass das alles mächtig
> „von hinten durch die Brust ins Auge“ gezimmert ist,

Mja, geht mir auch so.

von Holger T. (holger1979)


Lesenswert?

Meine Configuration.cpp muss so aussehen:
1
#include "Namespace/Configuration.h"
2
3
namespace Namespace
4
{
5
6
// Getting instance of class
7
Configuration& Configuration::getInstance()
8
{
9
  static Configuration instance;
10
  return instance;
11
}
12
13
Configuration::Configuration() // Hier im Konstruktor wird der Fehler angezeigt
14
{
15
16
}
17
18
void Configuration::TimerInit()
19
{
20
  handle.Instance = TIM1;
21
22
// Folgende Zeile geht nicht, weil der Compiler "new" nicht moechte
23
//  meineKlasse1 = new MeineKlasse1(&handle);
24
25
// Darum alternativ so:
26
  MeineKlasse1 meineKlasse1(&handle);
27
}
28
29
} /* namespace Namespace */

Das obige Beispiel hat noch nicht die Namen angepasst und war nicht 
minimal.

Es handelt sich um eine Konfiguration, eine Basisklasse und eine 
abgeleitete Klasse. Jeweils mit cpp und h-file.
Das ist jetzt wirklich das Minimum.

von Dr. Sommer (Gast)


Lesenswert?

Hier gibt es ein Beispiel, wie man dem GCC "malloc" beibringt, und 
dadurch auch "new" :

STM32 Eclipse JLink Linux/Windows: Optional: Syscalls implementieren

Was auch manchmal hilft ist
#include <new>

von Holger T. (holger1979)


Lesenswert?

Hallo,

bei der Verwendung von "new" erhalte ich folgende Fehlermeldung:
no match for 'operator=' (operand types are 'Namespace::Meineklasse1' 
and 'Namespace::Meineklasse1*')
  meineKlasse1 = new MeineKlasse1(&handle);

Der Compileraufruf wird folgendermaßen aufgerufen:
arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -Og  -fmessage-length=0 
-fsigned-char  -ffunction-sections  -fdata-sections -Wl,-gc-sections 
-DDEBUG -DUSE_FULL_ASSERT  -DTRACE -DSTM32F215xx -DUSE_HAL_DRIVER 
-DHSE_VALUE=8000000 -Wall -Wextra -g3

Auch mir kommt das Ganze wie von hinten durch die Brust geschossen vor, 
aber es muss doch eine Lösung geben.
Das Schwierige ist eben, dass die Objekte zum Anlegen bereits ein 
konfiguriertes Handle benötigen. Daher kann ich die Objekte zum 
Programmstart leider noch nicht anlegen.

von Ecki K. (eck)


Lesenswert?

Wie ist meineKlasse1 deklariert? Von der Fehlermeldung her sieht aus als 
wenn's

Namespace::Meineklasse1 meineKlasse1 ist, und nicht

Namespace::Meineklasse1 *meineKlasse1.

Der Compiler beschwert sich, dass er einer nicht-Pointer-Variable einen 
Pointer zuweisen soll.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Holger T. schrieb:
> Hallo,
>
> bei der Verwendung von "new" erhalte ich folgende Fehlermeldung:
> no match for 'operator=' (operand types are 'Namespace::Meineklasse1'
> and 'Namespace::Meineklasse1*')
>   meineKlasse1 = new MeineKlasse1(&handle);

Dann musst du einen Zuweisungsoperator implementieren (also sowohl
deklarieren als auch definieren).

Die Fehlermeldung ist auch suspekt: links steht eine Klasse, rechts
ein Zeiger auf die Klasse?

Eventuell hilft es auch schon, wenn du den Konstruktor anders
schreibst:
1
class MeineKlasse1 : public Basisklasse1
2
{
3
private:
4
  TIM_HandleTypeDef *pHandle;
5
6
public:
7
  MeineKlasse1(TIM_HandleTypeDef *handle):
8
    Basisklasse1(handle),
9
    pHandle(handle)
10
  {};
11
12
  virtual ~MeineKlasse1();
13
};

Also für pHandle die Konstruktor-Syntax statt einer Zuweisung
nehmen, und eigentlich kann das dann auch gleich inline in den
Header.

> Der Compileraufruf wird folgendermaßen aufgerufen:
> arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -Og  -fmessage-length=0
> -fsigned-char  -ffunction-sections  -fdata-sections -Wl,-gc-sections
> -DDEBUG -DUSE_FULL_ASSERT  -DTRACE -DSTM32F215xx -DUSE_HAL_DRIVER
> -DHSE_VALUE=8000000 -Wall -Wextra -g3

Da fehlt der Name der C++-Datei.  Das wäre eine der Fragen gewesen,
welche Datei ich hier überhaupt compilieren muss, um dein Problem
nachzuvollziehen.  Aber wie oben schon geschrieben, es gibt in deinem
Beispiel noch so viele Dinge, die ich hier nicht herumliegen habe,
dass ich das ohnehin nicht selbst probieren kann.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Holger T. schrieb:

> Dieses Handle wird global erzeugt und wird im Konstruktor als Pointer an
> meine Klasse übergeben und von dort aus wird der Pointer des Handle an
> die Basisklassen weitergegeben.

Pointer auf Handle? Das klingt erst einmal total schräg, da ein Handle 
üblicherweise schon so etwas wie ein Pointer ist. Ein Handle sollte auf 
jeden Fall folgende Eigenschaften haben:
 - klein
 - leicht zu kopieren

> Hier mal eine Basisklasse:
> Basisklasse::Basisklasse(TIM_HandleTypeDef *handle)
> {
>        pHandle = handle;
> }

benutze doch Initialisierung, wenn Du initialisieren möchtest:
1
Basisklasse::Basisklasse(TIM_HandleTypeDef *handle)
2
  : pHandle( handle )
3
{
4
}

Ok, ich nehme alles zurück, TIM_HandleTypeDef ist sicher kein handle. ST 
ist sich aber auch keine gute Quelle um Software-Entwicklung zu lernen 
;-)

Wenn handle nie 0 sein sollte, dann zwingt sich ggf. eine Referenz auf 
(oder sogar eine const reference).

Ich fänd' dass "timer" ein passenderer Name wäre.

> Das Problem:
> Ich muss jetzt Objekte der Timerklassen erzeugen, wobei ich den bereits
> konfigurierten Handle übergeben muss:
> switch(&htim1_switch) ergibt: no match for call to
> '(Namespace::MeineKlasse) (TIM_HandleTypeDef*)'

Wo kommt jetzt switch her? switch ist ein reserviertes Wort in C++.

> switch = new MeineKlasse(&htim1_switch) ergibt: no match for 'operator='
> (operand types are 'Namespace::MeineKlasse' and
> 'Namespace::MeineKlasse*')

Ja, auf der linken Seite steht so etwas int und auf der rechten Seite so 
etwas wie int*. Passt nicht! Ansonsten code, den Du im Forum zeigen 
möchtest immer kopieren, nie abschreiben (siehe htim1_switch/switch)!

> Kann mir da Jemand weiterhelfen?

Bei Fehlermeldungen immer ganz genau hin gucken. In der Regel steht da 
schon, was das Problem ist. In Deinem Fall, versuchst Du halt eine 
Initialisierung oder Zuweisung mit nicht kompatiblen Typen.

mfg Torsten

von Mark B. (markbrandis)


Lesenswert?

Jörg W. schrieb:
> aber warum du die Architektur so gewählt hast, wirst du sicher selbst
> wissen.

Komische Software-Architekturen entstehen bisweilen auch und gerade 
deshalb, weil jemand nicht so genau weiß, was er da tut. ;-)

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Holger T. schrieb:
> Configuration& Configuration::getInstance()

Juhu ein Singleton! :'(

> {
>   static Configuration instance;
>   return instance;
> }
>
> Configuration::Configuration() // Hier im Konstruktor wird der Fehler
> angezeigt
> {

Genau: Configuration hat ein MeineKlasse1 als member. Und MeineKlasse1 
hat keinen default c'tor mit dem die Instanz von MeineKlasse1 
initialisiert werden kann.

Vererbung ist so ziemlich die stärkste Kopplung, die man haben kann. 
Bist Du Dir sicher, dass Du Dir das antun möchtest?

von Holger T. (holger1979)


Lesenswert?

Habe eine Lösung gefunden!   :-)   :-)

Mir wurde mitgeteilt, dass die angebliche Deklaration im Headerfile in 
wirklichkeit schon den Konstruktor aufruft. Und zwar den 
Standardkonstruktor.

Mir wurde auch empfohlen daraus Pointer zu machen.
In der Initialisierung hat dann das "new" plötzlich funktioniert.

Die Objekte werden jetzt also dynamisch angelegt und die Pointer sorgen 
dafür, dass ich weiß wo ich hin schreibe.

Dies steht im Headerfile:
1
TIM_HandleTypeDef handle1;
2
MeineKlasse1* schalter1;

Und die Implementierung:
1
handle1.Instance = TIM1;
2
schalter1 = new MeineKlasse1(&handle1);

Und siehe da, es läuft jetzt. Zumindest kompiliert er, den Rest muss ich 
noch testen. :-)

Vielen Dank für die vielen Posts! Ich konnte viel lernen.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Holger T. schrieb:
> Mir wurde auch empfohlen daraus Pointer zu machen.
> In der Initialisierung hat dann das "new" plötzlich funktioniert.

Schauder :-|

> Die Objekte werden jetzt also dynamisch angelegt und die Pointer sorgen
> dafür, dass ich weiß wo ich hin schreibe.

Der ganze HeckMac, damit Du dieses Singleton Antipattern verwenden 
kannst?

von Holger T. (holger1979)


Lesenswert?

Jetzt müssen noch die Werte gesetzt werden. Da weigert sich noch wer.
Wenn ich folgende Zeile habe:
1
htim->Instance->CR2 |= sMasterConfig->MasterOutputTrigger;

Dann erwarte ich eigentlich, dass der Wert vom MasterOutputTrigger (hier 
32) auch in die Variable OR-Verknüpft wird.

htim->Instance->CR2 bleibt aber 0.

Wobei htim auf 0x20000284 zeigt und htim->Instance auf 0x40000000.

von Mark B. (markbrandis)


Lesenswert?

Holger T. schrieb:
> htim->Instance->CR2 bleibt aber 0.

Was je nach Bitmuster auch durchaus ein korrektes Ergebnis sein kann.

Hast Du einen Debugger, mit dem Du das Programm mal durchsteppen kannst? 
Dann mach das. Zum Beispiel gibt es den GDB auch für ARM.

von Holger T. (holger1979)


Lesenswert?

Ich habe mich da durchgestepped.

htim->Instance-CR2 ist vorher 0. (Das könnte i.O. sein)

Wenn dann aber
1
htim->Instance->CR2 |= sMasterConfig->MasterOutputTrigger;

aufgerufen wird, und sMasterConfig->MasterOutputTrigger ist dabei 32, 
dann müsste dies durch die ODER-Verknüpfung eigentlich in der Struktur 
stehen.

Es bleibt aber weiterhin 0.

Vielleicht stelle ich das Problem später ins Forum unter einem anderen 
Namen. Mit dem Ursprungsproblem hat das ja nichts mehr zu tun.

von Robert S. (robert_s68)


Lesenswert?

Holger T. schrieb:
> Mir wurde mitgeteilt, dass die angebliche Deklaration im Headerfile in
> wirklichkeit schon den Konstruktor aufruft. Und zwar den
> Standardkonstruktor.
>
> Mir wurde auch empfohlen daraus Pointer zu machen.
> In der Initialisierung hat dann das "new" plötzlich funktioniert.
>
> Die Objekte werden jetzt also dynamisch angelegt und die Pointer sorgen
> dafür, dass ich weiß wo ich hin schreibe.

vom "new" gibt es zwei Varianten:

die "normale" Version holt sich dynamischen Speicher für die Größe des 
zu erzeugenden Objekts, ruft den Konstruktor auf, und retourniert einen 
Zeiger auf das erzeugte Objekt

die "placement" Version bekommt einen Pointer mit, ruft den Konstruktor 
auf, und retourniert den Zeiger auf das erzeugte Objekt. Kommt im 
"normalen" Leben eher selten vor, Anwendungen sind IMHO auf 
Optimierungen oder Spezialfälle (Container) beschränkt.

"new" ist also quasi synonym mit "Objekt ist dynamisch angelegt". Man 
kann zwar "placement new" auf eine lokale oder statische Variable 
anwenden, also sowas wie "Objekt o; new(o) Objekt;", das hat aber keinen 
Sinn, sollte m.E. ein Error sein, und mir ist in über 20 Jahren C++ 
sowas noch nicht untergekommen, daher bin ich mir auch ob der 
syntaktischen Korrektheit nicht sicher.

auf alle Fälle:
1
Objekt o = new Objekt; // ist falsch
2
3
Objekt* o2 = new Objekt; // richtig

sonst noch aufgefallen:

* Ein Singleton sollte man eigentlich vermeiden, zur Repräsentation von 
Hardware mags ja Berechtigung haben, dann kann man das aber anders 
implementieren, so dass man ohne das dauernde "getInstance" auskommt. 
Grund für virtuelle Methoden etc... gibts bei einem solchen Singleton eh 
per Definition nicht, das muss alles zur Compile-Zeit feststehen.

* Mehrfach von verschiedenen Basisklassen zu erben, die dann alle einen 
Zeiger auf das selbe Handle vorrätig halten, verschwendet Platz. 
Virtuelle Basis-Klasse zum Handle-Speichern würde gehen, hat aber 
möglicherweise andere Nachteile.

* Eleganter geht sowas mit templates und dem "CRTP", also sowas a la
1
template <class C>
2
class Basis
3
{
4
public:
5
  Basis() {};
6
  do_something() {C.getTimer()->start();}
7
}
8
9
class Klasse
10
: public Basis<Klasse>
11
{
12
public:
13
  Klasse() {m_timer = new Timer();};
14
  Timer* getTimer() { return &m_timer; }
15
16
private:
17
  Timer* m_timer;
18
}

* Das speichert das Handle nur einmal
* Basisklassen kann man viele machen
* Zusammengestrickt wird alles erst in der konkreten "Klasse", und zwar 
schon zur Compile-Zeit, es gibt also keinen Laufzeit-Overhead

addendum: das "new Timer" ist nur ein Beispiel, das brauchts so auch 
nicht.

von Ralf G. (ralg)


Lesenswert?

Holger T. schrieb:
> Dies steht im Headerfile:
1
TIM_HandleTypeDef handle1;
Eine Definiton im Headerfile??

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.