Aku ingin memanggil fungsi dengan menggunakan variabel. Mungkin di C?
Sebenarnya, apa yang saya ingin lakukan adalah mendapatkan fungsi nama dari user dan menyimpannya dalam variabel. Sekarang saya ingin memanggil fungsi yang namanya disimpan. Siapa pun dapat memberitahu saya bagaimana hal ini dapat dilakukan di C?
Saya ingin mengembangkan AI mesin permainan untuk dua pemain game. Dua program dengan fungsi utama yang menerapkan logika untuk memenangkan permainan akan diumpankan ke mesin permainan. Biarkan aku menjadi jelas bahwa program nama akan sama seperti yang dari primefunctions dalam program yang mengimplementasikan logika untuk memenangkan permainan.
Jadi ketika pengguna memasukkan nama pertama dan kedua pemain, saya bisa menyimpannya di 2 variabel yang berbeda. Sekarang, sejak primefunction nama yang sama seperti nama program, saya berniat untuk memanggil fungsi dengan variabel-variabel yang mengandung nama prog.
C tidak mendukung operasi semacam ini (bahasa yang memiliki refleksi). Terbaik anda're akan mampu untuk melakukan ini adalah untuk membuat tabel pencarian dari nama-nama fungsi untuk fungsi pointer dan gunakan itu untuk mencari tahu apa fungsi untuk menelepon. Atau anda bisa menggunakan pernyataan switch.
Yang terbaik yang dapat anda lakukan adalah sesuatu seperti ini:
#include <stdio.h>
// functions
void foo(int i);
void bar(int i);
// function type
typedef void (*FunctionCallback)(int);
FunctionCallback functions[] = {&foo, &bar};
int main(void)
{
// get function id
int i = 0;
scanf("%i", &i);
// check id
if( i >= sizeof(functions))
{
printf("Invalid function id: %i", i);
return 1;
}
// call function
functions[i](i);
return 0;
}
void foo(int i)
{
printf("In foo() with: %i", i);
}
void bar(int i)
{
printf("In bar() with: %i", i);
}
Ini menggunakan angka bukan string untuk mengidentifikasi fungsi, tetapi melakukannya dengan string ini hanya masalah konversi string ke dalam fungsi yang tepat.
Apa yang kau lakukan sebenarnya? Jika hanya untuk rasa ingin tahu, di sini anda pergi, tetapi jika anda're mencoba untuk memecahkan masalah dengan ini, saya'm yakin ada cara yang lebih cocok untuk tugas anda.
Dalam keprihatinan dengan anda edit, anda akan ingin pergi dengan onebyone's menjawab, pasti.
Anda ingin pengguna anda's untuk membangun perpustakaan dinamis (thats bersama objek [.jadi] di Linux, dan dynamic link library [.dll] di Windows).
Setelah anda melakukan itu, jika mereka menyediakan anda dengan nama perpustakaan mereka, anda dapat meminta sistem operasi untuk memuat perpustakaan itu untuk anda, dan permintaan pointer ke fungsi di dalam perpustakaan itu.
Saat ini isn't benar-benar sebuah solusi praktis, saya yakin anda pasti bisa memanggil fungsi dengan sebuah string dengan memiliki program membaca di dalamnya's sendiri eksekusi dan mengurai simbol tabel. Tabel simbol harus berisi nama fungsi serta's pertama alamat instruksi. Anda bisa menempatkan alamat ini di fungsi variabel pointer dan panggilan itu.
Saya pikir saya dapat mencoba dan cambuk ini.
EDIT: Mohon tidak ada yang pernah menulis kode nyata seperti ini, tapi di sini adalah bagaimana anda dapat memanggil fungsi dengan menggunakan string untuk Linux ELF binary dengan utuh simbol tabel (membutuhkan libelf):
#include <fcntl.h>
#include <stdio.h>
#include <elf.h>
#include <libelf.h>
#include <stdlib.h>
#include <string.h>
void callMe() {
printf("callMe called\n");
}
int main(int argc, char **argv) {
Elf64_Shdr * shdr;
Elf64_Ehdr * ehdr;
Elf * elf;
Elf_Scn * scn;
Elf_Data * data;
int cnt;
void (*fp)() = NULL;
int fd = 0;
/* This is probably Linux specific - Read in our own executable*/
if ((fd = open("/proc/self/exe", O_RDONLY)) == -1)
exit(1);
elf_version(EV_CURRENT);
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
fprintf(stderr, "file is not an ELF binary\n");
exit(1);
}
/* Let's get the elf sections */
if (((ehdr = elf64_getehdr(elf)) == NULL) ||
((scn = elf_getscn(elf, ehdr->e_shstrndx)) == NULL) ||
((data = elf_getdata(scn, NULL)) == NULL)) {
fprintf(stderr, "Failed to get SOMETHING\n");
exit(1);
}
/* Let's go through each elf section looking for the symbol table */
for (cnt = 1, scn = NULL; scn = elf_nextscn(elf, scn); cnt++) {
if ((shdr = elf64_getshdr(scn)) == NULL)
exit(1);
if (shdr->sh_type == SHT_SYMTAB) {
char *name;
char *strName;
data = 0;
if ((data = elf_getdata(scn, data)) == 0 || data->d_size == 0) {
fprintf(stderr, "No data in symbol table\n");
exit(1);
}
Elf64_Sym *esym = (Elf64_Sym*) data->d_buf;
Elf64_Sym *lastsym = (Elf64_Sym*) ((char*) data->d_buf + data->d_size);
/* Look through all symbols */
for (; esym < lastsym; esym++) {
if ((esym->st_value == 0) ||
(ELF64_ST_BIND(esym->st_info)== STB_WEAK) ||
(ELF64_ST_BIND(esym->st_info)== STB_NUM) ||
(ELF64_ST_TYPE(esym->st_info)!= STT_FUNC))
continue;
name = elf_strptr(elf,shdr->sh_link , (size_t)esym->st_name);
if(!name){
fprintf(stderr,"%sn",elf_errmsg(elf_errno()));
exit(-1);
}
/* This could obviously be a generic string */
if(strcmp("callMe", name) == 0 ) {
printf("Found callMe @ %x\n", esym->st_value);
fp = esym->st_value;
}
}
/* Call and hope we don't segfault!*/
fp();
elf_end(elf);
return 0;
}
It's tidak mungkin di murni C, namun anda dapat memainkan trik dengan dll. Menempatkan semua fungsi yang ingin anda pilih dari ke dll, maka menggunakan dlsym
(atau GetProcAddress
pada Windows, atau apapun yang lain API anda menawarkan sistem) untuk mendapatkan fungsi pointer dengan nama, panggilan, dan menggunakan itu.
Ini doesn't bekerja pada beberapa platform, baik karena mereka don't memiliki dll di semua, atau karena seperti Symbian fungsi dalam dll't dapat diakses dengan nama pada saat runtime, hanya dengan nomor.
Diketahui bahwa jika anda pengguna trik anda dalam memilih fungsi yang doesn't memiliki parameter yang tepat untuk menghubungi anda ingin buat, maka program anda akan salah. C benar-benar isn't dirancang untuk mengatasi hal semacam ini.
#include <stdio.h>
#include <string.h>
void function_a(void) { printf("Function A\n"); }
void function_b(void) { printf("Function B\n"); }
void function_c(void) { printf("Function C\n"); }
void function_d(void) { printf("Function D\n"); }
void function_e(void) { printf("Function E\n"); }
const static struct {
const char *name;
void (*func)(void);
} function_map [] = {
{ "function_a", function_a },
{ "function_b", function_b },
{ "function_c", function_c },
{ "function_d", function_d },
{ "function_e", function_e },
};
int call_function(const char *name)
{
int i;
for (i = 0; i < (sizeof(function_map) / sizeof(function_map[0])); i++) {
if (!strcmp(function_map[i].name, name) && function_map[i].func) {
function_map[i].func();
return 0;
}
}
return -1;
}
int main()
{
call_function("function_a");
call_function("function_c");
call_function("function_e");
}
Ini adalah apa yang anda coba:
void foo()
{
printf("foo called\n");
}
void bar()
{
printf("bar called\n");
}
int main()
{
char fun[10] = {'\0'};
printf("Enter function name (max 9 characters):");
scanf("%s",fun);
if(strcmpi(fun, "foo") == 0)
{
foo();
}
else if(strcmpi(fun, "bar") == 0)
{
bar();
}
else
{
printf("Function not found\n");
}
return 0;
}
Seperti yang dikatakan oleh orang lain, memang benar bahwa C tidak memiliki refleksi mekanisme. Tapi anda dapat mencapai perilaku semacam ini dengan menggunakan dynamic load library/objek bersama. Bahkan anda dapat memuat perpustakaan dinamis dan kemudian anda dapat memanggil fungsi-fungsi dalam dll/jadi dengan nama mereka ! Hal ini tidak C dan OS tertentu, tapi yang's the way. Itu menggunakan dlopen di Linux dan LoadLibrary pada windows. Anda dapat menemukan perpustakaan yang melakukan pekerjaan untuk anda seperti gtk/glib.
Saya coba solusi ini : untuk membuka executable sebagai perpustakaan dinamis dengan dlopen().
It's bekerja dengan baik di Linux Ubuntu tapi sayangnya tidak saya tertanam ARM target. Saya don't tahu mengapa, jika itu's tergantung dari glibc atau versi libdl mungkin.
Di LENGAN saya target, pesan ini sangat jelas: "./test8: tidak beban dinamis eksekusi".
Pokoknya, kode yang saya gunakan adalah yang satu ini.
Aku yang disusun dengan:
gcc test8.c -ldl -rdynamic
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int TEST_MyTestFunction( char *pArgPos , int Size , char Param )
{
printf("TEST_MyTestFunction\n");
return 0;
}
int main(int argc, char **argv)
{
int ret;
void *handle;
char *error;
int(*func)(char *,int, char);
handle = dlopen(argv[0], RTLD_LAZY);
dlerror();
func = (int(*)(char *,int, char)) dlsym(handle, "TEST_MyTestFunction");
error = dlerror();
char *p1 = "param1";
int p2 = 5;
int p3 = 'A';
ret = func(p1, p2, p3);
return ret;
}
Jika saya mengerti pertanyaan anda dengan benar ingin anda gunakan akhir yang mengikat untuk memanggil fungsi C. Itu bukan sesuatu yang anda biasanya dapat dilakukan di C. nama Simbolik (seperti nama-nama fungsi) tidak disimpan di code yang dihasilkan oleh compiler C. Anda mungkin bisa membiarkan compiler memancarkan simbol-simbol, dan kemudian menggunakan mereka untuk melakukan ikatan, tetapi teknik ini akan bervariasi dari compiler compiler dan mungkin tidak akan bernilai hazzle.
Bahasa seperti C# dan Java mendukung refleksi sehingga mudah untuk melakukan ikatan.
Aku hanya mencoba Steve Jessop's menggunakan pendekatan statis-linked library seperti yang disarankan oleh Williham Totland di komentar dan ternyata untuk menjadi non-sepele.
Pertama, anda'll menemukan banyak tempat di Web (termasuk Wikipedia) yang akan memberitahu anda bahwa cara untuk membuka program utama sebagai perpustakaan adalah untuk memanggil dlopen(3) seperti ini dlopen(NULL, 0)
. Ini tidak akan bekerja untuk glibc karena mengikat bendera harus ditentukan, seperti halaman dengan jelas menyatakan:
salah Satu dari dua nilai berikut ini harus disertakan dalam bendera:
RTLD_LAZY
Melakukan malas mengikat. Hanya menyelesaikan simbol-simbol sebagai kode...
RTLD_NOW
Jika nilai ini ditentukan, atau variabel lingkungan...
Saya don't pikir mana yang anda pilih penting di sini karena anda're akan menghubungkan semua simbol dari statis ke perpustakaan anda eksekusi.
Hal ini membawa kita ke masalah berikutnya. Linker tidak akan menyertakan simbol-simbol dari anda perpustakaan statis di eksekusi karena mereka're tidak dirujuk. Cara untuk memaksa GNU linker untuk memasukkan simbol-simbol pokoknya adalah -Wl, - seluruh-arsip path/ke/static/lib.a-Wl, - tidak-seluruh-arsip
sebagai jawaban itu menjelaskan. Cara untuk memaksa Mac OS X linker untuk mencakup semua simbol dari anda perpustakaan statis adalah -Wl,-force_load path/ke/static/lib.a
.
Memperkenalkan Nginx-c-fungsi. Ini adalah NGINX modul yang memungkinkan anda untuk link anda .jadi(c/c++) aplikasi di server konteks dan memanggil fungsi .jadi aplikasi di lokasi direktif. Anda dapat menerapkan nginx berbagi memori cache data melalui nginx fungsi c(https://github.com/Taymindis/nginx-c-function/wiki/Nginx-Cache-Data-via-nginx-c-function). Ini adalah untuk pengembang yang suka host c server. https://github.com/Taymindis/nginx-c-function
C array hanya dapat diindeks dengan integral jenis. Jadi menulis sebuah tabel hash memetakan string untuk fungsi pointer.
Ini juga mungkin layak melihat fasilitas di Python, Lua, bahasa script lainnya untuk menanamkan mereka saat run-time dalam sebuah program C: bagian-bagian dari kode C kemudian dapat dimanipulasi dengan bahasa scripting.
Alt'ly, beberapa orang kode script bahasa ekstensi C. Kemudian mereka bisa memiliki kecepatan & rendahnya tingkat akses C dalam script.
Anda akan menemukan, cepat atau lambat, yang menggunakan dinamis mengetik script idiom bahasa - suka eval(), dan garis kabur antara fungsi dan nama fungsi, dan kode yang tergantung pada assoc array di C adalah mungkin tapi akhirnya menyakitkan.