2013-09-01 13 views
0

我有一個庫函數,它只需要一個level參數,並使用它來索引一個參數值數組,並將其從函數的使用者中抽象出來。請注意,參數表,甚至其結構類型僅對其包含的C文件可見。如何根據表的大小導出常量

library.h

#define MIN_LEVEL 0 
#define MAX_LEVEL ((sizeof(m_param_table)/sizeof(m_param_table[0]))-1) 

BOOL set_param_level(int level); 

LIBRARY.C

#include "library.h" 

typedef struct { 
    int param1; 
    int param2; 
} params_t; 

// Parameters table - static (local) to this C file 
static params_t m_param_table[] = { 
    {0, 1}, 
    {2, 3}, 
}; 

BOOL set_param_level(int level) { 
    int p1, p2; 

    // bounds checking on MIN_LEVEL and MAX_LEVEL 
    if (level < MIN_LEVEL) return FALSE; 
    if (level > MAX_LEVEL) return FALSE; 

    p1 = m_param_table[level].param1; 
    p2 = m_param_table[level].param2; 

    // do stuff with p1, p2 

    return TRUE; 
} 

consumer.c

#include "library.h" 

// Limit user input to MIN_LEVEL and MAX_LEVEL 

set_param_level(user_input_value); 

我想利弊umer.c能夠訪問MIN_LEVELMAX_LEVEL,以進行用戶輸入驗證。顯然,他沒有訪問m_param_table,所以這些宏不起作用。

什麼是最優雅的正確的方式來做到這一點?選項有:

1)移動typedef ... params_t到一個頭文件,和從m_params_table除去static。很顯然,我不喜歡這樣做,因爲它會讓這些東西不必要地被看到。

2)對頭文件中的值進行硬編碼。當然,硬編碼值很吸引人。

+0

我的第一個反應是在頭文件中使用'extern size_t MAX_LEVEL;',並在表格後的.c文件中將其定義爲一個變量,但我猜你已經想到了它。 – WhozCraig

+0

@WhozCraig而且它不是隻讀的... –

+0

無可否認,我還沒有嘗試過,懷疑我即將受到教育,但extern const size_t ...在decl和const size_t ...上高清?如果這是一個標準符號導出'_MAX_LEVEL',後者將只爲您的libs帶來好處,但至少頭文件中的decl仍會爲消費者端限制聲明'const'。 – WhozCraig

回答

1

那麼,如果你希望你的值是常量,同時你希望隱藏表和相關的類型聲明,那麼一種解決方案就是確實硬編碼值,但同時在.c文件中添加一個靜態斷言,以確保硬編碼的值始終保持最新。

所以,在頭文件中你做

#define MIN_LEVEL 0 
#define MAX_LEVEL 1 

而在.c文件你做

static params_t m_param_table[] = { 
    {0, 1}, 
    {2, 3}, 
}; 

STATIC_ASSERT(MIN_LEVEL == 0); 
STATIC_ASSERT(MAX_LEVEL == sizeof m_param_table/sizeof *m_param_table - 1); 

(我們使用在C你最喜歡的實施STATIC_ASSERT)。

這種方法消除了硬編碼值最重要的原因之一:它們傾向於悄悄地變得過時。

+0

雖然[其他答案](http:// stackoverflow。com/a/18560908/119527)非常好,這允許我編寫一個編譯時問題,雖然我沒有明確提及它,但我正在追求這個問題。 –

5

您還有第三種選擇:

移動MIN_LEVELMAX_LEVELlibrary.c,創造get_min_level()get_max_level()library.h聲明並在library.c實施,僅僅分別返回MIN_LEVELMAX_LEVEL新功能。這些功能將從consumer.c中可見,而不會泄露庫的內部數據結構。

+0

當我寫這個問題時,我明白了。添加一個函數調用似乎有點矯枉過正,考慮到這是一個嵌入式項目,但我可能擔心太多。 –

+2

@JonathonReinhart這樣的函數是一個非常標準的數據隱藏在C語言中的成語,不用擔心它們過度殺傷。與'MAX_LEVEL'相比,'get_max_level()'唯一的缺點是前者不是編譯時常量。 – user4815162342

+0

沒錯,那是我不願意的原因,但我不認爲這太過關注。我認爲跨模塊優化鏈接器可以優化函數調用,並插入常量值。可惜你不得不希望這種行爲。 –

1

編輯:添加const關鍵字

一個選項是使用externs。

首先,MIN_LEVEL和MAX_LEVEL不應該在library.h中,而應該在library.c中。正如您已經指出的那樣,如果沒有m_param_table的定義,這個頭文件不會被消費者使用。而且你不應該僅僅爲了提供對這些常量的訪問而公開。這是你可以做的。

在library.h:

extern const int min_level; 
extern const int max_level; 

BOOL set_param_level(int level); 

在LIBRARY.C:

... 

#define MIN_LEVEL 0 
#define MAX_LEVEL ((sizeof(m_param_table)/sizeof(m_param_table[0]))-1) 

const int min_level = MIN_LEVEL; 
const int max_level = MAX_LEVEL; 

這使得常量提供給消費者不暴露私有內部。

+0

除了它們根本不是「常量」。 –

+0

夠正確。但是這是一種在不暴露實現的私有內部結構的情況下導出常量值的方法。其他選項是向API添加驗證功能。 – Ziffusion

+0

哦,我忽略了提及,你可以使用「const」關鍵字來獲得const。您需要在標題和代碼中使用它。它仍然不是一個編譯時間常量(需要內存訪問),但它是一種方法。 – Ziffusion

相關問題