2012-10-07 55 views
1

爲了學習純C(來自C++),我決定用結構和宏編寫一個簡單的數學庫。C宏 - 通過指針傳遞與複製/錯誤傳遞到宏

到目前爲止,我有這個作爲測試宏:

#define MulVec2(dest,src) ((dest.x) = (dest.x) * (src.x); (dest.y) = (dest.y) * (src.y); return dest;) 

typedef struct vec2f_s 
{ 
    float x, y; 
} 
vec2f_t; 

在我的呼喚的代碼,我有這樣的:

int main(void) 
{ 

    vec2f_t v, w; 

    v.x = 5.0f; 
    v.y = 2.0f; 

    w.x = 3.0f; 
    w.y = 3.0f; 

    v = MulVec2(v, w); 

    printf("x => %f; y => %f \n", v.x, v.y); 

    return 0; 
} 

我的問題如下:

1)我是否需要編寫一個單獨的宏來將對象的指針/地址傳遞給宏?如果是這樣,怎麼樣?例如,請注意MulVec2(dest,src)宏假定傳入的對象不是動態分配的,但我也希望支持該對象。

2)當我編譯的代碼,我得到這個錯誤:

../main.c: In function 'main': 
../main.c:15:9: error: expected ')' before ';' token 
../main.c:15:7: error: incompatible types when assigning to type 'vec2f_t' from type 'float' 

我能做些什麼來解決這個問題?

編輯

我應該澄清,我不打算使用只是宏對於這一點,但是對於編寫宏的原因是,所以我不必寫一個單獨的函數矢量的雙重和浮點變體。我希望通過遵循DRY(不要重複自己)原則儘可能地重複使用它。

+0

你想在一個函數,而不是宏。 – chris

+0

你爲什麼在宏中這樣做? –

+0

函數肯定會讓事情變得更簡單。也就是說,由於C不支持重載,因此無法爲對象和指針創建相同的宏/函數。 – Mysticial

回答

2

正如其他人指出的,宏不是函數,這將是一個函數的用例。一個宏只是將Mul2Vec(...)文本地擴展爲您在宏中編寫的內容,這對編譯器沒有任何意義,因爲在表達式中不能有return

如果您想在C語言中實現C++模板的效果,請重新考慮您的策略,也許只需簡單地選擇floatdouble之一併堅持下去。但是,如果你絕對必須做仿製藥沒有重複代碼,你可以定義函數定義宏是這樣的:定義實際功能

/* vec-impl.h */ 

#define PASTE(a, b) a ## b 
#define NAME(prefix, type) PASTE(prefix, type) 

#define VEC_T NAME(vec_, T) 

typedef struct { 
    T x; 
    T y; 
} VEC_T; 

void NAME(MulVec2_, VEC_T)(VEC_T *dest, VEC_T *src) { 
dest->x = dest->x * src->x; 
dest->y = dest->y * src->y; 
} 

編制單位應該是這樣的:

/* vec-double.c */ 

#define T double 
#include "vec-impl.h" 

/* vec-float.c */ 

#define T float  
#include "vec-impl.h" 

這個設置給你一個窮人對C++模板的近似,沒有類型推理和任何花哨的元編程功能。再說一遍,除非你絕對必須這樣做,否則,儘量減少代碼的執行速度。這不是慣用的C代碼,並且不會被有能力的C程序員所接受 - 而C比C++更容易接受預處理器黑客攻擊,這遠遠超過了C++。

關於你發佈的代碼:爲了感受一下宏是什麼,獲得一本關於C的好書,解釋了這個話題,比如Kernighan和Ritchie的「The C Programming language」。在探索宏,請記住以下幾點:

  • 除非你真的知道自己在做什麼,從來沒有return了宏。無論如何,如果你這樣做,使宏名稱的RETURN部分。其他流量控制語句也是如此,例如break,continuegoto。在您之後維護該代碼的人將會感激。

  • 按照慣例,宏總是拼寫全部大寫,所以它是MUL_VEC2,而不是MulVec2。這提醒讀者他正在處理一個宏而不是一個函數。

  • 宏定義中的圓括號表示宏擴展即使傳遞了複合表達式也能正常工作。因此它是dest必須加括號,而不是dest.x。即將會寫入(dest).x而不是(dest.x)

  • 使用-E切換到編譯器向您顯示預處理器輸出,以便您可以看到編譯器看到的內容並找出發生了什麼。

+0

謝謝,先生/女士。我編輯了我的帖子以澄清一些事情。你還認爲應該使用一個函數嗎? – zeboidlund

+0

我現在已經更新了回覆以涵蓋澄清的問題。 – user4815162342

+0

哇,這是一個很棒的答案。謝謝。 – zeboidlund

0

第二個問題 - 刪除return desc;從宏,你不能分配這個宏給任何人

2

宏不是功能!

當您撥打MulVec2(v, w)時,它只是用其定義的值替換宏。

預處理的代碼會照顧這樣的:

int main(void) 
{ 

    vec2f_t v, w; 

    v.x = 5.0f; 
    v.y = 2.0f; 

    w.x = 3.0f; 
    w.y = 3.0f; 

v=MulVec2(dest,src) ((dest.x) = (dest.x) * (src.x); (dest.y) = (dest.y) * (src.y); 
    return dest;) 


    printf("x => %f; y => %f \n", v.x, v.y); 

    return 0; 
} 

這對編譯錯誤的原因。用一個函數簡單替換MulVec2的#define。

0

這是不好的設計。多次評估其參數的宏非常容易出錯,因爲作爲參數傳遞的表達式的副作用可能會炸燬整個代碼。使用inline函數幾乎與在C++中使用它們的函數幾乎相同。

作爲一般規則,不要將宏用於可以用函數完成的事情。

現在,如果你有這樣一個函數,創建另一個通過指針接收參數,命名爲MulVec2p,說。然後用C11的新功能,稱爲類型的通用宏,你可以做類似的東西發揮作用的第一個參數的類型重載在C++

#define MULVEC2(X, Y)    \ 
(_Generic((X),      \ 
    struct vec2f_s: MulVec2,   \ 
    struct vec2f_s*: MulVec2p)   \ 
    ((X), (Y))) 

(這個只是測試,但我希望你得到的圖片)

編譯器尚未完全支持C11,但例如clang已經有_Generic

+1

downvoter,請留下評論? –

1

如果你有C99編譯器,這個版本的宏會做你想要它做的事情:

#define DOT2(a, b) ((vec2f_t){(a).x * (b).x, (a).y * (b).y}) 

按照慣例,但如果他們評估的兩倍他們的論據尤其,如在這裏,宏必須有一個大寫的UGLY_NAME。我喜歡宏,但是我從痛苦的經歷中知道,如果你不遵循這個規則,那麼你就會一天r勞。所以我已經替換MulVec2DOT2;MULVEC2也可以。

同樣按照慣例,宏參數總是被括在圓括號內,例如(a).x。參看rue。此規則的例外情況向讀者表明您正在做一些不尋常的和/或錯誤的事情。但fabs((a))可能是不必要的。

有了這個宏,你甚至可以把地址(用gcc測試):

extern void foo(vec2f_t *); 
    // ... 
    foo(&DOT2(ying, yang));