簡體   English   中英

C99:靜態庫中的動態調度

[英]C99: Dynamic dispatch in a static library

假設我正在開發一個數學庫,並且希望它能夠檢測用戶的計算機是否支持SSE(以及哪個版本),然后基於該庫,將為同一個API函數調用單獨的內部函數。 我可以想到三種實現方法:

  1. 庫中具有全局函數指針,並讓用戶在其源代碼中調用mathInit()。 完成后,找出硬件細節,然后將功能指針分配給不同的功能。

  2. 相同,除了沒有全局函數指針外,將它們放在由mathInit()返回的結構中。 這樣,用戶將必須調用math.vec3Add(...)或類似名稱。

  3. 與1相同,但不使用全局指針,而是將mathInit()設為宏,以便函數指針在用戶的main()函數中具有局部作用域(並要求從main()調用mathInit())。 當然,它將在標題中。

這些方法是否可取? 還有其他更好的方法嗎?

恕我直言,這很大程度上是基於意見的。

我的觀點是,數學庫應公開其內部工作的最少詳細信息,並且在可能的情況下,不應要求復雜的函數指針,數據結構甚至宏來處理用戶代碼。

我會選擇(1),並假設您將函數指針完全隱藏在庫中,即通過庫代碼中的間接調用它們。

(3)絕對是最糟糕的選擇,因為它對用戶代碼施加了不直接明顯的限制。 調試用戶代碼時,它也可能會產生非顯而易見的問題/觀察。

(2)這是表示庫的一種非常不常見的方法,並且至少需要中等C的流利程度,並且可能會使非專家C用戶望而卻步。

您還可以將hasSSE函數與SSE和非SSE函數一起公開, hasSSE用戶決定使用什么功能。 但是不確定(1)是否會有任何好處。

我的建議是為每個指令集(例如Xnosse.o Xsse3.o Xsse4.o等)編譯一個單獨的單元,並對這些指令使用自動調度程序。 用戶需要為自己的PC獲得最佳性能,而無需關心內部細節。

由於您編寫的代碼是在庫中運行的,因此可以使用將在庫加載時調用的init函數來自動決定加載時間。 您還可以使此決定僅在首次調用函數時才運行,這是用於延遲綁定。

這是一個代碼示例(僅適用於gcc!)

編制單位:

//Xnosse.c
void do_some_math_stuff_no_sse(int x, int y)
{
    ...do some sophisticated math stuff with no sse support
}
void do_some_other_math_stuff_no_sse(int x, int y)
{
    ...do some other sophisticated math stuff with no sse support
}

//Xsse3.c
void do_some_math_stuff_sse3(int x, int y)
{
    ...do some sophisticated math stuff with sse3 support
}
void do_some_other_math_stuff_sse3(int x, int y)
{
    ...do some other sophisticated math stuff with sse3 support
}

//Xsse4.c
void do_some_math_stuff_sse4(int x, int y)
{
    ...do some sophisticated math stuff with sse4 support
}
void do_some_other_math_stuff_sse4(int x, int y)
{
    ...do some other sophisticated math stuff with sse4 support
}

現在到圖書館:

//my_math.h
/* Following definitions are in my_math.c */
extern void (*do_some_math_stuff)(int x, int, y);
extern void (*do_some_other_math_stuff)(int x, int y);

//my_math.c
void not_set(int x, int y)
{
    // If you don't want to use the constructor for any reason,
    // say you want lazy binding, this will do the trick as our
    // functions do_math_stuff and do_other_math_stuff are initialized
    // to this one
    setup();
}

void (*do_some_math_stuff)(int x, int, y) = not_set;
void (*do_some_other_math_stuff)(int x, int y) = not_set;

int detect_sse()
{
    ..Do runtime detection of sse version
}

/* The following function will be called when your library loads */
void __attribute__ ((constructor)) setup(void) 
{
    if (detect_sse() == 0)
    {
        do_some_math_stuff = do_some_math_stuff_no_sse;
        do_some_other_math_stuff = do_some_other_math_stuff_no_sse;
    }
else if (detect_sse() == 3)
    {
        do_some_math_stuff = do_some_math_stuff_sse3;
        do_some_other_math_stuff = do_some_other_math_stuff_sse3;
    }
else if (detect_sse() == 4)
    {
        do_some_math_stuff = do_some_math_stuff_sse4;
        do_some_other_math_stuff = do_some_other_math_stuff_sse4;
    }
}

如果您想要延遲綁定,請從安裝程序中刪除構造函數decorater並使用以下命令進行編譯:

gcc -Wall -shared -fPIC -o libmy_math.so my_math.c Xnosse.c Xsse3.c Xsse4.c

如果要在庫加載時運行動態調度程序,請對gcc使用以下附加參數:

gcc -Wall -shared -Wl,-init,setup -fPIC -o libmy_math.so my_math.c Xnosse.c Xsse3.c Xsse4.c

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM