क्या C में किसी फ़ंक्शन के लिए डिफ़ॉल्ट तर्क निर्दिष्ट करने का कोई तरीका है?
क्या C में किसी फ़ंक्शन के लिए डिफ़ॉल्ट तर्क निर्दिष्ट करने का कोई तरीका है?
जवाबों:
ज़रुरी नहीं। एक ही तरीका होगा कि एक वार्गर्स फ़ंक्शन लिखें और मैन्युअल रूप से तर्कों के लिए डिफ़ॉल्ट मान भरें, जो कॉलर पास नहीं करता है।
open(2)
सिस्टम कॉल एक वैकल्पिक तर्क यह है कि आवश्यक तर्क के आधार पर उपस्थित हो सकता है के लिए इस का उपयोग करता है, और printf(3)
एक प्रारूप स्ट्रिंग है कि निर्दिष्ट करता है कि कितने तर्क नहीं होगा पढ़ता है। दोनों काफी सुरक्षित रूप से और प्रभावी रूप से varargs का उपयोग करते हैं, और यद्यपि आप निश्चित रूप से उन्हें पेंच कर सकते हैं, printf()
विशेष रूप से काफी लोकप्रिय लगते हैं।
वाह, हर कोई इधर-उधर एक निराशावादी है। इसका जवाब है हाँ।
यह तुच्छ नहीं है: अंत तक, हमारे पास कोर फ़ंक्शन, एक सहायक संरचना, एक आवरण फ़ंक्शन और आवरण फ़ंक्शन के चारों ओर एक मैक्रो होगा। अपने काम में मेरे पास यह सब स्वचालित करने के लिए मैक्रोज़ का एक सेट है; एक बार जब आप प्रवाह को समझ जाते हैं तो आपके लिए वही करना आसान होगा।
मैंने इसे कहीं और लिखा है, इसलिए यहाँ सारांश को पूरक करने के लिए एक विस्तृत बाहरी लिंक दिया गया है: http://modelingwithdata.org/arch/00000022.htm
हम मोड़ना चाहते हैं
double f(int i, double x)
एक फ़ंक्शन में जो चूक लेता है (i = 8, x = 3.14)। एक साथी संरचना को परिभाषित करें:
typedef struct {
int i;
double x;
} f_args;
अपने फ़ंक्शन का नाम बदलें f_base
, और एक रैपर फ़ंक्शन को परिभाषित करें जो डिफॉल्ट सेट करता है और आधार कहता है:
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);
}
अब C के वैरेडिक मैक्रोज़ का उपयोग करके एक मैक्रो जोड़ें। इस तरह से उपयोगकर्ताओं को यह जानने की ज़रूरत नहीं है कि वे वास्तव में एक f_args
संरचना को आबाद कर रहे हैं और सोचते हैं कि वे हमेशा से कर रहे हैं:
#define f(...) var_f((f_args){__VA_ARGS__});
ठीक है, अब निम्नलिखित में से सभी काम करेंगे:
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
नियमों की जांच करें कि कैसे कंपाउंड इनिशियलाइज़र सटीक नियमों के लिए चूक निर्धारित करते हैं।
एक चीज जो काम नहीं करेगी: f(0)
क्योंकि हम एक लापता मूल्य और शून्य के बीच अंतर नहीं कर सकते हैं। मेरे अनुभव में, यह देखने के लिए कुछ है, लेकिन इस बात का ध्यान रखा जा सकता है कि जरूरत पड़ने पर --- आपका डिफ़ॉल्ट समय वास्तव में शून्य हो।
मैं इसे लिखने की परेशानी से गुज़रा क्योंकि मुझे लगता है कि नाम के तर्क और चूक वास्तव में सी में कोडिंग को आसान और भी मज़ेदार बनाते हैं। और सी इतना सरल होने के लिए भयानक है और अभी भी यह सब संभव बनाने के लिए पर्याप्त है।
{}
(खाली इनिशियलाइज़र) एक त्रुटि C99 है।
#define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__})
हाँ। :-) लेकिन एक तरह से आप अपेक्षा नहीं करेंगे।
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");
}
दुर्भाग्यवश, C आपको विधियों को ओवरलोड करने की अनुमति नहीं देता है, इसलिए आप दो अलग-अलग कार्यों को समाप्त करेंगे। फिर भी, f2 पर कॉल करके, आप वास्तव में डिफ़ॉल्ट मान के साथ f1 कॉलिंग करेंगे। यह "डोंट रिपीट योरसेल्फ" सॉल्यूशन है, जो मौजूदा कोड को कॉपी / पेस्ट करने से बचने में आपकी मदद करता है।
हम ऐसे फ़ंक्शन बना सकते हैं जो डिफ़ॉल्ट मानों के लिए नामित पैरामीटर (केवल) का उपयोग करते हैं। यह बीके के उत्तर की निरंतरता है।
#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 मानक परिभाषित करता है कि प्रारंभ में नाम पिछले आइटम को ओवरराइड करता है। हमारे पास कुछ मानक स्थितियां भी हो सकती हैं, बस मैक्रो और फ़ंक्शन हस्ताक्षर को तदनुसार बदलें। डिफ़ॉल्ट मान पैरामीटर का उपयोग केवल नामित पैरामीटर शैली में किया जा सकता है।
कार्यक्रम का उत्पादन:
1 2 3 4 5 6 7 8 9 10
2 3 4
1 3 5 7 9
OpenCV कुछ इस तरह का उपयोग करता है:
/* 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));
यदि उपयोगकर्ता नहीं जानता कि उसे क्या लिखना चाहिए, तो यह ट्रिक मददगार हो सकती है:
नहीं।
बहुत नवीनतम C99 मानक भी इसका समर्थन नहीं करता है।
संक्षिप्त उत्तर: नहीं।
थोड़ा लंबा जवाब दें: एक पुराना, पुराना वर्कअराउंड है जहां आप एक स्ट्रिंग पास करते हैं जिसे आप वैकल्पिक तर्कों के लिए पार्स करते हैं:
int f(int arg1, double arg2, char* name, char *opt);
जहां ऑप्ट में "नाम = मान" जोड़ी या कुछ और शामिल हो सकता है, और जिसे आप पसंद करेंगे
n = f(2,3.0,"foo","plot=yes save=no");
जाहिर है कि यह कभी-कभार ही उपयोगी है। आम तौर पर जब आप कार्यक्षमता के परिवार के लिए एकल इंटरफ़ेस चाहते हैं।
आप अभी भी कण भौतिकी कोड में इस दृष्टिकोण को पाते हैं जो कि c ++ में व्यावसायिक कार्यक्रमों द्वारा लिखे गए हैं (उदाहरण के लिए ROOT )। यह मुख्य लाभ यह है कि इसे वापस संगतता बनाए रखते हुए लगभग अनिश्चित काल तक बढ़ाया जा सकता है।
struct
और कॉलर को एक बना देता हूं , विभिन्न विकल्पों के लिए फ़ील्ड भरें, और फिर इसे पते से पास करें, या NULL
डिफ़ॉल्ट विकल्पों के साथ पास करें।
संभवतः ऐसा करने का सबसे अच्छा तरीका (जो आपकी स्थिति के आधार पर आपके मामले में संभव हो सकता है या नहीं भी हो सकता है) C ++ में जाना और इसे 'बेहतर C' के रूप में उपयोग करना है। आप क्लास, टेम्प्लेट, ऑपरेटर ओवरलोडिंग या अन्य उन्नत सुविधाओं का उपयोग किए बिना सी ++ का उपयोग कर सकते हैं।
यह आपको फ़ंक्शन ओवरलोडिंग और डिफ़ॉल्ट मापदंडों (और जो भी अन्य विशेषताओं का उपयोग करने के लिए चुना है) के साथ आपको सी का एक प्रकार देगा। यदि आप सी ++ के केवल एक सीमित उप-वर्ग का उपयोग करने के बारे में वास्तव में गंभीर हैं, तो आपको थोड़ा अनुशासित होना होगा।
बहुत सारे लोग कहेंगे कि इस तरह से सी ++ का उपयोग करना एक भयानक विचार है, और उनके पास एक बिंदु हो सकता है। लेकिन यह सिर्फ एक राय है; मुझे लगता है कि यह सी ++ की सुविधाओं का उपयोग करने के लिए वैध है जो आपको पूरी चीज खरीदने के बिना आरामदायक हैं। मुझे लगता है कि C ++ की सक्सेज का एक महत्वपूर्ण कारण यह है कि इसका उपयोग बहुत सारे प्रोग्रामर द्वारा किया जाता है।
अभी तक एक और विकल्प का उपयोग करता है struct
:
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);
मैक्रो का उपयोग कर एक और चाल:
#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;
}
यदि केवल एक तर्क पारित किया जाता है, b
तो डिफ़ॉल्ट मान प्राप्त होता है (इस मामले में 15)
नहीं, लेकिन आप डिफ़ॉल्ट आर्ग्स का उपयोग करके अनुमानित (या मैक्रोज़) के एक सेट का उपयोग करने पर विचार कर सकते हैं :
// 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
}
हां, C99 की सुविधाओं के साथ आप ऐसा कर सकते हैं। यह नए डेटा संरचनाओं को परिभाषित किए बिना काम करता है या बिना फ़ंक्शन के रनटाइम पर निर्णय लेने के लिए कि यह कैसे कहा जाता है, और बिना किसी कम्प्यूटेशनल हेडहेड के।
विस्तृत विवरण के लिए मेरी पोस्ट देखें
http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/
जेन्स
आम तौर पर नहीं, लेकिन जीसीसी में आप एक मैक्रो के साथ funcA () के अंतिम पैरामीटर को वैकल्पिक बना सकते हैं।
FuncB () में मैं 'बी' पैरामीटर के लिए डिफ़ॉल्ट मान की आवश्यकता के संकेत के लिए एक विशेष मान (-1) का उपयोग करता हूं।
#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) );
}
मैंने जेन्स गुस्तेद के उत्तर में सुधार किया ताकि:
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
सरलीकृत उपयोग परिदृश्य:
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__)
और साथ में:
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__))
और varadic फ़ंक्शन नाम चयन के साथ, जिसे _Generic के साथ जोड़ा नहीं जा सकता है:
// 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__)
हाँ
मैक्रों के माध्यम से
3 पैरामीटर:
#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 वें तर्क चाहते हैं, तो अतिरिक्त my_func3 को जोड़ना होगा। VAR_FUNC, my_func2 और my_func में परिवर्तन देखें
4 पैरामीटर:
#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);
}
केवल अपवाद जो फ्लोट चर को डिफ़ॉल्ट मान नहीं दे सकते हैं ( जब तक कि यह 3 पैरामीटर मामले में अंतिम तर्क के रूप में नहीं है ), क्योंकि उन्हें अवधि ('।') की आवश्यकता होती है, जो कि मैक्रो तर्कों के भीतर स्वीकार नहीं किया जाता है। लेकिन my_func2 मैक्रो ( 4 मापदंडों के मामले में) के रूप में देखा गया एक काम का पता लगा सकता है ) के
कार्यक्रम
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;
}
आउटपुट:
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
हाँ, आप कुछ भी कर सकते हैं simulair, यहाँ आपको अलग-अलग तर्क सूचियों को जानना होगा जो आप प्राप्त कर सकते हैं लेकिन आपके पास एक ही कार्य है फिर सभी को संभालने के लिए।
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;
}
}
हम ऐसा क्यों नहीं कर सकते।
वैकल्पिक तर्क को एक डिफ़ॉल्ट मान दें। इस तरह, फ़ंक्शन के कॉलर को तर्क के मान को पारित करने की आवश्यकता नहीं है। तर्क डिफ़ॉल्ट मान लेता है। और आसानी से वह तर्क ग्राहक के लिए वैकल्पिक हो जाता है।
उदाहरण के लिए
शून्य फू (इंट ए, इंट बी = 0);
यहाँ b एक वैकल्पिक तर्क है।