Ich habe einen Code-Schnipsel aus einem Example in C per c&p in eine
Datei application_1.cpp verschoben.
In application_1.cpp:
1
voidNVICtest_1(void){
2
if(HAL_Init()!=HAL_OK)
3
{
4
/* Start Conversation Error */
5
Error_Handler();
6
}
7
8
...
9
...
10
11
}
12
13
staticvoidError_Handler(void)
14
{
15
/* Turn LED3 on */
16
BSP_LED_On(LED3);
17
while(1);
18
}
In application_1.hpp:
1
voidNVICtest_1(void);
2
staticvoidError_Handler(void);
Das führt zu den Warnungen:
'void Error_Handler()' declared 'static' but never defined
[-Wunused-function] C/C++ Problem
Unused static function 'Error_Handler' application_1.cpp
Code Analysis Problem
Wird 'static' in C++ anders betrachtet als in C? Oder was isch da los?
Statiker schrieb:> Wird 'static' in C++ anders betrachtet als in C? Oder was isch da los?
static bei Funktionen sagt dem Compiler das sie nur in diesem modul
verwendet werden. Es macht in deinen Fall also keine sinn. Warum willst
du sie static machen?
Du solltest die static-Funktion nicht im Headerfile deklarieren, sondern
am Anfang des CPP-Files.
Denn sonst deklarierst Du in jedem anderen CPP-File, das
application_1.hpp inkludiert, die static-Funktion, obwohl sie in den
anderen CPP-Files nicht vorhanden ist. Das ergibt dann die Warnung.
Hans (Gast) schrieb:
>Du solltest die static-Funktion nicht im Headerfile deklarieren, sondern>am Anfang des CPP-Files.>Denn sonst deklarierst Du in jedem anderen CPP-File, das>application_1.hpp inkludiert, die static-Funktion, obwohl sie in den>anderen CPP-Files nicht vorhanden ist. Das ergibt dann die Warnung.
Jetzt wo du es schreibst...
Danke.
rein aus Neugierde,
welchen Sinn macht es denn ein C-Programm in eine cpp file zu packen?
würde es sich hier nicht anbieten im Header eine Klasse mit public und
in dem BSP. von oben privat Funktionen zu erzeugen?
Habe selber mit c++ allerdings noch nicht so furchtbar viel gemacht...
>welchen Sinn macht es denn ein C-Programm in eine cpp file zu packen?
Das ist ne laaange verkorkse Geschichte (die viele Fürsprecher hat),
verbunden mit der philosophischen Frage was wann richtig und was falsch
ist...
nur malso schrieb:> rein aus Neugierde,> welchen Sinn macht es denn ein C-Programm in eine cpp file zu packen?
Es gibt ein 'Sprichwort' wonach C++ das bessere C ist.
Das 'C-Subset' von C++ unterscheidet sich vom echten C in ein paar
Punkten. Von den meisten derartigen Unterschieden muss man sagen: die
C++ Variante ist besser und hätte eigentlich in C von Anfang an so sein
sollen.
Ein Beispiel: enum sind in C nichts weiter als verpackte Integer. In C++
sind es aber echte Datentypen.
Das folgende Programm
1
#include"stdio.h"
2
3
enumKarte{Herz,Pik,Kreuz,Treff};
4
enumFarbe{Rot,Schwarz};
5
6
voidfoo(enumKartearg)
7
{
8
printf("foo\n");
9
}
10
11
intmain(intargc,char*argv[])
12
{
13
foo(Rot);
14
}
(man beachte den Datentypfehler beim Aufruf von foo) compiliert
anstandslos in C, nicht aber in C++.
Auch das in C ein void-Pointer einfach so mirnichts dirnichts an jeden
anderen Pointertyp zuweisbar ist, ist etwas was man eigentlich nicht
haben will.
Die Kleinigkeit, dass der Name einer Struktur automatisch ein eigener
Datentyp wird (kein typedef erforderlich), ist ein nettes kleines C++
Feature von dem man sich fragt, warum das in C nicht auch gleich so
gemacht wurde.
Und so gibt es noch ein paar Kleinigkeiten, in denen sich ein C++
Compiler beim compilieren eines reinen C-Codes zum Wohle des
Programmierers auswirkt.
Da die meisten Compiler sowieso Kombicompiler sind, die von einem
Zwischencode weg die Optimierungsstufe und Codegenerierung abarbeiten
und sich nur darin unterscheiden, welcher Satz von Regeln bei der
Übertragung von C bzw. C++ in diesen Zwischencode zur Anwendung kommt,
ist die Codequalität die hinten raus kommt normalerweise bei gleichem
Source Code identisch und nicht davon abhängig, ob der C++ oder der C
Compiler sich durch den Source geackert hat. Der einzige wirkliche
Unterschied der bleibt ist damit, dass der C++ Compiler an ein paar
(sinnvollen) Stellen pingeliger ist als der C Compiler.
Zumindest fällt mir jetzt auf Anhieb kein gegenteiliges (negativ)
Beispiel ein. :-)
nur malso schrieb:> rein aus Neugierde,> welchen Sinn macht es denn ein C-Programm in eine cpp file zu packen?
Es ist ein Irrglaube, dass in C++ die Klasse das einzige, Struktur
gebende Element wäre. Wenn etwas günstiger als Funktion modellierbar
ist, sollte man auch in C++ eine Funktion draus machen.
Ansonsten hat C++ z.B. namespaces, templates, striktere Typ-Prüfungen,
keinen eigenen namespace für structs und wahrscheinlich vieles mehr, was
einem die Arbeit erleichtert und angenehmer macht.
Karl Heinz schrieb:> Zumindest fällt mir jetzt auf Anhieb kein gegenteiliges (negativ)> Beispiel ein. :-)
Eins wäre das name mangling von C++. Das zwingt einem häufig die
Verwendung von extern "C" auf. Kein Problem wenn man weiß wie und warum.
Aber bis es soweit ist schafft es manchmal Frust.
Karl Heinz schrieb:> Zumindest fällt mir jetzt auf Anhieb kein gegenteiliges (negativ)> Beispiel ein. :-)
Naja, vielleicht haette ich zwei...
Ich schreibe eigentlich immer sehr gerne C++ Code, dennoch sind IMHO in
C einige Details ganz gut geloest und erleichtern das 'Leben':
1. Struct Initialisierung mittels Membername.
2. In C++ kann ein character array, welches ein struct member ist, nicht
sinnvoll initialisiert werden:
1
typedefstruct{
2
intsomeInt;
3
constcharsomeTxt[];
4
}MyTypeT;
5
6
constMyTypeTa={42,"geht in C++ so nicht"};
Punkt 1 ist einfach nett, wohingegen Punkt 2. mich wirklich nervt.
Ich weiss in C++ sind char-arrays Vergangenheit und man sollte
std::string verwenden. Im embedded Bereich braucht man strings trotzdem
ab und zu, aber ein std::string will ich dafuer nicht nehmen.
Das tritt oeffters mal auf, wenn man 3-rd party code oder libs
einbindet.
Ich versuche mir einfach dadurch zu helfen, dass in einem sepraraten *.c
file die Initialisierung globaler Variablen von diesem Typ erfolgt und
in *.cpp 'extern' referenziert wird. Soll die Variable aus irgend einem
Grund auf den Stack, dann geht es nicht (darf aber auch nicht vorkommen,
wenn man 'someTxt' aendern will).
Ansonsten, bei allem was Du schreibst, bin ich voll bei Dir und werde
meine 8-Bitter weiterhin, so weit wie moeglich, in C++ programmieren ;-)
-Foka
Ps. Wenn jemand eine elegante C++ Loesung fuer das
char-array-im-struct-Problem hat, wuerde ich mich sehr freuen diese
kennen zu lernen!
Das Problem ist, dass someTxt[] kein Array, sondern ein Pointer ist. Es
ist eine andere Schreibweise für: const char *someTxt;
Der C-Compiler schreibt den Text ins Programm und kopiert die Adresse
des Texts nach someTxt. Fertig.
Der C++-Compiler versucht nach meinem Verständnis den Text an den
Pointer zuzuweisen, was natürlich Unsinn ist, schon alleine weil die
Typen nicht passen.
Lösung: Ein richtiges Array erzeugen, mit dem Text initialisieren und
dann den Pointer setzen.
Foka Mokra schrieb:> Karl Heinz schrieb:>> Zumindest fällt mir jetzt auf Anhieb kein gegenteiliges (negativ)>> Beispiel ein. :-)>> Naja, vielleicht haette ich zwei...>> Ich schreibe eigentlich immer sehr gerne C++ Code, dennoch sind IMHO in> C einige Details ganz gut geloest und erleichtern das 'Leben':> 1. Struct Initialisierung mittels Membername.> 2. In C++ kann ein character array, welches ein struct member ist, nicht> sinnvoll initialisiert werden:>>
Das kann doch der Konstruktor übernehmen:
Foka Mokra schrieb:> Ich schreibe eigentlich immer sehr gerne C++ Code, dennoch sind IMHO in> C einige Details ganz gut geloest und erleichtern das 'Leben':> 1. Struct Initialisierung mittels Membername.> 2. In C++ kann ein character array, welches ein struct member ist, nicht> sinnvoll initialisiert werden:
Das sind allerdings beides C99 Features, die nur wenige Compiler
beherrschen. Wenn der Code portabel sein soll, ist das nicht zu
empfehlen.
Ein Array in einer Struktur kann man natürlich auch in C++ anlegen,
sofern man die Länge in der Deklaration angibt. Oder Du nimmst einen
Zeiger. Das lässt sich beides auch ohne selbstdefinierten Konstruktor
initialisieren.
Fabian O. schrieb:> Ein Array in einer Struktur kann man natürlich auch in C++ anlegen,> sofern man die Länge in der Deklaration angibt. Oder Du nimmst einen> Zeiger. Das lässt sich beides auch ohne selbstdefinierten Konstruktor> initialisieren.
Klar,
wenn ich es selbst in der Hand habe ist es kein Thema.
Das Problem, wie ich oben geschrieben habe, tritt vorwiegend im
Zusammenhang mit 3-rd party code oder libraries. Ich habe also nicht die
Moeglichkeit den Code zu aendern (oder nur sehr aufwaendig).
C ist eben nicht mehr einfach nur ein Subset von C++. Deswegen kann die
Verwendug von C-libraries in C++ code, unter Umstaenden, etwas
problematisch sein. Darueber muss man sich halt im klaren sein.
Uebrigens einfach durch 'const char* myText;' ersetzen ist auch nicht
immer die Loesung. Hier ein Beispiel was der gcc aus beiden macht:
1
typedefstructA{
2
intmyInt;
3
constchar*someText;
4
}AT;
5
6
typedefstructA2{
7
intmyInt;
8
constcharsomeText[];
9
}A2T;
10
11
constATa={42,"das geth in C++ nicht"};
12
constA2Ta2={43,"das geth in C++ nicht"};
Ein
avr-gcc --save-temps charArrInStruct.c
macht daraus:
1
.global a
2
.section .rodata
3
.LC0:
4
.string "das geth in C++ nicht"
5
.type a, @object
6
.size a, 4
7
a:
8
.word 42
9
.word .LC0
10
.global a2
11
.type a2, @object
12
.size a2, 2
13
a2:
14
.word 43
15
.string "das geth in C++ nicht"
Ein
1
constcharsomeText[];
ist also wirklich ein Text an exakt der Speicherstelle die durch den
struct vorgegeben wird. Hier kann ich aus der Adresse des symbols 'a2'
genau berechnen wo mein Text sich befindet.
Ein
1
constchar*someText;
ist dagegen ein Zeiger auf die Adresse wo mein Text vom compiler
abgelegt wurde, und ich kann die Textadresse aus der Adresse des Symbols
'a' nicht berechnen.
Im embedded Bereich will man solche structs oft in bestimmte
Speicherbereiche legen und dann evtl. mit speziellen Tools direkt aus
dem Flash herauslesen oder hineinschreiben. Das geht dann nur mit 'const
char x[]'.
Ich gebe zu, dieses Beispiel ist etwas konstruiert. Ich werde leider
damit sehr oft konfrontiert, weil bei uns in diesen Bereichen (in
solchen structs) bestimmte Konfigurationen und/oder Versionsnummern
liegen.
Ich denke das ist jetzt aber sehr off-topic, sorry.
-Foka
Markus F. schrieb:> Das kann doch der Konstruktor übernehmen:typedef struct t {> int someint;> const char *someTxt;> t(int someint, const char *someTxt) { this->someint = someint;> this->someTxt = someTxt; }> } MyTypeT;
Wenn Du nicht die gleichen Namen für die Membervariablen und die
Argumente des Konstruktors verwenden würdest, könntest Du Dir die
Dereferenzierung über den this-Pointer sparen.
Die in manchen Kreisen verpönte Notation, Membervariablen durch ein
vorangestelltes m_ kenntlich zu machen, ist hier hilfreich:
Rufus Τ. Firefly schrieb:> Markus F. schrieb:>> Das kann doch der Konstruktor übernehmen:typedef struct t {>> int someint;>> const char *someTxt;>> t(int someint, const char *someTxt) { this->someint = someint;>> this->someTxt = someTxt; }>> } MyTypeT;>> Wenn Du nicht die gleichen Namen für die Membervariablen und die> Argumente des Konstruktors verwenden würdest, könntest Du Dir die> Dereferenzierung über den this-Pointer sparen.
Oder man verwendet die Initialisierungsliste:
1
typedefstructt{
2
intsomeint;
3
constchar*someTxt;
4
t(intsomeint,constchar*someTxt)
5
:someint(someint),someTxt(someTxt){}
6
}MyTypeT;
Bei nicht-trivialen Membern ist das auch effizienter, da man so gleich
den richtigen Konstruktor aufrufen kann, anstatt erst den
Default-Konstruktor und dann Copy-Assignment zu machen.
Ich wüßte nur gerade keinen einfachen Weg, die Arraygröße automatisch
berechnen zu lassen, statt sie explizit als Template-Parameter angeben
zu müssen. Geht aber bestimmt irgendwie.
Ich persönlich mag's durchaus, "this" hingeschrieben zu haben, wenn ich
auch "this" meine (schreib's also auch meist hin, wenn's gar nicht
notwendig ist).
Die Problematik "fehlende flexible array member" lässt sich übrigens
auch - wenig schön, aber immerhin - über ein template lösen:
1
#include<cstddef>
2
3
template<size_tN>structt
4
{
5
intsomeint;
6
charsomechar[N];
7
};
8
9
t<sizeof("so geht's")>s={22,"so geht's"};
Der Schönheit kann man mit einem Macro auf die Sprünge helfen.
Karl Heinz schrieb:> Die Kleinigkeit, dass der Name einer Struktur automatisch ein eigener> Datentyp wird (kein typedef erforderlich), ist ein nettes kleines C++> Feature von dem man sich fragt, warum das in C nicht auch gleich so> gemacht wurde.
Hat mich beispielsweise noch nie gestört. Ein typedef benutze ich
höchstens für einen Funktionszeiger, weil mir die Syntax desselben als
Parameter zu umständlich ist.
Aber für eine struct?
>> Ich wüßte nur gerade keinen einfachen Weg, die Arraygröße automatisch> berechnen zu lassen, statt sie explizit als Template-Parameter angeben> zu müssen. Geht aber bestimmt irgendwie.
Dann werde ich wieder mal meckern ;-)
Das habe ich auch schon probiert und dachte ich habe endlich die
Loesung gefunden. Naja... zumindest teilweise, weil wie gesagt: die
Definition des structs liegt ja innerhlab von 3-rd party code (aber
lassen wir das kurz bei Seite).
Bei dieser 'Loesung' wird fuer jede, unterschiedlich lange, Zeichenkette
im Flash eine neue Tempalte-Instanz angelegt. Erstellt man also n
Instanzen von
1
constMyTypeT<X_n>a_n
dann fuellt sich die .text section schnell auf (natuerlich nur, wenn
fuer jedes n das X_n unique ist).
Ganz klar, es ist schon mal viel besser, vor allem ist es C++. Doch 100%
zufrieden war ich mit dieser Loesung auch nicht. So unglaublich
praktisch wie Templates sind, muss man, IMHO besonders im 8-Bitter
Kontext, aufpassen, denn das oben beschriebene Verhalten kann schneller
auftraeten als man denkt. Ich habe mal nicht daran gedacht und musste es
leidvoll erfahren...
-Foka
Foka Mokra schrieb:> Bei dieser 'Loesung' wird fuer jede, unterschiedlich lange, Zeichenkette> im Flash eine neue Tempalte-Instanz angelegt.
Was sollte da denn im Flash erzeugt werden außer den Daten, die da ja so
oder so liegen müssen? Das Template enthält doch gar keinen Code.
Rolf Magnus schrieb:> Foka Mokra schrieb:>> Bei dieser 'Loesung' wird fuer jede, unterschiedlich lange, Zeichenkette>> im Flash eine neue Tempalte-Instanz angelegt.>> Was sollte da denn im Flash erzeugt werden außer den Daten, die da ja so> oder so liegen müssen? Das Template enthält doch gar keinen Code.
Stimmt Du hast natuerlich Recht.
So lange das template keine Funktionen enthalet, hat es keinen Einfluss
auf die .text section.
Mein Fehler.
Ich hatte vergessen, dass ich damals in dem struct noch einige
Memberfuktionen hatte, in etwa so:
1
#include<stdio.h>
2
3
template<intN>
4
structAmen{
5
intmyInt;
6
constcharsomeText[N+1];
7
voidprintme()const{
8
printf("%s\n",someText);
9
}
10
};
11
12
constAmen<1>a1={1,"1"};
13
constAmen<2>a2={2,"22"};
14
constAmen<3>a3={3,"333"};
15
constAmen<4>a4={4,"4444"};
16
constAmen<5>a5={1,"55555"};
17
constAmen<6>a6={1,"666666"};
18
19
voidprintme(){
20
a1.printme();
21
a2.printme();
22
a3.printme();
23
a4.printme();
24
a5.printme();
25
a6.printme();
26
}
27
28
intmain(){
29
printme();
30
return0;
31
}
nach einem: avr-g++ -Wl,--Map=a.map tt.cpp
enthalet das map file folgendes:
1
*(.text.*)
2
.text._ZNK4AmenILi1EE7printmeEv
3
0x000000000000006e 0x20 tt.o
4
0x000000000000006e _ZNK4AmenILi1EE7printmeEv
5
.text._ZNK4AmenILi2EE7printmeEv
6
0x000000000000008e 0x20 tt.o
7
0x000000000000008e _ZNK4AmenILi2EE7printmeEv
8
.text._ZNK4AmenILi3EE7printmeEv
9
0x00000000000000ae 0x20 tt.o
10
0x00000000000000ae _ZNK4AmenILi3EE7printmeEv
11
.text._ZNK4AmenILi4EE7printmeEv
12
0x00000000000000ce 0x20 tt.o
13
0x00000000000000ce _ZNK4AmenILi4EE7printmeEv
14
.text._ZNK4AmenILi5EE7printmeEv
15
0x00000000000000ee 0x20 tt.o
16
0x00000000000000ee _ZNK4AmenILi5EE7printmeEv
17
.text._ZNK4AmenILi6EE7printmeEv
18
0x000000000000010e 0x20 tt.o
19
0x000000000000010e _ZNK4AmenILi6EE7printmeEv
... und ich habe mich gewundert warum mein Code, wegen paar Variablen,
enormen flash-Verbrauch hatte :-O
-Foka
Osterhase schrieb:> Das Problem ist, dass someTxt[] kein Array, sondern ein Pointer ist. Es> ist eine andere Schreibweise für: const char *someTxt;
das ist es nicht.
Foka Mokra schrieb:> Ich hatte vergessen, dass ich damals in dem struct noch einige> Memberfuktionen hatte, in etwa so:
dann pack die funktionen in die Basisklasse und mach die Ableitung mit
den Stringmember. NAchteil ist du brauchst eine virtuelle Funktion, die
die Daten liefert und doch wieder in jeder Klasse enhalten ist.
Aber die großen Funktionen sind wenigstens nur einmal enthalten.
also in etwa:
1
structBase{
2
virtualconstchar*getText()const=0;
3
intmyInt;
4
voidprintme()const{
5
printf("%s\n",getText());
6
}
7
8
};
9
10
template<intN>
11
structAmen:publicBase{
12
constcharsomeText[N+1];
13
virtualconstchar*getText()const{returnsomeText;};
14
15
};
(nicht getestet, nur ne idee - weiß nicht, ob die Aggregatinitialisierer
hier noch funktionieren)