2010-03-16 28 views
11

Bir veri bloğunu ve bloğun boyutunu ve bir işlev işaretçisini argüman olarak alan bir işlev var. Daha sonra veriler üzerinde yineleme yapar ve veri bloğunun her bir öğesi üzerinde bir hesaplama yapar.Genel işlev işaretçileri C

int myfunction(int* data, int size, int (*functionAsPointer)(int)){ 
    //walking through the data and calculating something 
    for (int n = 0; n < size; n++){ 
     data[n] = (*function)(data[n]); 
    } 
} 

Ben argümanlar olarak geçirerek fonksiyonları şuna benzer: aşağıdaki Ben ne yapıyorum temel anahat olan bu iyi çalışıyor

int mycalculation(int input){ 
    //doing some math with input 
    //... 
    return input; 
} 

ama şimdi geçmesi gerekiyor benim fonksiyonuma ek bir değişken. hatlar

int mynewcalculation(int input, int someVariable){ 
    //e.g. 
    input = input * someVariable; 
    //... 
    return input; 
} 

boyunca bir şey benim genel tasarım fikri tutarak bunu başarmak ve aynı zamanda zarif bir yolu var mı?

+2

Ek tamsayı parametresini içerecek şekilde işlev bildirimini değiştirememenizin bir nedeni var mı? –

+0

@dbingham: Eğer seslendirme yapmazsam, ikinci bir "fonksiyona" ihtiyacım var, o zaman iki "ints" ile bir fonksiyon göstergesine sahip olacaktı. Sorun şu ki, farklı tip ve argüman sayısıyla çok daha fazla fonksiyon olacak. Yani bu benim tasarımımı berbat eder. – Lucas

+0

Emin değilim, soruyu başlangıçtan doğru anladım. Bence Jefromi zaten bu noktada oldukça iyi bir şekilde kaplanmış üslere sahip. –

cevap

13

tamamen jenerik hale getirmek için olağan bir yol elbette typesafety konularında her türlü neden olan bir boşluk * ile geçerli:

int map_function(int* data, int size, int (*f_ptr)(int, void*), void *userdata){ 
    //walking through the data and calculating something 
    for (int n = 0; n < size; n++){ 
     data[n] = (*f_ptr)(data[n], userdata); 
    } 
} 

int simple_calculation(int input, void *userdata) { 
    // ignore userdata and do some math with input 
    return input; 
} 

int calculation_with_single_extra_arg(int input, void *userdata) { 
    int input2 = * (int*) userdata; 
    // do some math with input and input2 
    return input; 
} 

int calculation_with_many_extra_args(int input, void *userdata) { 
    my_data_t data = (my_data_t *) userdata; 
    return input * (input * data->a + data->b) + data->c; 
} 

int main(int argc, char **argv) { 
    int array[100]; 
    my_data_t userdata = { 1, 2, 3 }; 

    populate(array, 100); 

    map_function(data, calculation_with_many_extra_args, &userdata); 
} 

Herhangi verilen fonksiyon o prototip olması gerekir geçirilecek ama sen yapabilirsin userdata aracılığıyla istediğiniz herhangi bir veri itin. Sadece yazım hatası yok.

Ayrıca, va_args'ı dbingham'ın önerdiği gibi de kullanabilirsiniz; Bu, aslında, sizin fonksiyonunuz için prototipin int(int, va_list) olması gerektiğinden, gerçekten çok farklı değil.

Düzenleme: void* yaklaşımını tercih ederim. va_list, herhangi bir türdeki güvenlik özelliğini eklemez ve kullanıcı hatası için daha fazla potansiyel ekler (özellikle va_end'u iki kez çağırmak veya va_arg döngüsünü doğru uygulamamak). Boşluk * ayrıca herhangi bir ek kod satırı eklemez; temiz bir şekilde geçer ve kullanıcı sadece doğru bir şekilde (umarız) doğru tipte (genel olarak bir yapı olan).

+0

Bunu yapmak için temiz bir yol düşünemiyorum, ancak burada açıklanan "void *" yöntemi çok kötü değil. Bu, sistemin ioctl() işlevi gibi işlev görmesidir. İhtiyacınız olan herhangi bir veriyi (tamsayı, yapı, NULL) işaretçiden geçirebilirsiniz, ancak sizi korumak için herhangi bir tip kontrolüne sahip olmadığınızdan, işlevin ve arayanın her ikisi de dikkatli olmalıdır. – bta

+0

Evet, bu korkutucu, ama bu senin için C! Bu aynı zamanda bir grup GLib fonksiyonuyla (örn., G_hash_table_foreach') yapılan yoldur. – Cascabel

+0

Daha önce benzer bir şey denedim, ancak bu bana yüzlerce uyarı verdi: "uyumsuz işaretçi türünden argümanı geçme". Bunları görmezden gelebilir miyim? – Lucas