考慮以下 C 程式:
#include <stdio.h>
const int OP_0 = 0;
const int OP_1 = 1;
const int OP_2 = 2;
int op_0(int x) {
return x 2;
}
int op_1(int x) {
return x * 3 1;
}
int op_2(int x) {
return 2 * x * x - 10 * x 5;
}
int compute(int op, int x) {
switch (op) {
case OP_0: return op_0(x);
case OP_1: return op_1(x);
case OP_2: return op_2(x);
}
return 0;
}
int main() {
int opcode;
int number;
printf("Enter the opcode: ");
scanf("%d", &opcode);
printf("Enter the number: ");
scanf("%d", &number);
printf("Result: %d\n", compute(opcode, number));
return 0;
}
這是一個非常簡單的程式,讓用戶可以選擇 3 個操作之一來對int輸入執行。要使用這個程式,我們可以用,例如,編譯它gcc program.c -o program,然后用 運行它./program。這都是顯而易見的。不過,假設我們想添加另一個操作:
int op_3(int x) {
return 900 x;
}
如果我們想使用這個新操作,我們需要重新編譯整個程式。向該程式添加新操作很O(n)復雜,而且速度很慢,因為它需要完全重新編譯。
我的問題是:在 C 中,是否有可能讓這個程式添加新的本機操作(不撰寫解釋器)?換句話說,是否可以動態編譯并將op_3添加到上面的C程式中,而不必重新編譯所有內容?
出于說明目的,以下是我想到的示例:
int compute(int op, int x) {
// the first time it runs, would load `op_N.dll`
// the next time, would use the loaded version
// so, to add a new operation, we just compile
// it and add `op_N.dll` to this directory
Fun op = dynamic_load(op);
return op(x);
}
uj5u.com熱心網友回復:
我能想到的唯一方法是編譯一個新的動態庫,然后由程式使用dlopen()...
另一種類似但可能更原始的方法是將代碼編譯成目標檔案,然后將其加載到具有執行權限的 mmaped 區域中,然后使用函式指標跳轉到該區域。
uj5u.com熱心網友回復:
好吧,您要問的是“ SOLID 的開放原則”。為此,您需要dlsym在dlopen. 要獲得動態,dlsym您需要能夠讀取頭檔案或具有正確函式原型的檔案。是的,您需要轉換函式指標,但型別轉換取決于引數串列的型別。
編輯:硬編碼dlsym意味著每次向共享物件添加函式時都必須將匯入庫重新鏈接到可執行檔案。
或者
您有兩個共享物件。一個是匯入庫,另一個是您要添加功能的庫。正如大衛·惠勒所說,“計算機科學的所有問題都可以用另一個層次的間接來解決,除了間接層太多的問題。”。
uj5u.com熱心網友回復:
完整的菜鳥證明答案。正如其他答案所建議的那樣,我們可以使用dlopen和dlsym在 C 上動態加載共享庫。首先,讓我們創建庫。將以下檔案另存為0.c
int fn(int x) {
return x * 10;
}
然后,運行以下命令來創建共享庫:
clang -shared 0.c -o 0
現在,我們必須修改我們的compute負載功能,fn從0.c動態和使用它。首先,我們宣告一個fn : int -> int函式指標:
int (*fn)(int);
然后,我們將操作轉換為十進制(因為我們將共享庫保存為0,沒有擴展名):
char file[256];
sprintf(file, "%d", 0);
然后,我們0動態加載:
void *handle = dlopen(file, RTLD_LAZY);
然后,我們fn在該庫上找到并分配到fn函式指標:
*(void**)(&fn) = dlsym(LIB[op], "fn");
然后,我們就可以呼叫它了!
fn(5) // will return 50
這是一個完整的例子,它處理錯誤并將函式指標存盤在一個跳轉表中(所以我們不需要每次都重新加載 lib,很明顯!):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
const int MAX_OPS = 256;
// Jump-table with available functions
int (*OP[MAX_OPS])(int);
// Array with shared libraries
void* LIB[MAX_OPS];
// Loads an operation dynamically
void load_op(int op) {
int (*fn)(int);
// Generates the file name
char file[256];
sprintf(file, "%d", op);
// Opens the dynamic lib
LIB[op] = dlopen(file, RTLD_LAZY);
// Handles error opening the lib
if (!LIB[op]) {
fprintf(stderr, "Couldn't load operation: %s\n", dlerror());
}
// Creates the function pointer
*(void**)(&fn) = dlsym(LIB[op], "fn");
// Handles error finding the function pointer
if (!fn) {
fprintf(stderr, "Couldn't load operation: %s\n", dlerror());
dlclose(LIB[op]);
}
// Adds to jump table
OP[op] = fn;
}
// Clears the dynlib objects
void close_ops() {
for (int op = 0; op < MAX_OPS; op) {
dlclose(LIB[op]);
}
}
// Applies the specified operation to an input
// Requires a shared object file with a name equivalent to the decimal
// representation of op to be loaded on the current directory
int compute(int op, int x) {
if (!OP[op]) {
load_op(op);
}
return OP[op](x);
}
int main() {
int opcode;
int number;
printf("Enter the opcode: ");
scanf("%d", &opcode);
printf("Enter the number: ");
scanf("%d", &number);
printf("Result: %d\n", compute(opcode, number));
return 0;
}
感謝那些花時間在這里和 Libera.Chat 上的#c 上回答我的問題的人。謝謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/383270.html
標籤:C
