C'deki bir işleve varsayılan bağımsız değişkenler belirtmenin bir yolu var mı?
C'deki bir işleve varsayılan bağımsız değişkenler belirtmenin bir yolu var mı?
Yanıtlar:
Pek sayılmaz. Tek yol bir varargs işlevi yazmak ve arayanın geçmediği argümanlar için varsayılan değerleri manuel olarak doldurmaktır.
open(2)
Sistem çağrısı gerekli argümanlar bağlı mevcut olabilir isteğe bağlı bir argüman için bu kullanır ve printf(3)
kaç argüman belirtir olacağı bir biçim dizesi okur. Her ikisi de varargs'ı oldukça güvenli ve etkili bir şekilde kullanıyor ve kesinlikle onları vidalayabilseniz de, printf()
özellikle oldukça popüler görünüyor.
Vay canına, buralarda herkes çok kötümser. Cevap Evet.
Bu önemsiz değil: sonunda, çekirdek fonksiyona, destekleyici bir yapıya, bir sarmalayıcı fonksiyonuna ve sarmalayıcı fonksiyonunun etrafında bir makroya sahip olacağız. Çalışmamda bütün bunları otomatikleştirmek için bir dizi makro var; akışı anladıktan sonra, aynı şeyi yapmanız kolay olacaktır.
Bunu başka bir yerde yazdım, işte burada özeti tamamlamak için ayrıntılı bir dış bağlantı: http://modelingwithdata.org/arch/00000022.htm
Dönmek istiyoruz
double f(int i, double x)
varsayılanları alan bir işleve (i = 8, x = 3.14). Tamamlayıcı bir yapı tanımlayın:
typedef struct {
int i;
double x;
} f_args;
İşlevinizi yeniden adlandırın f_base
ve varsayılanları ayarlayan ve tabanı çağıran bir sarmalayıcı işlevi tanımlayın:
double var_f(f_args in){
int i_out = in.i ? in.i : 8;
double x_out = in.x ? in.x : 3.14;
return f_base(i_out, x_out);
}
Şimdi C'nin değişken makrolarını kullanarak bir makro ekleyin. Bu şekilde, kullanıcıların aslında bir f_args
yapı doldurduklarını bilmeleri ve her zamanki gibi yaptıklarını düşünmeleri gerekmez :
#define f(...) var_f((f_args){__VA_ARGS__});
Tamam, şimdi aşağıdakilerin hepsi işe yarayacak:
f(3, 8); //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2); //i=2, x=3.14
f(.x=9.2); //i=8, x=9.2
Bileşik başlatıcıların kesin kurallar için varsayılanları nasıl ayarladığına ilişkin kuralları kontrol edin.
İşe yaramayacak bir şey: f(0)
çünkü eksik bir değer ile sıfır arasında ayrım yapamayız. Deneyimlerime göre, bu dikkat edilmesi gereken bir şey, ancak ihtiyaç ortaya çıktığında halledilebilir --- varsayılanınızın yarısı gerçekten sıfırdır.
Bunu yazma zahmetinden geçtim, çünkü adlandırılmış argümanlar ve varsayılanların gerçekten C'deki kodlamayı daha kolay ve daha eğlenceli hale getirdiğini düşünüyorum. Ve C, bu kadar basit ve hala tüm bunları mümkün kılmak için yeterince sahip olduğu için harika.
{}
(Boş başlatıcı) bir C99 hatası olduğunu unutmayın .
#define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__})
Evet. :-) Ama beklediğiniz bir şekilde değil.
int f1(int arg1, double arg2, char* name, char *opt);
int f2(int arg1, double arg2, char* name)
{
return f1(arg1, arg2, name, "Some option");
}
Ne yazık ki, C, yöntemleri aşırı yüklemenize izin vermez, bu nedenle iki farklı işlevle sonuçlanırsınız. Yine de, f2'yi çağırarak, aslında varsayılan bir değerle f1'i çağırırsınız. Bu, mevcut kodu kopyalamak / yapıştırmaktan kaçınmanıza yardımcı olan bir "Kendinizi Tekrarlama" çözümüdür.
Varsayılan değerler için adlandırılmış parametreler (yalnızca) kullanan işlevler oluşturabiliriz. Bu, bk.'nin cevabının devamıdır.
#include <stdio.h>
struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})
/* use parentheses to avoid macro subst */
void (range)(struct range r) {
for (int i = r.from; i <= r.to; i += r.step)
printf("%d ", i);
puts("");
}
int main() {
range();
range(.from=2, .to=4);
range(.step=2);
}
C99 standardı, başlatma işlemindeki sonraki adların önceki öğeleri geçersiz kıldığını tanımlar. Bazı standart konumsal parametrelerimiz de olabilir, sadece makro ve fonksiyon imzasını buna göre değiştirin. Varsayılan değer parametreleri yalnızca adlandırılmış parametre stilinde kullanılabilir.
Program çıktısı:
1 2 3 4 5 6 7 8 9 10
2 3 4
1 3 5 7 9
OpenCV şöyle bir şey kullanır:
/* in the header file */
#ifdef __cplusplus
/* in case the compiler is a C++ compiler */
#define DEFAULT_VALUE(value) = value
#else
/* otherwise, C compiler, do nothing */
#define DEFAULT_VALUE(value)
#endif
void window_set_size(unsigned int width DEFAULT_VALUE(640),
unsigned int height DEFAULT_VALUE(400));
Kullanıcı ne yazması gerektiğini bilmiyorsa, bu hile yardımcı olabilir:
Hayır.
En yeni C99 standardı bile bunu desteklemiyor.
Kısa cevap: Hayır.
Biraz daha uzun cevap: İsteğe bağlı bağımsız değişkenler için ayrıştırdığınız bir dizeyi ilettiğiniz eski, eski bir geçici çözüm vardır :
int f(int arg1, double arg2, char* name, char *opt);
Burada opt, "name = value" çifti veya başka bir şey içerebilir ve buna şöyle diyebilirsiniz
n = f(2,3.0,"foo","plot=yes save=no");
Açıkçası bu sadece zaman zaman yararlıdır. Genellikle işlevsellik ailesine tek bir arayüz istediğinizde.
Bu yaklaşımı hala c ++ 'da profesyonel programlar tarafından yazılan parçacık fiziği kodlarında bulabilirsiniz (örneğin ROOT gibi) ) . Ana avantajı, geri uyumluluğu korurken neredeyse süresiz olarak genişletilebilmesidir.
struct
ve arayan biri yapmak, farklı seçenekler için alanları doldurmak ve sonra adrese göre geçmek veya NULL
varsayılan seçenekler için geçmek istiyorum .
Muhtemelen bunu yapmanın en iyi yolu (durumunuza bağlı olarak sizin durumunuzda mümkün olabilir veya olmayabilir) C ++ 'a geçmek ve' daha iyi bir C 'olarak kullanmaktır. Sınıfları, şablonları, operatör aşırı yüklemesini veya diğer gelişmiş özellikleri kullanmadan C ++ kullanabilirsiniz.
Bu, fonksiyon aşırı yüklenmesi ve varsayılan parametrelerle (ve kullanmayı seçtiğiniz diğer özelliklerle) C değişkenini verecektir. Sadece sınırlı bir C ++ alt kümesi kullanma konusunda gerçekten ciddiyseniz, biraz disiplinli olmanız gerekir.
Birçok insan C ++ 'ı bu şekilde kullanmanın korkunç bir fikir olduğunu söyleyebilir ve bir anlamı olabilir. Ama bu sadece bir fikir; Ben her şeyi satın almak zorunda kalmadan rahat C ++ özelliklerini kullanmak için geçerli olduğunu düşünüyorum. C ++ 'ın başarısının nedeninin önemli bir kısmının, ilk günlerinde çok fazla programcı tarafından tam olarak bu şekilde kullanılmasıdır.
Yine başka bir seçenek struct
s kullanır :
struct func_opts {
int arg1;
char * arg2;
int arg3;
};
void func(int arg, struct func_opts *opts)
{
int arg1 = 0, arg3 = 0;
char *arg2 = "Default";
if(opts)
{
if(opts->arg1)
arg1 = opts->arg1;
if(opts->arg2)
arg2 = opts->arg2;
if(opts->arg3)
arg3 = opts->arg3;
}
// do stuff
}
// call with defaults
func(3, NULL);
// also call with defaults
struct func_opts opts = {0};
func(3, &opts);
// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);
Makro kullanan başka bir numara:
#include <stdio.h>
#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)
int (func)(int a, int b)
{
return a + b;
}
int main(void)
{
printf("%d\n", func(1));
printf("%d\n", func(1, 2));
return 0;
}
Yalnızca bir bağımsız değişken iletilirse, b
varsayılan değeri alır (bu durumda 15)
Hayır, ancak varsayılan bağımsız değişkenleri kullanarak yaklaşık değerlere erişmek için bir dizi işlev (veya makro) kullanmayı düşünebilirsiniz :
// No default args
int foo3(int a, int b, int c)
{
return ...;
}
// Default 3rd arg
int foo2(int a, int b)
{
return foo3(a, b, 0); // default c
}
// Default 2nd and 3rd args
int foo1(int a)
{
return foo3(a, 1, 0); // default b and c
}
Evet, C99 özellikleri ile bunu yapabilirsiniz. Bu, yeni veri yapılarını tanımlamaksızın ve çalışma zamanında nasıl çağrıldığına karar vermek zorunda kalmadan ve herhangi bir hesaplama yükü olmadan çalışır.
Ayrıntılı bir açıklama için adresime bakın
http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/
Jens
Genellikle hayır, ancak gcc funcA () parametresinin son parametresini bir makro ile isteğe bağlı yapabilirsiniz.
FuncB () 'de' b 'parametresi için varsayılan değere ihtiyacım olduğunu belirtmek için özel bir değer (-1) kullanıyorum.
#include <stdio.h>
int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 )
int funcB( int a, int b ){
if( b == -1 ) b = 8;
return a+b;
}
int main(void){
printf("funcA(1,2): %i\n", funcA(1,2) );
printf("funcA(1): %i\n", funcA(1) );
printf("funcB(1, 2): %i\n", funcB(1, 2) );
printf("funcB(1,-1): %i\n", funcB(1,-1) );
}
Jens Gustedt'ın cevabını geliştirdim ki:
variadic.h:
#ifndef VARIADIC
#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)
// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)
#endif
Basitleştirilmiş kullanım senaryosu:
const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);
/*
The output buffer defaults to NULL if not provided.
*/
#include "variadic.h"
#define uint32_frombytes_2( b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c) a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
Ve _Generic ile:
const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);
const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);
const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);
const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);
/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/
#include "variadic.h"
#define uint16_tobytes_2(a, c) a, NULL, c
#define uint16_tobytes_3(a, b, c) a, b, c
#define uint16_tobytes(...) VARIADIC( uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define uint16_frombytes_2( b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c) a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define uint32_tobytes_2(a, c) a, NULL, c
#define uint32_tobytes_3(a, b, c) a, b, c
#define uint32_tobytes(...) VARIADIC( uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define uint32_frombytes_2( b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c) a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define tobytes(a, ...) _Generic((a), \
const uint16*: uint16_tobytes, \
const uint32*: uint32_tobytes) (VARIADIC2( uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))
#define frombytes(a, ...) _Generic((a), \
uint16*: uint16_frombytes, \
uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))
Ve _Generic ile birleştirilemeyen değişken fonksiyon adı seçimi ile:
// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.
#define merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define winternitz_5_name() merkle_lamport
#define winternitz_7_name() winternitz
#define winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)
EVET
Makrolar aracılığıyla
3 Parametreler:
#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)
void my_func3(char a, int b, float c) // b=10, c=0.5
{
printf("a=%c; b=%d; c=%f\n", a, b, c);
}
4. bağımsız değişken istiyorsanız, fazladan bir my_func3 eklenmelidir. VAR_FUNC, my_func2 ve my_func'daki değişikliklere dikkat edin
4 Parametreler:
#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)
void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}
Sadece yüzer istisna değişkenleri (varsayılan değerler verilemez o 3 parametre olduğu gibi son argüman ise olmadıkça ) onlar dönemini gerektiğinden, ( ''), makro argümanlarla kabul edilmez hangi. Ancak my_func2 makrosunda görüldüğü gibi bir çözüm bulabilir ( 4 parametreli büyük / küçük harf durumu) )
program
int main(void)
{
my_func('a');
my_func('b', 20);
my_func('c', 200, 10.5);
my_func('d', 2000, 100.5, "hello");
return 0;
}
Çıktı:
a=a; b=10; c=0.500000; d=default
a=b; b=20; c=0.500000; d=default
a=c; b=200; c=10.500000; d=default
a=d; b=2000; c=100.500000; d=hello
Evet, bir şeyler simulair yapabilirsiniz, burada alabileceğiniz farklı argüman listelerini bilmelisiniz, ancak hepsini işlemek için aynı işleve sahipsiniz.
typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;
typedef struct{
INPUT_SET type;
char* text;
} input_set1;
typedef struct{
INPUT_SET type;
char* text;
int var;
} input_set2;
typedef struct{
INPUT_SET type;
int text;
} input_set3;
typedef union
{
INPUT_SET type;
input_set1 set1;
input_set2 set2;
input_set3 set3;
} MY_INPUT;
void my_func(MY_INPUT input)
{
switch(input.type)
{
case my_input_set1:
break;
case my_input_set2:
break;
case my_input_set3:
break;
default:
// unknown input
break;
}
}
Bunu neden yapamıyoruz?
İsteğe bağlı bağımsız değişkene varsayılan bir değer verin. Bu şekilde, işlevin arayanının bağımsız değişkenin değerini iletmesi gerekmez. Bağımsız değişken varsayılan değeri alır. Ve bu argüman kolayca müşteri için isteğe bağlı hale gelir.
Örneğin;
void foo (int a, int b = 0);
Burada b isteğe bağlı bir argüman.