如何做到這一點修改:
- 我們需要鏈接器在鏈接時指針數組以某種方式分配給功能
- 由於我們不知道數組的大小,我們還需要鏈接器以某種方式紀念數組的結尾
這是非常具體的,因爲正確的方法是使用自定義鏈接腳本,但如果我們在標準鏈接腳本中找到始終「保留」和「排序」的節, 。
通常情況下,對於.ctors.*
輸入節(標準需要C++構造函數按函數名順序執行,並且它在標準ld腳本中以這種方式執行),這是正確的,因此我們可以稍微修改一下一試。
只要考慮到它可能不適用於所有平臺(我已經在xtensa嵌入式架構和CygWIN中進行過測試,但這是一個黑客技巧,所以......)。另外,當我們把指針放在構造函數部分時,我們需要使用一個字節的RAM(對於整個程序)在C運行時初始化時跳過回調代碼。
test.c的:
註冊表的叫test
模塊,並在某些時候調用它的回調庫
#include "callback.h"
CALLBACK_LIST(test);
void do_something_and_call_the_callbacks(void) {
// ... doing something here ...
CALLBACKS(test);
// ... doing something else ...
}
callme1.c:
客戶端代碼註冊模塊的兩個回調0。生成的函數沒有名字(事實上他們確實有一個名字,但它奇蹟般地產生是編譯單元內唯一)
#include <stdio.h>
#include "callback.h"
CALLBACK(test) {
printf("%s: %s\n", __FILE__, __FUNCTION__);
}
CALLBACK(test) {
printf("%s: %s\n", __FILE__, __FUNCTION__);
}
void callme1(void) {} // stub to be called in the test sample to include the compilation unit. Not needed in real code...
callme2。C:
客戶端代碼註冊另一個回調模塊test
...
#include <stdio.h>
#include "callback.h"
CALLBACK(test) {
printf("%s: %s\n", __FILE__, __FUNCTION__);
}
void callme2(void) {} // stub to be called in the test sample to include the compilation unit. Not needed in real code...
callback.h:
和魔...
#ifndef __CALLBACK_H__
#define __CALLBACK_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef void (* callback)(void);
int __attribute__((weak)) _callback_ctor_stub = 0;
#ifdef __cplusplus
}
#endif
#define _PASTE(a, b) a ## b
#define PASTE(a, b) _PASTE(a, b)
#define CALLBACK(module) \
static inline void PASTE(_ ## module ## _callback_, __LINE__)(void); \
static void PASTE(_ ## module ## _callback_ctor_, __LINE__)(void); \
static __attribute__((section(".ctors.callback." #module "$2"))) __attribute__((used)) const callback PASTE(__ ## module ## _callback_, __LINE__) = PASTE(_ ## module ## _callback_ctor_, __LINE__); \
static void PASTE(_ ## module ## _callback_ctor_, __LINE__)(void) { \
if(_callback_ctor_stub) PASTE(_ ## module ## _callback_, __LINE__)(); \
} \
inline void PASTE(_ ## module ## _callback_, __LINE__)(void)
#define CALLBACK_LIST(module) \
static __attribute__((section(".ctors.callback." #module "$1"))) const callback _ ## module ## _callbacks_start[0] = {}; \
static __attribute__((section(".ctors.callback." #module "$3"))) const callback _ ## module ## _callbacks_end[0] = {}
#define CALLBACKS(module) do { \
const callback *cb; \
_callback_ctor_stub = 1; \
for(cb = _ ## module ## _callbacks_start ; cb < _ ## module ## _callbacks_end ; cb++) (*cb)(); \
} while(0)
#endif
主.c:
如果你想嘗試一下...(測試和工作在GCC-cygwin的)這個切入點一個獨立的程序
void do_something_and_call_the_callbacks(void);
int main() {
do_something_and_call_the_callbacks();
}
輸出:
這是我的嵌入式設備中的(相關)輸出。函數名在callback.h
產生,可能有重複,因爲功能是靜態的
app/callme1.c: _test_callback_8
app/callme1.c: _test_callback_4
app/callme2.c: _test_callback_4
而且在Cygwin中......
$ gcc -c -o callme1.o callme1.c
$ gcc -c -o callme2.o callme2.c
$ gcc -c -o test.o test.c
$ gcc -c -o main.o main.c
$ gcc -o testme test.o callme1.o callme2.o main.o
$ ./testme
callme1.c: _test_callback_4
callme1.c: _test_callback_8
callme2.c: _test_callback_4
連接圖:
這是有關部分由連接器生成的映射文件
*(SORT(.ctors.*))
.ctors.callback.test$1 0x4024f040 0x0 .build/testme.a(test.o)
.ctors.callback.test$2 0x4024f040 0x8 .build/testme.a(callme1.o)
.ctors.callback.test$2 0x4024f048 0x4 .build/testme.a(callme2.o)
.ctors.callback.test$3 0x4024f04c 0x0 .build/testme.a(test.o)
您是否在尋找一個平臺* *獨立*的解決方案,或者您有一個特定的平臺?如果你把獨立性放在門外,各種怪異的東西都可以被利用(比如MS的鏈接器,它的部分名稱的字母順序,我個人的最愛之一)。否則,你可能會更好地使用init'er入口點(或者你提供的鏈接,這實際上是非常光滑的)。 – WhozCraig
您是否考慮將回調數組構建爲makefile的一部分? –
您已經爲C和C++標記了這一點,但對於其中一個很好的答案可能會是另一個答案。也許只是堅持其中之一。 – PlasmaHH