Ungewöhnliche Verwendung der .h-Datei in C.

77

Beim Lesen des Artikels über das Filtern habe ich eine seltsame Verwendung von .hDateien festgestellt - verwenden Sie diese zum Füllen von Koeffizientenarrays:

#define N 100 // filter order
float h[N] = { #include "f1.h" }; //insert coefficients of filter
float x[N];
float y[N];

short my_FIR(short sample_data)
{
  float result = 0;

  for ( int i = N - 2 ; i >= 0 ; i-- )
  {
    x[i + 1] = x[i];
    y[i + 1] = y[i];
  }

  x[0] = (float)sample_data;

  for (int k = 0; k < N; k++)
  {
    result = result + x[k]*h[k];
  }
  y[0] = result;

  return ((short)result);
}

Ist es also üblich, float h[N] = { #include "f1.h" };diesen Weg zu verwenden?

artsin
quelle
25
Es ist ungewöhnlich. Das heißt, es wird selten verwendet: f1.hWird möglicherweise von einem externen Tool generiert und als Eingabe für Ihr (kompiliertes) Programm verwendet. Es ist jedoch üblicher, dass die externen Tools eine vollständige Header-Datei generieren, z . B. float h[N] = { ... }im Inneren f1.h. Beispiel hier .
ta.speot.is
8
Es sollte nicht kompiliert werden, siehe die Syntax in C11 6.10.2. # include "q-char-sequence" new-line. Wo ist die neue Linie?
Lundin
2
Also ... wie schaffen es all diese Leute, die sagen, dass dies gültiger Code ist, ihn zum Kompilieren zu bringen? Ich kann mit gcc nicht umgehen, ich bekomme error: "stray #"unabhängig davon, welchen Standard oder welche Optionen ich übergebe.
Lundin
2
@Lundin: Einige Compiler erlauben überall Präprozessor-Bits, und im Rest können Sie einfach Zeilenumbrüche einfügen. Bricht dieses spezielle Beispiel nicht.
Mooing Duck
2
@ user657267: Es ist gängige Praxis , selten Fälle. Ich hatte ungefähr drei oder vier Gelegenheiten in meiner Karriere, als dies meinen Tag rettete. Wie Jason R schrieb, werden Koeffizienten dankenswerterweise auf diese Weise einbezogen (ohne den Syntaxfehler im Beispiel). Oder generell importierte Daten, die Sie in Ihrer Anwendung haben möchten. Was beim Code zählt, ist die Lesbarkeit und exotische Parser oder Grammatiken, um jedes Mal, wenn sich Ihre Daten ändern, eine ordentliche C- oder C ++ - Datei zu erstellen, die weitaus weniger lesbar ist.
Sebastian Mach

Antworten:

132

Präprozessor- Direktiven wie #includeführen nur eine Textersetzung durch (siehe die Dokumentation von GNU cpp in GCC ). Es kann an jeder Stelle auftreten (außerhalb von Kommentaren und String-Literalen).

A #includesollte jedoch #das erste nicht leere Zeichen seiner Zeile haben. Sie werden also codieren

float h[N] = {
  #include "f1.h"
};

Die ursprüngliche Frage hatte keine #includeeigene Zeile, hatte also falschen Code.

Es ist keine normale Praxis, aber es ist erlaubt zu üben. In diesem Fall würde ich vorschlagen, eine andere Erweiterung als .hz. B. use #include "f1.def"oder #include "f1.data"...

Bitten Sie Ihren Compiler, Ihnen das vorverarbeitete Formular anzuzeigen. Mit GCC kompilieren Sie mit gcc -C -E -Wall yoursource.c > yoursource.iund schauen Sie mit einem Editor oder einem Pager in die generierteyoursource.i

Eigentlich bevorzuge ich solche Daten in einer eigenen Quelldatei. Daher würde ich stattdessen vorschlagen, eine in sich geschlossene h-data.cDatei mit einem Tool wie GNU awk zu generieren (die Datei h-data.cbeginnt also const float h[345] = {mit };... und endet mit ...). Wenn es sich um konstante Daten handelt, deklarieren Sie const float h[]sie besser (damit sie gelesen werden können) -nur Segment wie .rodataunter Linux). Wenn die eingebetteten Daten groß sind, kann der Compiler einige Zeit benötigen, um sie (nutzlos) zu optimieren (dann können Sie Ihre Daten h-data.cschnell und ohne Optimierungen kompilieren ).

Basile Starynkevitch
quelle
3
Warum die Ausgabe umleiten und nicht nur gcc -C -E -o Ihre Quelle.i -Wand Ihre Quelle.c verwenden? Diese Option gibt es aus einem Grund!
Dave
26
Weil >ein Charakter kürzer ist als -ound weil ich lahm bin.
Basile Starynkevitch
10
Eigentlich war mein erstes Unix auf Sun3, SunOS3.2 ... (also 1987)
Basile Starynkevitch
10

Ist es also üblich, float h [N] = {#include “f1.h”} zu verwenden? diesen Weg?

Es ist nicht normal, aber gültig (wird vom Compiler akzeptiert).

Vorteile dieser Verwendung: Sie sparen den geringen Aufwand, der erforderlich wäre, um eine bessere Lösung zu finden.

Nachteile:

  • Es erhöht das WTF / SLOC-Verhältnis Ihres Codes.
  • Es führt eine ungewöhnliche Syntax ein, sowohl im Client-Code als auch im enthaltenen Code.
  • Um zu verstehen, was f1.h tut, müssten Sie sich ansehen, wie es verwendet wird (dies bedeutet, dass Sie Ihrem Projekt zusätzliche Dokumente hinzufügen müssen, um dieses Biest zu erklären, oder dass die Leute den Code lesen müssen, um zu sehen, was es ist bedeutet - keine Lösung ist akzeptabel)

Dies ist einer der Fälle, in denen zusätzliche 20 Minuten Nachdenken vor dem Schreiben des Codes Ihnen einige zehn Stunden Zeit verfluchen können, Code und Entwickler während der Laufzeit des Projekts zu verfluchen.

utnapistim
quelle
4
Fehlende Zeilenumbrüche #includesollten sich in einer separaten Zeile befinden.
Basile Starynkevitch
Wie Starynkevitch bemerkt, wird dieser C-Code mit der Include-Direktive kompiliert, die von Zeilenumbrüchen umgeben ist. Siehe ISO / IEC 9899 (C11) §6.10 2 "Eine Vorverarbeitungsrichtlinie besteht aus einer Folge von Vorverarbeitungstoken, die mit einem # Vorverarbeitungstoken" ... "beginnt oder auf Leerzeichen mit mindestens einem Zeilenumbruchzeichen folgt endet mit dem nächsten Zeilenumbruch. " "...". Was f1.h bietet, geht aus dem Kontext hervor. Es wäre eine durch Kommas getrennte Liste von Ausdrücken, die mit float, N-Größe, kompatibel sind. Ich würde davon ausgehen, dass diese Koeffizienten sowieso aus einem separaten Standard oder Dokument stammen.
user1155120
1
Was ist "normal"? Ich halte es für eine gute Praxis, wenn importierte Daten besser lesbar sind als exotische und komplexe Trigger, die wiederum saubere Quelldateien erstellen. Die Syntax ist ungewöhnlich, aber für den erfahrenen C- oder C ++ - Programmierer, der weiß, was der Präprozessor tut, sollte es eine Überraschung sein, aber keine große WTF.
Sebastian Mach
@ BasileStarynkevitch, ich weiß, es sollte in einer separaten Zeile sein. Ich war mehr daran interessiert, die von OP gestellte Frage zu beantworten als den Code zu korrigieren.
Utnapistim
@phresnel, in meiner Berufserfahrung (mehr als 8 Jahre C ++) habe ich dies nur einmal im Produktionscode gesehen, und dann erklärte Stephan Lavavej, wie sie std :: function-Spezialisierungen mit variabler Anzahl von Parametern durch Einbeziehen erzeugten die Parameter in Boilerplate-Deklarationen (ich denke, es war vor C ++ 11). Deshalb halte ich es für atypisch ("nicht normal"). Ordentliche Quelldateien und eine gute Lesbarkeit für importierte Daten schließen sich nicht gegenseitig aus (diesbezüglich ist dies eine falsche Wahl).
Utnapistim
10

Wie bereits in früheren Antworten erläutert, ist dies keine normale Praxis, aber eine gültige.

Hier ist eine alternative Lösung:

Datei f1.h:

#ifndef F1_H
#define F1_H

#define F1_ARRAY                   \
{                                  \
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \
    10,11,12,13,14,15,16,17,18,19, \
    20,21,22,23,24,25,26,27,28,29, \
    30,31,32,33,34,35,36,37,38,39, \
    40,41,42,43,44,45,46,47,48,49, \
    50,51,52,53,54,55,56,57,58,59, \
    60,61,62,63,64,65,66,67,68,69, \
    70,71,72,73,74,75,76,77,78,79, \
    80,81,82,83,84,85,86,87,88,89, \
    90,91,92,93,94,95,96,97,98,99  \
}

// Values above used as an example

#endif

Datei f1.c:

#include "f1.h"

float h[] = F1_ARRAY;

#define N (sizeof(h)/sizeof(*h))

...
Barak Manos
quelle
7
Und dieser Ansatz "verbrennt" die Symbole F1_ARRAYund F1_H. Wenn Sie eine generierte Datei haben, kann es hilfreich sein, die Verwendung solcher Präprozessorsymbole zu vermeiden. Für vom Menschen erstellte Header-Dateien ist Ihre Lösung besser, nicht jedoch für eine Reihe generierter Datendateien.
Harper
4
Ich verstehe nicht, warum dies besser ist als die bestehende Lösung. Jetzt ist es schwieriger, diese Datendatei automatisch zu generieren, und sie verschmutzt den Symbolraum. Wie Harper bereits erwähnt, ist dies für vom Menschen generierte Daten vielleicht besser, aber es ist immer noch schlimmer, als die Menschen sie in der realen Quelldatei generieren zu lassen. Wenn es zu komplex ist, ist die menschliche Generation wahrscheinlich sowieso falsch.
Sebastian Mach
@phresnel: Warum ist es schwieriger, diese Datendatei automatisch zu generieren? Es enthält lediglich 6 weitere Zeilen mit konstantem Text (4 Präprozessoranweisungen und 2 geschweifte Klammern).
Barak Manos
@harper: Danke für deinen Kommentar. Da für eine Reihe von generierten Datendateien jede Datei einen eindeutigen Namen hat, können Sie problemlos einige eindeutige Präprozessorsymbole pro Datei erstellen. Das Generieren dieser konstanten Textzeilen per Skript sollte auch nicht zu schwierig sein.
Barak Manos
@ Barakmanos: Genau; Jetzt müssen Sie diese zusätzlichen Zeilen irgendwie drucken. Sie müssen Ihr vorhandenes Python- oder Haskell-Programm anpassen, um nicht nur durch Kommas getrennte Werte, sondern auch C-Code auszugeben. Wenn Ihre Koeffizienten von einem proprietären Anbieter stammen, z. B. von einem Labor oder von einer Berechnung des Lichtreflexionsvermögens, müssen Sie jetzt zusätzliche catet al. Aufrufen und sicherstellen, dass Ihre catund Skripte tatsächlich ausgeführt werden.
Sebastian Mach
9

Nein, das ist keine normale Praxis.

Es ist wenig bis gar kein Vorteil, ein solches Format direkt zu verwenden , stattdessen könnten die Daten in einer separaten Quelldatei generiert werden, oder in diesem Fall könnte zumindest eine vollständige Definition gebildet werden.


Es gibt jedoch ein "Muster", bei dem eine Datei an solchen zufälligen Stellen eingefügt wird : X-Makros wie diese .

Die Verwendung von X-Makro besteht darin, eine Sammlung einmal zu definieren und an verschiedenen Stellen zu verwenden. Die einheitliche Definition gewährleistet die Kohärenz des Ganzen. Betrachten Sie als triviales Beispiel:

// def.inc
MYPROJECT_DEF_MACRO(Error,   Red,    0xff0000)
MYPROJECT_DEF_MACRO(Warning, Orange, 0xffa500)
MYPROJECT_DEF_MACRO(Correct, Green,  0x7fff00)

die jetzt auf verschiedene Arten verwendet werden kann:

// MessageCategory.hpp
#ifndef MYPROJECT_MESSAGE_CATEGORY_HPP_INCLUDED
#define MYPROJECT_MESSAGE_CATEGORY_HPP_INCLUDED

namespace myproject {

    enum class MessageCategory {
#   define MYPROJECT_DEF_MACRO(Name_, dummy0_, dummy1_) Name_,
#   include "def.inc"
#   undef MYPROJECT_DEF_MACRO
    NumberOfMessageCategories
    }; // enum class MessageCategory

    enum class MessageColor {
#   define MYPROJECT_DEF_MACRO(dumm0_, Color_, dummy1_) Color_,
#   include "def.inc"
#   undef MYPROJECT_DEF_MACRO
    NumberOfMessageColors
    }; // enum class MessageColor

    MessageColor getAssociatedColorName(MessageCategory category);

    RGBColor getAssociatedColorCode(MessageCategory category);

} // namespace myproject

#endif // MYPROJECT_MESSAGE_CATEGORY_HPP_INCLUDED
Matthieu M.
quelle
7

Vor langer Zeit wurde der Präprozessor überbeansprucht. Siehe zum Beispiel das XPM-Dateiformat, das so konzipiert wurde, dass Benutzer:

#include "myimage.xpm"

in ihrem C-Code.

Es wird nicht mehr als gut angesehen.

Der OP-Code sieht Cso aus, also werde ich darüber sprechenC

Warum wird der Präprozessor überbeansprucht?

Die Präprozessor- #includeDirektive soll Quellcode enthalten. In diesem Fall und im Fall des OP handelt es sich nicht um echten Quellcode, sondern um Daten .

Warum wird es als schlecht angesehen?

Weil es sehr unflexibel ist . Sie können das Image nicht ändern, ohne die gesamte Anwendung neu zu kompilieren. Sie können nicht einmal zwei Bilder mit demselben Namen einfügen, da dadurch nicht kompilierbarer Code erzeugt wird. Im Fall des OP kann er die Daten nicht ändern, ohne die Anwendung neu zu kompilieren.

Ein weiteres Problem besteht darin, dass eine enge Kopplung zwischen den Daten und dem Quellcode hergestellt wird. Beispielsweise muss die Datendatei mindestens die Anzahl der Werte enthalten, die durch das Nin der Quellcodedatei definierte Makro angegeben werden .

Durch die enge Kopplung wird Ihren Daten auch ein Format zugewiesen. Wenn Sie beispielsweise 10x10-Matrixwerte speichern möchten, können Sie entweder ein eindimensionales Array oder ein zweidimensionales Array in Ihrem Quellcode verwenden. Wenn Sie von einem Format zum anderen wechseln, ändert sich Ihre Datendatei.

Dieses Problem des Ladevorganges wird leicht gelöst durch die Verwendung der Standard - I / O - Funktionen. Wenn Sie wirklich einige Standards Bilder müssen enthalten können Sie einen Standard geben Pfade zu den Bildern in Ihrem Quellcode. Auf diese Weise kann der Benutzer diesen Wert zumindest ändern (über eine #defineoder -D-Option beim Kompilieren) oder die Bilddatei aktualisieren, ohne sie erneut kompilieren zu müssen.

Im Fall des OP wäre sein Code wiederverwendbarer, wenn die FIR-Koeffizienten und -Vektoren x, yals Argumente übergeben würden. Sie können ein erstellen struct, um diese Werte zusammen zu halten. Der Code wäre nicht ineffizient und würde auch mit anderen Koeffizienten wiederverwendbar werden . Die Koeffizienten können beim Start aus einer Standarddatei geladen werden, es sei denn, der Benutzer übergibt einen Befehlszeilenparameter, der den Dateipfad überschreibt. Dies würde die Notwendigkeit globaler Variablen beseitigen und die Absichten des Programmierers explizit machen. Sie können sogar dieselbe FIR-Funktion in zwei Threads verwenden, vorausgesetzt, jeder Thread hat seine eigene struct.

Wann ist es akzeptabel?

Wenn Sie keine dynamischen Daten laden können. In diesem Fall müssen Sie Ihre Daten statisch laden und müssen solche Techniken anwenden.

Wir sollten beachten, dass kein Zugriff auf Dateien bedeutet, dass Sie für eine sehr eingeschränkte Plattform programmieren und daher Kompromisse eingehen müssen. Dies ist der Fall, wenn Ihr Code beispielsweise auf einem Mikrocontroller ausgeführt wird.

Aber selbst in diesem Fall würde ich es vorziehen, eine echte CQuelldatei zu erstellen, anstatt Gleitkommawerte aus einer halbformatierten Datei aufzunehmen.

Zum Beispiel die Bereitstellung einer realen CFunktion, die die Koeffizienten zurückgibt, anstatt eine halbformatierte Datendatei zu haben. Diese CFunktion kann dann in zwei verschiedenen Dateien definiert werden, von denen eine E / A für Entwicklungszwecke verwendet und eine andere statische Daten für den Release-Build zurückgibt. Sie würden die korrekte Bedingung für die Quelldatei kompilieren.

Fjardon
quelle
@YvesDaoust Zum einen ist es eine riesige Sicherheitslücke. Da XPMs Bilddateien sind, werden sie häufig aus anderen Quellen bezogen, anstatt sie selbst zu erstellen. Ein böswilliger Benutzer kann die XPM-Datei leicht ändern, um die Zeichenfolge zu beenden, und so leicht Code einfügen.
trlkly
@trlkly: Und wie erhält der böswillige Benutzer Zugriff auf die Versionskontrollsysteme für Quellcode und Assets sowie auf die Produktions-Build-Maschine? Zum einen kennen Sie C oder C ++ nicht gut.
Sebastian Mach
"Die Präprozessor-Direktive #include soll Quellcode enthalten": Der Präprozessor kümmert sich nicht darum, er erstellt eine vollständige Datei aus Blöcken, und der Compiler weiß es nicht. (Es kann einen Unterschied für vorkompilierte Header machen, aber diese gehören nicht zum Standard.) Ich verwende sie, wenn es sauberer ist.
Yves Daoust
@YvesDaoust Sie können den C-Präprozessor sicherlich verwenden, um die gewünschte Makrosubstitution für jeden gewünschten Dateityp durchzuführen. Aber meine Antwort war im Zusammenhang mit dieser Frage. Der C - Präprozessor ist benannt C aus einem Grund Präprozessor: es soll Vorprozess C - Dateien. Und C-Dateien sollen am Ende Quellcode sein. Ich denke also, es ist richtig zu sagen, dass: eine Direktive, die Dateien enthalten soll, die von einem Präprozessor verwendet werden sollen, selbst Quellcode vorverarbeiten soll, eine Direktive ist, die Quellcode enthalten soll. :)
Fjardon
@phresnel Worüber sprichst du? Sie haben direkten Zugriff auf den Quellcode, da die nicht autorisierte XPM-Datei gerade eine #includeDatei war. Das ist das Szenario, das in der Frage diskutiert wird. Der XPM hat Zugriff auf die Build-Umgebung, da dort die Datei enthalten ist. Zugegeben, wenn Sie überhaupt schlau sind, überprüfen Sie das XPM, um sicherzustellen, dass es gültig ist, bevor #includeSie es verwenden, aber es ist immer noch eine große Sicherheitslücke.
trlkly
5

Es gibt manchmal Situationen, in denen entweder externe Tools zum Generieren von .C-Dateien basierend auf anderen Dateien, die Quellcode enthalten, verwendet werden müssen, externe Tools C-Dateien mit einer übermäßigen Menge an Code generieren müssen, die fest mit den generierenden Tools verbunden sind, oder Code verwendet werden muss #includeRichtlinie auf verschiedene "ungewöhnliche" Arten. Von diesen Ansätzen würde ich vorschlagen, dass letzteres - obwohl es eklig ist - oft das am wenigsten böse ist.

Ich würde vorschlagen, die Verwendung des .hSuffixes für Dateien zu vermeiden, die nicht den normalen Konventionen für Header-Dateien entsprechen (z. B. durch Einbeziehen von Methodendefinitionen, Zuweisen von Speicherplatz, Erfordernis eines ungewöhnlichen Einschlusskontexts (z. B. in der Mitte einer Methode) und Erfordernis mehrerer Aufnahme in verschiedene definierte Makros usw. Ich vermeide im Allgemeinen auch die Verwendung von .coder .cppfür Dateien, die über andere Dateien in andere Dateien integriert werden, es #includesei denn, diese Dateien werden hauptsächlich eigenständig verwendet. [In einigen Fällen kann z. B. eine Datei fooDebug.cmit #define SPECIAL_FOO_DEBUG_VERSION[newline] `#include" foo vorhanden sein. c "` `wenn ich möchte, dass zwei Objektdateien mit unterschiedlichen Namen aus derselben Quelle generiert werden und eine davon die" normale "Version ist.]

Meine normale Praxis besteht darin, .ials Suffix entweder von Menschen oder Maschinen erzeugte Dateien zu verwenden, die so konzipiert sind, dass sie auf übliche Weise aus anderen C- oder C ++ - Quelldateien aufgenommen werden. Wenn Dateien maschinell generiert werden, wird das Generierungswerkzeug in der Regel in der ersten Zeile einen Kommentar enthalten, der das zum Erstellen verwendete Werkzeug angibt.

Übrigens, ein Trick, bei dem ich dies verwendet habe, war, als ich zulassen wollte, dass ein Programm nur mit einer Batch-Datei ohne Tools von Drittanbietern erstellt wird, aber zählen wollte, wie oft es erstellt wurde. In meine Batch-Datei habe ich aufgenommen echo +1 >> vercount.i; dann in der Datei vercount.c, wenn ich mich richtig erinnere:

const int build_count = 0
#include "vercount.i"
;

Der Nettoeffekt ist, dass ich einen Wert erhalte, der bei jedem Build erhöht wird, ohne dass ich mich bei der Erstellung auf Tools von Drittanbietern verlassen muss.

Superkatze
quelle
3

Wenn der Präprozessor die #includeDirektive findet, öffnet er einfach die angegebene Datei und fügt den Inhalt ein, als wäre der Inhalt der Datei am Speicherort der Direktive geschrieben worden.

Laura Maftei
quelle
1
Vergessen Sie nicht, dass die enthaltene Datei vorverarbeitet und dann auch kompiliert wird.
Sebastian Mach
3

Wie bereits in den Kommentaren erwähnt, ist dies keine normale Praxis. Wenn ich solchen Code sehe, versuche ich ihn umzugestalten.

Zum Beispiel f1.hkönnte so aussehen

#ifndef _f1_h_
#define _f1_h_

#ifdef N
float h[N] = {
    // content ...
}

#endif // N

#endif // _f1_h_

Und die .c-Datei:

#define N 100 // filter order
#include “f1.h”

float x[N];
float y[N];
// ...

Dies scheint mir etwas normaler zu sein - obwohl der obige Code noch weiter verbessert werden könnte (z. B. ohne Globals).

TobiMcNamobi
quelle
3

Hinzu kommt, was alle anderen gesagt haben - der Inhalt von f1.hmuss so sein:

20.0f, 40.2f,
100f, 12.40f
-122,
0

Weil der Text in f1.h in das betreffende Array initialisieren wird!

Ja, es kann Kommentare, andere Funktionen oder Makroverwendungen, Ausdrücke usw. enthalten.

Ajay
quelle
3

Für mich ist das normal.

Mit dem Präprozessor können Sie eine Quelldatei in beliebig viele Blöcke aufteilen, die von # include-Direktiven zusammengestellt werden.

Es ist sehr sinnvoll, wenn Sie den Code nicht mit langen / nicht zu lesenden Abschnitten wie Dateninitialisierungen überladen möchten. Wie sich herausstellt, ist meine Datensatzdatei "Array-Initialisierung" 11000 Zeilen lang.

Ich verwende sie auch, wenn einige Teile des Codes automatisch von einem externen Tool generiert werden: Es ist sehr praktisch, wenn das Tool nur seine Chunks generiert und sie in den Rest des von Hand geschriebenen Codes einfügt.

Ich habe einige solcher Einschlüsse für einige Funktionen, die je nach Prozessor mehrere alternative Implementierungen haben, von denen einige Inline-Assembly verwenden. Die Einschlüsse machen den Code übersichtlicher.

Traditionell wurde die Direktive #include verwendet, um Header-Dateien einzuschließen, dh Sätze von Deklarationen, die eine API verfügbar machen. Aber nichts verlangt das.

Yves Daoust
quelle
2

Ich habe gelesen, dass Leute umgestalten wollen und sagen, dass dies böse ist. Trotzdem habe ich in einigen Fällen verwendet. Wie einige Personen sagten, handelt es sich um eine Präprozessor-Richtlinie, die auch den Inhalt der Datei enthält. Hier ist ein Fall, in dem ich Zufallszahlen erstellt habe. Ich baue Zufallszahlen und möchte dies nicht jedes Mal tun, wenn ich weder zur Laufzeit kompiliere. Ein anderes Programm (normalerweise ein Skript) füllt einfach eine Datei mit den generierten Zahlen, die enthalten sind. Dies vermeidet das Kopieren von Hand, dies ermöglicht ein einfaches Ändern der Zahlen, des Algorithmus, der sie erzeugt, und anderer Feinheiten. Sie können die Praxis nicht einfach beschuldigen, in diesem Fall ist es einfach der richtige Weg.

Juan Chô
quelle
1
Warum erstellen Sie die Zufallszahlen nicht zur Kompilierungszeit? Dieser Anwendungsfall scheint mir nicht zu gültig zu sein.
Sebastian Mach
2

Ich habe die Technik des OP verwendet, eine Include-Datei für den Dateninitialisierungsteil einer Variablendeklaration für einige Zeit zu platzieren. Genau wie beim OP wurde die enthaltene Datei generiert.

Ich habe die generierten .h-Dateien in einem separaten Ordner isoliert, damit sie leicht identifiziert werden können:

#include "gensrc/myfile.h"

Dieses Schema fiel auseinander, als ich anfing, Eclipse zu verwenden. Die Überprüfung der Eclipse-Syntax war nicht ausgefeilt genug, um dies zu handhaben. Es würde reagieren, indem es Syntaxfehler meldet, bei denen es keine gibt.

Ich habe Beispiele an die Eclipse-Mailingliste gemeldet, aber es schien kein großes Interesse daran zu bestehen, die Syntaxprüfung zu "korrigieren".

Ich habe meinen Codegenerator geändert, um zusätzliche Argumente zu verwenden, damit die gesamte Variablendeklaration generiert werden kann, nicht nur die Daten. Jetzt werden syntaktisch korrekte Include-Dateien generiert.

Auch wenn ich Eclipse nicht verwendet habe, denke ich, dass es eine bessere Lösung ist.

Seien Sie freundlich zu neuen Benutzern
quelle
1

Im Linux-Kernel habe ich ein Beispiel gefunden, IMO, schön. Wenn Sie sich die Header-Datei cgroup.h ansehen

http://lxr.free-electrons.com/source/include/linux/cgroup.h

Sie finden die Anweisung, #include <linux/cgroup_subsys.h>die zweimal verwendet wird, nach unterschiedlichen Definitionen des MakrosSUBSYS(_x) . Dieses Makro wird in cgroup_subsys.h verwendet, um mehrere Namen von Linux-cgroups zu deklarieren (wenn Sie mit cgroups nicht vertraut sind, handelt es sich um benutzerfreundliche Schnittstellen, die Linux anbietet und die beim Systemstart initialisiert werden müssen).

Im Code-Snippet

#define SUBSYS(_x) _x ## _cgrp_id,
enum cgroup_subsys_id {
#include <linux/cgroup_subsys.h>
   CGROUP_SUBSYS_COUNT,
};
#undef SUBSYS

Jedes SUBSYS(_x)in cgroup_subsys.h deklarierte Element wird zu einem Element des Typs enum cgroup_subsys_id, während es sich im Code-Snippet befindet

#define SUBSYS(_x) extern struct cgroup_subsys _x ## _cgrp_subsys;
#include <linux/cgroup_subsys.h>
#undef SUBSYS

Jedes SUBSYS(_x)wird zur Deklaration einer Variablen vom Typ struct cgroup_subsys.

Auf diese Weise können Kernel-Programmierer cgroups hinzufügen, indem sie nur cgroup_subsys.h ändern, während der Vorprozessor automatisch die zugehörigen Aufzählungswerte / Deklarationen in die Initialisierungsdateien hinzufügt.

user1466329
quelle
+1 Ich stimme zu. Dies hängt mit X-Makros zusammen . Ich betrachte es als eine Möglichkeit, den Präprozessor so zu programmieren, dass er einen Teil meines Codes schreibt und den Code wartbar macht. Es gibt Leute, die nicht zustimmen.
Mike Dunlavey