2016-10-13 2 views
0

Ich habe das folgende Makrokonstrukt festgestellt, das das Skalarprodukt in der Headerdatei einer Implementierung eines mathematischen Algorithmus definiert.C Makro für Skalarprodukt mit variabler Dimension

#define DIM 3 
#define MULTIPLY(a, b)  ((a)*(b)) 
#define DOT_PRODUCT(a, b) ( MULTIPLY((a)[0],(b)[0]) + \ 
           MULTIPLY((a)[1],(b)[1]) + \ 
           MULTIPLY((a)[2],(b)[2])) 

Es wird vermutlich verwendet, um die Implementierung unabhängig von der Dimension zu halten. Aber natürlich, wenn man DIM ändert, muss man auch DOT_PRODUCT ändern, um das zu reflektieren.

Gibt es eine Möglichkeit, DOT_PRODUCT so zu definieren, dass das eingestellte Maß DIM automatisch eingehalten wird? Vielleicht mit einer (pseudo) rekursiven Makrozauberei? Ich habe versucht, mir etwas einfallen zu lassen, aber mein C-foo ist nicht besonders stark.

+0

Ich würde nicht denken, aber Sie könnten bedingte Logik verwenden, um alle Fälle für 'DIM' in einem kleinen Bereich zu behandeln. –

+1

Verwenden Sie kein Makro, wo eine Funktion auch funktioniert. – Olaf

+0

Sie könnten es mit [Boost :: Preprocessor] (http://www.boost.org/doc/libs/release/libs/preprocessor/) tun - sogar in C; dieser Code ist für den C- oder C++ - Präprozessor. Sie sollten es nicht mit Boost :: Preprocessor tun. –

Antwort

1

Ich stimme @Olaf zu: Verwenden Sie keine Makros, wo Funktionen verwendet werden können. Aber manchmal Umstände oder Wünsche überschreiben diesen Gedanken.

Der Präprozessor wünscht manchmal nur die richtige Anzahl von Umleitungen an der richtigen Stelle, also experimentierte ich, bis ich das bekam. Ich gebe zu, ich konnte nicht vorhersagen und ich kann nicht im Detail erklären, zumindest nicht die interessanten, nicht offensichtlichen Teile.

#define DIM 2 
#define CONCAT(x,y,a,b) x ## y(a,b) 
#define DOT_PRODUCT_DIM(y,a,b) CONCAT(DOT_PRODUCT_,y,a,b) 
#define MULTIPLY(a, b)  ((a)*(b)) 
#define DOT_PRODUCT_2(a, b) ( MULTIPLY((a)[0],(b)[0]) + \ 
           MULTIPLY((a)[1],(b)[1])) 
#define DOT_PRODUCT_3(a, b) ( MULTIPLY((a)[0],(b)[0]) + \ 
           MULTIPLY((a)[1],(b)[1]) + \ 
           MULTIPLY((a)[2],(b)[2])) 
#define DOT_PRODUCT_4(a, b) ( MULTIPLY((a)[0],(b)[0]) + \ 
           MULTIPLY((a)[1],(b)[1]) + \ 
           MULTIPLY((a)[2],(b)[2]) + \ 
           MULTIPLY((a)[3],(b)[3])) 
#define DOT_PRODUCT(a,b) DOT_PRODUCT_DIM(DIM,a,b) 

DOT_PRODUCT_DIM(2,a,b) 
DOT_PRODUCT_DIM(3,a,b) 
DOT_PRODUCT_DIM(4,a,b) 
DOT_PRODUCT_DIM(DIM,a,b) 
DOT_PRODUCT(a,b) 

OUTPUT (gcc -E Toy.c):

((((a)[0])*((b)[0])) + (((a)[1])*((b)[1]))) 
((((a)[0])*((b)[0])) + (((a)[1])*((b)[1])) + (((a)[2])*((b)[2]))) 
((((a)[0])*((b)[0])) + (((a)[1])*((b)[1])) + (((a)[2])*((b)[2])) + (((a)[3])*((b)[3]))) 
((((a)[0])*((b)[0])) + (((a)[1])*((b)[1]))) 
((((a)[0])*((b)[0])) + (((a)[1])*((b)[1]))) 

OUTPUT (nach nur #define DIM 3 Ändern zitieren nur die letzten beiden Zeilen der Ausgabe):

((((a)[0])*((b)[0])) + (((a)[1])*((b)[1])) + (((a)[2])*((b)[2]))) 
((((a)[0])*((b)[0])) + (((a)[1])*((b)[1])) + (((a)[2])*((b)[2]))) 

OUTPUT (nach nur #define DIM 4 ändern, Zitieren nur die letzten zwei Zeilen der Ausgabe):

((((a)[0])*((b)[0])) + (((a)[1])*((b)[1])) + (((a)[2])*((b)[2])) + (((a)[3])*((b)[3]))) 
((((a)[0])*((b)[0])) + (((a)[1])*((b)[1])) + (((a)[2])*((b)[2])) + (((a)[3])*((b)[3]))) 

OUTPUT (nach nur #define DIM 5 Ändern zitieren nur die letzten beiden Zeilen der Ausgabe):

DOT_PRODUCT_5(a,b) 
DOT_PRODUCT_5(a,b) 

Selbstverständlich können die Zwischen DOT_PRODUCT_number() Makros definiert werden müssen. Die von mir bereitgestellten Beispiele funktionieren, es gibt also kein systematisches Problem, das höhere Dimensionen verhindern würde.
Ich hoffe, dass der Use Case entspricht. Du denkst nicht an unbestimmte, statisch unbekannte/unvorhersagbare hohe Anzahl von Dimensionen, oder?

+0

Interessante Lösung, dies würde meinen Anwendungsfall abdecken. Ich brauche nur eine Variante, die es mir erlaubt, 2D und 3D gleich zu behandeln, indem ich DIM ändere. Eine ideale Lösung wäre natürlich völlig flexibel, aber das ist trotzdem hilfreich, danke. Nur eine Frage könnte etwas Ähnliches nicht durch ein IF-ELSEIF-ELSE-Konstrukt erreichen? – PeterE

+0

Ich dachte, ich adressiert genau die Notwendigkeit, nur DIM zu wechseln. Was ist damit für dich nicht? Meine drei Beispiele zeigen, dass nur das Umschalten von DIM durch 2,3,4 (, 5) entsprechende Ergebnisse liefert. Ein IF-ELSEIF-Konstrukt wäre ebenfalls möglich. Aber ich wurde (unbeabsichtigt und unwissentlich) von @John Coleman herausgefordert. Sein Kommentar klang so, als ob er es für den einzigen Weg hielt. Außerdem bietet meine Lösung (zusätzlich zu der DIM-gesteuerten Voreinstellung) Zugriff auf die explizit dimensionierten 'DOT_PRODUCT_digit()' Versionen. ZB, wenn Sie hauptsächlich 3dim tun, aber ein paar Fälle von 4dim haben. – Yunnosch

+0

Ich glaube, du hast meinen Kommentar missverstanden, deine Lösung funktioniert ** für mich, da ich nur DIM = 2 oder DIM = 3 brauche. Aber in diesem Fall wäre IF-ELSE wahrscheinlich einfacher. Anfangs war ich auf der Suche nach der perfekten Lösung für ein beliebiges DIM, aber das ist mehr als das, was ich * wirklich * brauche, um mein Problem zu lösen. – PeterE