Archiv verlassen und diese Seite im Standarddesign anzeigen : MAL Wieder "C" Frust
Hallo zusammen:
Ich wollte gestern mal eben eine Software für mein RIGOL Oszilloskop installieren,
Dies schlug aber fehl, weil irgend etwas mit Microsofts Redistributable nicht in Ordung ist.
Keine Ahnung was das sein soll,
in meiner Systemsteuerung konnte ich finden, das es "ETLICHE" von diesen Dingern gibt.
Bei weiteren Recherchen scheint es so zu sein, das man die NUR braucht wenn jemand Software in Visual C++ geschrieben hat ???
Ich bin echt, mal wieder genervt von dem "C" Mist.....
Kann man keine Programme mehr in "vernünftigen Programmiersprachen" schreiben, die auch funktional sind....
Ihr wisst ja, dass ich ein "C" Gegner bin und irgendwie werde ich immer wieder bestätigt.
Wenn ich in Pascal, Delphi oder Lazarus ein Programm schreiben, dann habe ich genau EINE .EXE Datei, die funktional ist.
Zumal scheint es ja ZIG Versionen von diesem Redistributable zu geben, wo jedes Programm anscheinend sein eigenes benötigt.
Welche Programme die benötigen weis ich nicht, das werde ich merken wenn ich alle rausschmeisse.
So, der Frust abgelassen ;)
kann man die Dinger einfach so rausschmeissen und im Zweifelsfalle wieder nachinstallieren ?
Das sind ja wohl irgendwelche Bibliotheken, und warum kann man die nicht gleich mit ranlinken, dann bräuchte man die doch garnicht.
Siro
Ihr wisst ja, dass ich ein "C" Gegner bin und irgendwie werde ich immer wieder bestätigt.
hey hey hey hey bitte! Schmeiß hier C nicht mit C# und .Net zusammen, das ist echt mies!
Delphi oder besser gesagt Pascal (ist ja nur ein Dialekt von Pascal) sind zwar leichter als Anfänger aber in Performance kommt man nicht an ordentliches C/++ ran!
Das hier für ein Oszi massiver Gebrauch von .Net gemacht wird ist ein M$ Problem. Die von Rigol sind wohl primär C Programmierer (für die Firmware der Oszis) und müssen dnan auchnoch eine GUI dafür schreiben (selten schafft sich dafür eine Firma einen dedizierten Anwendungsentwickler an, wenn sie schon Programmierer haben)
Und wenn du schonmal Windows API mit reinem C gemacht hast wärst du nicht hier sondern hättest dich wohl schon erschossen :P (Sinnbildlich, es ist zu kotzen, das kann cih dir sagen)
Also nimmt man Visual Studio und das ist halt mit .Net zugekippt ... um es noch schlimmer zu machen hat man als trölfzig Stellen die Möglichkeit die gleiche Eigenschaft zu verändern damit etwas so aussieht wie es soll, nur wenn man anfängt an trölfzig stellen rum zu fummeln funktioniert hinterher garnichts mehr ... deswegen sage ich ja dass man eigentlich einen .Net gelernten Entwickler (der die Struktur hinter .Net begreift) für sowas braucht, auch wenn jeder hinz und kunz "mal was programmieren kann" mit .Net ... nur eben nicht richtig.
Aber da Ende vom Lied ist wie bei Java ... man muss die Redistributables eben mit reinpacken ...
schau doch mal in die zip ob da nicht ein isntaller neben der ssetup.exe ist (.msi z.b.) der bringt das notwendige paket meist mit.
Rausschmeißen würde ich sie nicht sonst funktioniert irgendwann irgend ein anderes Programm nicht ... wenn du nicht gerade eine zu kleine Festplatte hast würde ich sie lieber drin lassen
Und wenns erlaubt ist zu fragen, wo ziehst du deine Grenze der Aversion was C/++/# angeht denn? Ich muss gestehen dass mir persönlich C++ schon nciht schmeckt weils eine Symbolüberhäufte Sprache ist ....
"hey du hast da ein ":" vergessen ... ich schmeiß jetzt mal n kryptischen Fehler an irgend einer andern Stelle deswegen"
oder mein top favorit
"hmm ... da müsste eigentlich ein ":" stehen ... tut es aber nicht ... egal compilen wir es trotzdem und verhalten uns am ende einfach affig"
und was .Net angeht, finde ich es herrlich wenn sich ein Ingenieur damit versucht und du am Ende eine Episode von 1000 Wege ins Gras zu beißen mit der Anwendung machen kannst weil der ingenieur linear denkt und alles in einem einzigen Thread schreibt weil Thread-Handling ne Wissenschaft für sich ist und du dann an der GUI zugucken kannst was das Programm im Hintergrund rechnet.
Bestes Beispiel dafür ist eine Profi-Fräse von einem Namenhaften Hersteller dessen Software sogar das Kamerabild während des Fräsen zeigt ... aber die GUI bei jeder Bewegung inklusive Kamerabild einfriert solange die Fräse eine kontinuierliche Bewegung ausführt, weil der Steuerprozess im selben Thread wie die GUI läuft und die Methode zum verfahren auf das Ende der Fahrt wartet
PS: um mich Shedepe an der Stelle ein kein wenig entgegen zu stellen, ich habe beide Welten programmiert, Borland Turbo Pascal (und die können einem auch herzlichen mit Packages und Libs um die Ohren fliegen) und intensives Anwendungsentwicklung mit C++ .Net .... ich mag Pascal echt lieber weil es eindeutiger ist, aber schneller ist halt immernoch OS-Nativ mit .Net
Ohne jetzt mal auf deinen Rant gegen C einzugehen:
Überlege dir mal warum es eventuell eine schlechte Idee ist fest gegen Bibliotheken zu linken. Eine davon ist: Du kannst ansonsten keine Updates der Bibliothek machen z.B. bei Sicherheitslücken. Das ist vorallem bei so zentralen Sachen wie dem was in der Redistributable drin ist sinnvoll.
Die Unterschiedlichen Versionen kommen daher, dass für jede Major Version ein neues Release gemacht wird (dann wenn die Kompabilität gebrochen wird).
Btw. Du kannst auch in C diese Bibliotheken fest reinlinken. Ich würde es dir nur nicht empfehlen. (Und mach doch bitte noch einen Unterschied zwischen Visual C++ und normalen C/C++)
Und um doch noch auf deinen Rant einzugehen: ich bin entschiedener Delphi Gegner ;)
Hallo Ceos und shedepe
es ist nicht ganz so negativ gemeint, wie es aussehen mag....
aber da wir hier im "Tratsch" Thread sind, kann man mal vom Leder lassen
Ich kann ja zu C# C++ oder.NET z.B. garnichts sagen, da ich nie damit gearbeitet habe...:p
Aber ich war "mal wieder" genervt, als ich ergoogeln muste,
das die Rigol Software nicht funktioniert, weil sie anscheinend in einer Variante von "C" geschrieben wurde.
Wobei dies anscheinend C++ ist.
Die Abneigung von "C" hat bei mir viele Gründe, wobei ich nun nach zig Jahren programmieren in "C"
für Embedded Anwendungen die meisten Hürden erlebt und überwunden habe.
Habe mit dem "normalen" C eigentlich gar keine Probleme mehr.
Irgendwie hab ich gelesen, das man die Software bei C++ wohl hätte "statisch" linken können, dann bräuchte man wohl
die Laufzeitbibliotheken garnicht, zumindest hab ich das so verstanden. Deshalb finde ich es sehr schade,
dass Rigol eine so "halbherzige" Software dazu liefert.
Hab grad gelesen von shedepe, dass es aber nicht unbedingt sinnvoll ist wegen Updates etc.
@Ceos:
Windows API programmieren:
Jo, is geil, wenn man dann Masochist ist :-)
Ich hab da auch so einige Erfahrung und bin immer wieder mal dran an der API
Hab mal am Audio Interface programmiert (Multimedia Api) und vor kurzen grad wieder RS232 und Timer..
brauchte eine serielle Schnittstelle die mit den FTDI oder SILABs Treiber kommuniziert.
Zurück zum Redistributable:
Ich muss mal gucken welche Version die Rigol Software braucht und Du hast recht,
meistens haben die Installer gleich eine Version der Bibliothek mit installiert.
Da werd ich sicher noch fündig.
@shedepe:
Ich bin auch ein Delpi Gegner geworden, da es einfach ÜBERTEURT war und nun ist Lazarus (Open Source Freeware) mein Nonplusultra...:)
Siro
RS232 mit Windows API .... Strg + A, Strg + D, Strg + S .... wegrenn
ich pack mir immer eine POSIX Uart Lib dazwischen ... sonst wird man beim setzen der serial struct irre
@Ceos:
Du meinst bestimmt die Struktur DCB, muss man mögen :)
Viel interesanter fand ich, um zu ermitteln wieviele Bytes sich im Empfangspuffer befinden,
muss man die Funktion "ClearCommError" aufrufen. Da muss man erstmal drauf kommen....
und da es keine Uart Interrupts mehr gibt, habe ich diese Funktion nun in einen Timer packen müssen,
weil ich Zeitnah (sofern man das bei Windows überhaupt erwähnen darf) meine Daten haben wollte.
gegen C ist eigentlich nichts zu sagen: es ist klar definiert, schreibt extrem wenig vor, kann so gut wie jede Hardware adressieren und händeln, hat nur knapp 20 fest definierte Bezeichner, eine extrem einfache Syntax, und die Executables sind rasend schnell.
Nicht umsonst werden ganze Betriebssysteme damit geschrieben (wozu es ja gerade entwickelt wurde).
Alles was du bemängelst, betrifft die libs, die andere Leute geschrieben und zu vertreten haben, und da gebe ich dir völlig Recht: da ist irrsinng viel Schrott unterwegs.
Hast Du nicht unrecht HaWe, jede Sprache hat Vor und Nachteile.
Ich hab aber doch immer wieder mal Probleme mit C die ich in Pascal nicht habe.
Mal abgesehen davon, dass es bei C99 wohl 190 Fälle gibt, wo nicht vorgeschrieben ist was der Compiler daraus machen soll...
Muste ich natürlich mit in meine Risikoanalyse aufnehmen....
https://www.elektronikpraxis.vogel.de/zertifizierte-funktionale-sicherheit-mit-der-programmiersprache-c-a-426198
Mich würde ja mal interessiren um welche Fälle es sich da handelt, falls da jemand Informationen oder Links zu hat, wäre ich sehr dankbar.
Zum Problem mit den Redistributablen von Microsoft:
Ich war mal, wie immer, experimentierfreudig ;) ich habe die gesamten Microsoft Redistributables deinstalliert.....:p
Ältestes zuerst bis zum Aktuellsten.
Das Letzte liess sich nicht entfernen, vielleicht braucht das Windows selbst, keine Ahnung.
Aber nun konnte ich problemlos meine RIGOL Software installieren, was voher nicht ging.
Ich hatte übrignes nach der Deinstallation diverse Programme ausprobiert, ob sie davon betroffen sind.
OpenOffice
FireFox
Eagle
MCUXPresso
MPLAB
LAZARUS
keiner hatte Probleme, aber es war, wie gesagt, noch eine letzte Version vorhanden von 2017
viel mehr an Software läuft auch nicht auf meinem Rechner.
wenn ich jetzt sagen sollte, die Rigol Software läuft gut, müste ich lügen.
Das Bild auf dem Rechner entspricht nicht dem auf dem Oszilloskop Bildschirm.....Ohje.....
Wenn man das Programm verlässt und neu startet stimmt es wieder. Man darf nix am Ossi ändern, das bekommt die Software am PC anscheinend nicht mit.
Ist nicht perfekt, aber brauchbar...
Siro
Mal abgesehen davon, dass es bei C99 wohl 190 Fälle gibt, wo nicht vorgeschrieben ist was der Compiler daraus machen soll...
Muste ich natürlich mit in meine Risikoanalyse aufnehmen....
Und das ist der Grund warum wir hier auf Arbeit keine schönen Dinge haben können (TM)
Wir haben Guidelines die uns gewisse Dinge verbieten ... So viele schöne Kontrukte die man bauen kann aber dann kommt der "nein das darfst du nicht"-Hammer weil "offsetof" nicht sauber spezifiziert ist
... und solch ähnliche Späße
Habe mir eine Standpauke über X-Makros anhören müssen .... mimimi statische Codeanalyse kann keine Functionpointer in Arrays verfolgen mimimi .... gut, jetzt ists halt ein ebenfalls per X-Makro generierter riesen-switch-case statt einer Spungtabelle ... der Compiler baut eh wieder ne Sprungtabelle draus aber wenigstens kann die statise Code analyse das jetzt verarbeiten
Moin Ceos
Ist doch mal schön zu hören, dass ich nicht der Einzige bin, der nach diversen Regeln arbeiten soll...
Wobei ich glücklicherweise NOCH keine wirklichen Vorgaben habe wie ich was implementiere.
Der Compiler macht eh was er will, das kann man kaum steuern.
Die wirklichen Probleme einer nicht funktionierenden Software liegen oftmals völlig woanders.
Das ist auch der Grund warum ich stets mit verschiedenen Optionen compiliere und teste.
Es finden sich immer wieder neue Merkwürdigkeiten, die oft sogar mit der Prozessorarchitektur zusammen hängen.
Das kann eh keine automatische Codeanlayse feststellen.
Ich habe auch Funktionstabellen, weil es wesentlich Übersichtlicher ist als ein switch-case.
"offsetof" kannte ich noch garnicht, witzigerwiese scheint das genau das zu sein, was ich für meine
EEPROM struktur verwende....
Was soll daran nicht "sauber" bzw. nicht nachvollziehbar sein ?
Im eeprom bei mir liegt ein struct, wo welcher Wert liegt ist nicht direkt bekannt, aber man kann es berechnen:
t_ee_struct *p;
/* we dont know where the userdata is stored in the EEPROM */
/* but we can calulate the address: */
p = 0; /* think the struct starts at address 0 */
/* convert the pointer to a 32 Bit value (EE-ADDRESS) with the offset of setup_data */
ee_address = (U32)(&p->setup_data);
// Deine genannte Variante mit offsetof sieht doch VIEL besser aus, muss ich direkt mal ausprobieren .....
ee_address = offsetof(ee_data,setup_data);
das macro hab ich grad gesichtet:
#define offsetof(struct_type, member) \
(size_t) &(((struct_type *)0)->member)
Siro
weil es nicht prozessorspezifische alignment regeln beachtet (zumindest laut spezifikation) ... also bei nicht "packed" structs oder wenn du bit access machst könnte es gut passieren dass du irgendwo in eine lücke geschickt wirst oder hinter das struct guckst oder auf der stelle trittst
es ist eben nicht "eindeutig"
Achso, ja, das packed hab ich drin:
/* the User preset range in the EEPROM was fixed to 16 Bytes */
/* these paramters can changed by the user in normal operation mode */
#pragma pack(1) /* align the structure to bytes to minimize size */
typedef struct
{
U16 last_P10_pre; /* last user Pressure for PRE-Insufflation */
U16 last_Q10_pre; /* last user Flow for PRE-Insufflation */
U16 last_P10_main; /* last user Pressure for MAIN-Insufflation */
U16 last_Q10_main; /* last user Flow for MAIN-Insufflation */
BOOL last_Video; /* last user Video on/Off state */
U8 reserved1;
U16 reserved2;
U16 reserved3;
U16 checksum; /* CRC16 */
} t_user_data;
#pragma pack() /* end of packed structure */
hier "dürfte" es also keine Lücken geben, der Compiler "sollte" es Byteweise ablegen.
ohne das packed, wäre es gut möglich, dass er es auf die Standardbreite eines int aligned.
Probier ich gleich mal....
- - - Aktualisiert - - -
ich hab das grad mal probiert:
//#pragma pack(1) /* align the structure to bytes */
typedef struct
{
short a;
char b;
short c;
} t_my_data;
//#pragma pack() /* end of packed structure */
o = offsetof(t_my_data,c);
jo, hier verschiebt sich der Offset um 1 Byte
der Offset zu "c" ist unpacked = 4 bei packed = 3
der GNU-ARM Compiler macht das als richtig.
fast alle machen das richtig, aber es ist halt nciht "spezifiziert" ... es KÖNNTE ja sei dass man IRGENDWANN mal einen exotischen compiler verwenden KÖNNTE der EVENTUELL ..... mehr sarkasmus und das forum erleidet einen schlaganfall, drum brech ich das mal ab XD
:) das steht ja in meiner Softwaredokumentation welcher Compiler verwendet wird... ;)
Juti, sind wa durch, ich wünsche alleseits ein schönes Wochenende.
Habe wieder etwas dazu gelernt, mit dem MACRO: mir gefällt es
Siro
kann mal ein kleines abstrakt davon hier einstellen, cih finde es ziemlich nützlich und relativ kompakt ist es auch (bis es sich entfaltet .... origami issn scheiß dagegen :D )
@Ceos: ja stell mal rein....
Ich "tratsche mal noch ein bissle" ;)
Zum Thema Codetest statisch oder wie auch immer: wie Aussagekräftig ist das eigentlich ?
Ich hatte 15 Jahre lang den gleichen "funktionierenden" Code für die RS232 verwendet.
Der lief auf meinen PICs und auf auch dem PC mit INTEL 80xx
bis ich dann auf ARM umgestiegen bin, plötzlich traten merkwürdige Fehler auf:
Das beschreibt recht gut wie wenig Aussagekräftig ein Test, ja sogar ein Unittest sein kann, wenn er nicht direkt auf dem Zielsystem (Prozessor) ausgeführt wird.
Der Comiler hat seine Aufgabe richtig getan, aber die Systemarchitektur macht einem plötzlich einen Strich durch die Rechnung:
Wer lange Weile hat (is ja Wochenende) kann sich das gerne mal anschauen, was ich damals ermittelt habe:
Ein Zeichen aus dem Empfangspuffer holen:
U8 uart_getc(void)
{ U8 value;
if (rx_count)
{
value = rx_buffer[rx_out];
if (++rx_out >= UART_RX_BUFFER_SIZE) rx_out = 0;
rx_count--;
}
return value;
}
dazu muss man wissen, was der Compiler für einen Assembler Code generiert hat: aus rx_count-- wird beim LPC1768 (ARM Code)
LDR r0,[r2, #0x14] ; lade r0 mit dem Wert rx_count
SUBS r0,r0,#1 ; ziehe von r0 eins ab
STR r0,[r2,'0x014] ; speicher den Wert an rx_count
Nehmen wir an, rx_count steht auf 1. Nun gelangen wir in den Assemblercode 2.te Zeile. im r0 Register befindet sich dann der Wert 1. In dem Moment tritt ein Receive Interrupt auf. Die Software verzweigt sofort zur Interrupt Service Routine.
Dort steht folgender Code:
void uart_isr(void)
{
rx_buffer[rx_in]=databyte;
if (++rx_in >= UART_RX_BUFFER_SIZE) rx_in = 0;
rx_count++;
}
rx_count wird also am Ende der Interrupt Routine um 1 erhöht.
jetzt kehrt die Software zurück zur Routine uart_getc.
zur Info: rx_count stand auf 1 und wurde im Interrupt nun auf 2 erhöht. Im Register r0 steht aber noch der Wert 1, dieser wurde ja zuvor in der funktion uart_getc geladen. Nun zieht die Software in uart_getc eins ab. im r0 register steht nun 0. Dieser Wert wird dann zurück an die Speicherstelle rx_count geschrieben. Das wars es schon, alles durcheinander. Es befindet sich ein neues Zeichen im Puffer und rx_count steht auf 0.
Damit dies nun nicht mehr passiert, werden vor dem Verändern der variablen rx_count alle Interrupts gesperrt. Im Prinzip bräuchte man nur den RX Interrupt sperren. Dann wird der vollständige Code
rx_count-- in jedem Fall ausgeführt. Danach werden die Interrupts
wieder eingeschaltet.
Selbiges Problem tritt auch beim TX Interrupt auf.
Auch hier werden vor dem Verändern der Variablen tx_count alle Interrupts gesperrt.
Bei einem PIC zum Beispiel tritt dieses Problem normalerweise nicht auf, da er ein pre oder post increment direkt in einem Befehl ausführt, da kann also keiner "dazwischen" funken.
Darauf sollte man sich aber auch hier nicht verlassen, wer weis schon was der C-Compiler daraus macht.
Je nach verwendeten Prozessor kann es also funktionieren oder auch nicht. Wenn der Prozessor zum Beispiel einen 2 Assembler Befehl erzeugt, wäre auch kein Problem vorhanden: Beispiel:
hier kann das mov R0,@rx_count ; lade R0 Register mit der Adresse von rx_count
inc [R0] ; erhöhe die Speicherstelle worauf R0 zeigt
auch hier kein Promlem, weil das indirekte increment nicht unterbrochen werden kann.
da es in einem Befehl ausgeführt wird.
Bei einem Intel Prozessor passiert dies auch nicht, auch hier wird das increment in einer einzigen Anweisung ausgeführt. Sonst wäre der Fehler auch schon in meinen PC-Programmen vor 15 Jahren aufgetreten.
LEA EDI,dword ptr rx_count
inc dword ptr ES:[EDI]
Eine statische Codenalyse oder was auch immer für ein Testprogramm hätte diesen Fehler wohl nie aufdecken können.
Fazit: "atomarer code" war gestern, das geht kaum noch..
Heutztage bekomme ich teilweise nichtmal ein einziges BIT mehr gesetzt ohne das der Code nicht unterbrochen werden könnte.
Da werden teilwesie 5 Zeilen Assemblercode draus gebildet obwohl ich nur ein Bit setzten möchte.
Ich liebe aber solche Fehler, daran könnte ich mich ewig dran aufhalten.
Kein Witz, sowas dann zu finden macht mir echt Spass. Besser also DOKU ;)
----- P A U S E --
Schön fand ich auch, wo der C-Compiler mir Codezeilen weggelöscht hat, die ER für unnötig hielt:
Beim UART muss man das sogenennt DLAB Bit setzen um auf spezielle Register zuzugreifen,
das gab es schon in 90 Jahren so und es wurde tatsächlich in die modernen 32 Bitter übernommen.
Also setzte ich das Bit
dann meine Baudrate
und setze das Bit zurück
Der Compiler fragt sich: Wozu soll das gut sein, Du benutzt es doch garnicht und schmeist genau diese beiden Zeilen raus....
Das hat und hätte ein Pascal Compiler nie getan.
Natürlich kann und muss man dann gegensteuern. Ich brauche "volatile" und dann gehts.
Aber hier ist mir der Compiler manchmal zu selbständig. Man kann solche Optimierungen auch leider nicht abstellen.
Was sagt da die Code Anaylse dazu ? solche Fehler können doch garnicht erkannt werden ?
Zumal die der Compiler ja erst reinbaut...Das lief ja, bis er eigenmächtig Änderungen vorgenommen hat :)
Diese moderne, angebliche Intelligenz ist nicht immer wünschenswert.....
White_Fox
26.11.2018, 09:11
Was bin ich froh, Hardware zu machen... :D
(Naja, stimmt nicht ganz, bei meinem letzten Job war ich im Wesentlichen auch nur mit Programmiererei beschäftigt, und was hab ich da über Visio C und Visual Studio geflucht, diese elenden Scheißkonstrukte...)
wie Aussagekräftig ist das eigentlich ?
ziemlich, es kommt auf das Tool an und die Regeln die darin festgelegt werden ob und welche Fehler man findet .... für dich wäre es zum Beispiel extreeeeem hilfreich gewesen, wenn du eine REgel definiert hättest die Warnungen für alle Variablen generiert die innerhalb des statischen und des ISR Code verwendet werden und NICHT "volatile" deklariert wurden.
Du hast das perfekte Beispiel gezeigt wie man Variablen nicht verwenden sollte. Die Count Variable sowohl im statischen als auch im ISR Code zu manipulieren ist gefährlich.
Ich benutze für derartige Zwecke üblicherweile (ich nenn sie mal so) "Katz und Maus" Variablen und bau mir damit einen Ringpuffer. Die Maus ist der ISR counter der sich immer weiter erhöht und defineirt oder implizit überläuft (wenn man den Luxus von genug Speicher hat). Die Katze ist dann mein Readmarker, der immer der Maus hinterher läuft. So kann ich immer und zu jederzeit sicherstellen dass nur ein "Prozess" (streng genommen ist das multithreading) eine Variable zu einer bestimmten Zeit bearbeiten kann und ichkann sogar zu jedem Zeitpunkt eine Plausibilitätsprüfung machen.
Ausnahme dabei ist natürlich wenn du Variablen hast die aufeinander zulaufen, dann musst du tatsächlich volatile machen damit du garantiert den aktuellen Wert erhälst oder eben manuell die ISRs blockieren bis die Plausibilitätsprüfung abgeschlossen ist.
Das Makro das ich versprochen habe kommt im Laufe des Tages wenn cih es mal kurz funktionell arrangiert habe
- - - Aktualisiert - - -
So, hier ist das versprochene Makro, man muss zwar noch ein wenig improvisieren damit es auch sofort funktioniert aber der code wie er ist kann als template benutzt werden, man muss nur seine Datenstrukturen einbauen und den switch case entweder in eine art "callViaEnum(myEnum_t what)" methode dafür einbauen oder gleich direkt im code einarbeiten
//die Kontruktionsliste, hier werden alle Daten eingegeben um das Programm zu konstruieren
#define XTABLE(ENTRY) \
// ENUM | METHODE | DATA (ACHTUNG, dieser Kommentar erzeugt ein Multiline Comment Warning und dient nur als Tabellenkopf)\
ENTRY( CALL1, callMethode1, {irgend_welche_daten, in_struct, oder_array, oder_einzeln)\
ENTRY( CALL2, callMethode2, {irgend_welche_daten, in_struct, oder_array, oder_einzeln)\
ENTRY( DO_STH, callSomething, {irgend_welche_daten, in_struct, oder_array, oder_einzeln)
//ein X-Makro um uns passend die notwendigen Daten bereit zu stellen
#define AS_DATA(ENUM,METHODE,DATA) DATA
//das X-Makro um den Caller Switch-Case zu erzeugen, benötigt das DATA Makro, deswegen die Reihenfolge beachten
#define AS_METHODE_CASES(ENUM,METHODE,DATA) \
case(ENUM):\
{\
parameter = prepareSomething(DATA);\
METHODE(parameter,irgendwasanderes);\
break;\
}
//hier erzeugen wir einen Enum anhand unserer Tabelle als Lookup und Call Referenz für den switch-case
#define AS_ENUM(ENUM,METHODE,DATA) ENUM,
typedef enum
{
XTABLE(AS_ENUM)
LAST_ENTRY
}entryEnum_t;
//wir erzeugen hier Beispielsweise auch eine LookupTable mit den Daten falls wir dynamisch darauf zugreifen müssen
//und hinterlegen sie implizit/explizit im ROM (static const, oder andere prefixe je nach compiler) zum RAM sparen
typedef struct
{
entryEnum_t entryEnum;
somedata_t entryData;
}lookupDataStruct_t;
static const lookupDataStruct_t entryDataLookup[] = {
XTABLE(AS_DATA)
};
//hier generieren wir die eigentlichen Prototypen
#define AS_PROTOTYPE(ENUM,METHODE,DATA) return_t METHODE(simpledata_t input_parameter, otherdata_t andere_sachen);
XTABLE(AS_PROTOTYPE)
//irgendwo im Code erzeugen wir dann an der passenden stelle den switch-case
switch(entryEnum)
{
XTABLE(AS_METHODE_CASES)
default:
{
ASSERT(FAIL);
break;
}
}
Jedes Tool sollte in der Lage sein die Makros aufzulösen und über den switch-case eine code coverage von 100% damit erreichen
wenn ich nur Functionpointer in einer Tabelle einsetze kommt die Linkerebene dazwischen, man kann es zwar logisch auflösen aber das beherrscht kaum ein Tool, da die wenigsten auch den konkreten Inhalt von Arrays in die Analyse mit einbeziehen.
Was dieses X-Makro erzeugt ist ein sauber lesbarer Switch Case der Anhand der Enums die Daten passend verarbeite udn der Methode dann die Parameter übergibt und sie aufruft.
Wer Tippfehler findet darf sie behalten :P
Hallo CEOS,
für mein count Problem habe ich jetzt auch einen anderen Weg genommen,
scheinbar ähnlich deiner Beschreibung.
Ich habe jetzt getrennte Einlese und Auslesezeiger.
Der Empfangs RX-Interrupt benutzt nur "seinen" Einlesezeiger.
Das Hauptprogramm seinen "Auslesezeiger"
Hier kann man nun, um die Anzahl Bytes im RX Puffer zu ermitteln,
ohne Probleme beide Werte voneinander abziehen.
Es spielt nun auch keine Rolle mehr ob es indexe in das Array sind
oder direkt Zeiger auf die entsprechende Pufferpositionen.
rx_count = abs(inPtr-outPtr) // oder auch umgekehrt.
Ein Problem könnte aber immer noch auftauchen,
bei 8 Bit Controllern, wenn er Low und High Byte bei Zugriffen separat tätigt.
also:
inc(LowByte); wenn LowByte = 0 dann inc(HighByte)
Microchip PIC-Code:
incfz rx_count+0 // increment low byte of count, scip next Line if Zeroflag is set
incf rx_count+1 // else increment also the High Byte of count
Eine wasserdichte Software zu schreiben ist eine echte Herausforderung.....
Interrupts sperren mag ich eigentlich überhaupt nicht, ist aber manchmal unumgänglich.
-----
Zu deinem Konstrukt:
Ui, da muss ich erstmal durchblicken....ich analysiere aber noch.
Im Prinzip habe ich es, glaube ich zumindest, verstanden um was es geht.
Bei Dir bildet sich die case Schleife selbständig indem Du die obige Struktur erweiterst.
Etwas ähnliches (ist aber doch um einiges anders) habe ich in meinem Setupmenü gemacht.
/* every menu point has the following structure */
typedef struct
{
U16 menu_no; /* displayed menu no */
U8 flags; /* 3 flags for User, Service, Internal Setupmode */
S16 min; /* min value of parameter for range check */
S16 max; /* max value of parameter for range check */
U8 led_code[3]; /* 3 Digit LED Information Text, only 2 digits used */
U8 lcd_code[20]; /* 20 Digit LCD Information Text, not used at time */
void (*MenuFunc)(); /* call this function if menu point want to change */
void (*OnChangeFunc)(); /* maybe call a special function if param changed */
} TMenuPoint;
/* here is the complete initialized Setup Menu */
const TMenuPoint menu[]={
{ 1, /* Key Beep Volume Frontpanel Buttons */
F_USER + F_SERVICE + F_INTERNAL,
BEEP_MIN_VOL,BEEP_MAX_VOL,
{SEG_CODE_b,SEG_CODE_0,0}, /* displaycode "b0" */
"Key Beep ",
Setup_VolumeKeyBeep,
OnChangeBeeper
},
{ 2, /* Overpressure Beep Volume */
F_USER + F_SERVICE + F_INTERNAL,
BEEP_MIN_VOL,BEEP_MAX_VOL,
{SEG_CODE_b,SEG_CODE_1,0}, /* displaycode "b1" */
"Overpressure Beep ",
Setup_VolumeOverpressureBeep,
OnChangeBeeper
},
.......
Mein Code ist doch was ganz anderes, stelle ich grad fest, ist auch kaum statisch zu analysieren denke ich...
Ich habe mit Macros noch nicht viel gemacht in "C" hier kann man sich doch Einiges vereinfachen wie ich deinem Code entnehme.
Im PIC Assembler habe ich aber derart viele Macros geschrieben, das es später schon fast wie ein Hochsprache aussah.
Mit Code coverage usw. habe ich auch noch nichts zu tun gehabt. Machst Du solche Tests mit "Tessy" ? oder welche Tools benutzt Du dafür ?
Siro
Ich lese grade über "statische" Codeanalyse und da tauchte folgender Text auf:
Problematiken in "C"
Das wären unter anderem die undefinierte Auswertungsreihenfolge bei arithmetischen Ausdrücken,
compilerabhängige Ergebnisse beim Rechts-Shift von negativen ganzen Zahlen und die undefinierte Bitbreite des Datentyps int.
Nun bin ich doch etwas verunsichert:
Ich benutze ja sehr gerne mal das Shiften in alle Richtungen, zumindest links und rechts...:)
Das stellt ja ein "erhebliches" Risiko dar, wenn nicht eindeutig festgelegt ist, was der Compiler daraus macht.
Ist natürlich auch etwas problematisch mit dem shiften und abhängig vom Datentyp, wer mal Assembler programmiert hat
weis um die Problematik des Vorzeichenbits. Hier gibt es teilweise unterschiedliche Befehle zum Schieben,
die das Vorzeichen beachten, auffüllen, rotieren usw.
Kann man sich bei "C" darauf verlassen, dass ein Schieben mit "unsigned" immer richtig funktioniert ?
Einen signed, egal welchen Typs, würde ich eh nicht shiften wollen, da das Ergbenis ungewiss...
Durch die "Integer promotion" wird ja generell erstmal alles zum int gecastet und der hat ein Vorzeichen,
Aber selbst diese Aussage stimmt in "C" ja nicht weil:
Bei Bitdefinitionen ist es wiederum compiler spezifisch festgelegt bzw. einstellbar ob es ein signed oder unsigned ist.
Der C-Compiler von IAR Embedded Workbench z.B. interpretiert einen "int" bei Bitdefinitionen vorzeichenlos,
also wie ein "unsigned int" Möchte man eine Bitkombination mit Vorzeichen haben, muss man explizit einen "signed int" benutzen,
was ursprünglich ja der default int wäre(war, ist ?) oder wie auch immer...
Somit gehört in meinen Augen ein "int" als VERBOTEN, da dieser Typ nicht festgelegt ist.
Das habe ich auch schon seit Jahren in meiner Doku entsprechend spezifiziert und benutze ihn auch nicht.
Ein int könnte demnach Alles sein was mindestens 16 Bit hat. Mehr sagt es anscheinend nicht aus.
Na wie dem auch sei, je mehr ich mich mit der C Programmierung beschäftige, umso mehr erkenne ich wie schwierig es ist,
Fehler zu vermeiden. Eine "sinnvolle" statische Codeanalyse erscheint mir auch nicht wirklich sinnvoll, denn
ich müste dem Tool ja erstmal die ganzen "Eigenheiten" meines verwendeten Compilers verraten.
Ich glaube ich kann eure Gedanken lesen;)
Der Siro meckert nur immer....
Das ist aber nicht so, das sieht nur so aus...
ich beschäftige mich doch recht intensiv mit der Programmierung den Sprachen und deren Eigenheiten
um halt bessere Software zu schreiben, also versteht es bitte nicht falsch.
Ich schöpfe ja auch immer wieder viele neu Informationen von euch und das ist gut so.
Siro
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.