मैं एक फ़ंक्शन कैसे लिख सकता हूं जो एक चर संख्या के तर्कों को स्वीकार करता है? क्या यह संभव है, कैसे?
मैं एक फ़ंक्शन कैसे लिख सकता हूं जो एक चर संख्या के तर्कों को स्वीकार करता है? क्या यह संभव है, कैसे?
जवाबों:
आपको शायद नहीं करना चाहिए, और आप शायद वही कर सकते हैं जो आप सुरक्षित और सरल तरीके से करना चाहते हैं। तकनीकी रूप से C में तर्कों की चर संख्या का उपयोग करने के लिए आप stdarg.h को शामिल करते हैं। उस से आपको va_list
टाइप के साथ-साथ तीन फ़ंक्शन मिलेंगे जो उस पर काम करते हैं va_start()
, va_arg()
और va_end()
।
#include<stdarg.h>
int maxof(int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
int max = va_arg(ap, int);
for(int i = 2; i <= n_args; i++) {
int a = va_arg(ap, int);
if(a > max) max = a;
}
va_end(ap);
return max;
}
अगर तुम मुझसे पूछते हो, यह गड़बड़ है। यह बुरा लग रहा है, यह असुरक्षित है, और यह तकनीकी विवरणों से भरा है, जिनका वैचारिक रूप से हासिल करने के लिए कुछ भी नहीं है। इसके बजाय, ओवरलोडिंग या इनहेरिटेंस / बहुरूपता, बिल्डर पैटर्न (जैसे operator<<()
धाराओं में) या डिफ़ॉल्ट तर्क आदि का उपयोग करने पर विचार करें । ये सभी सुरक्षित हैं: संकलक को इस बारे में अधिक पता चल जाता है कि आप क्या करने की कोशिश कर रहे हैं, ताकि अधिक से अधिक मौके मिल सकें। इससे पहले कि आप अपना पैर उड़ा दें।
...
वाक्यविन्यास से पहले कम से कम एक तर्क प्रदान करना आवश्यक है ?
printf()
, उदाहरण के लिए, समारोह कितने अतिरिक्त तर्क यह चर तर्क सूची में उम्मीद करनी चाहिए यह पता लगाने की विशेष टोकन स्ट्रिंग तर्क पार्स करता है।
<cstdarg>
<stdarg.h>
में सी ++ 11 आप के रूप में दो नये विकल्प है, variadic कार्यों में संदर्भ पेज वैकल्पिक खंड कहता है:
- वेरिएडिक टेम्प्लेट का उपयोग उन कार्यों को बनाने के लिए भी किया जा सकता है जो चर संख्या में तर्क लेते हैं। वे अक्सर बेहतर विकल्प होते हैं क्योंकि वे तर्कों के प्रकारों पर प्रतिबंध नहीं लगाते हैं, अभिन्न और फ्लोटिंग-पॉइंट प्रमोशन नहीं करते हैं, और सुरक्षित हैं। (C ++ 11 के बाद से)
- यदि सभी चर तर्क एक सामान्य प्रकार साझा करते हैं, तो एक चर :: initializer_list चर तर्कों तक पहुँचने के लिए एक सुविधाजनक तंत्र (एक अलग वाक्यविन्यास के साथ) प्रदान करता है।
नीचे एक उदाहरण है जो दोनों विकल्प दिखा रहा है ( इसे लाइव देखें ):
#include <iostream>
#include <string>
#include <initializer_list>
template <typename T>
void func(T t)
{
std::cout << t << std::endl ;
}
template<typename T, typename... Args>
void func(T t, Args... args) // recursive variadic function
{
std::cout << t <<std::endl ;
func(args...) ;
}
template <class T>
void func2( std::initializer_list<T> list )
{
for( auto elem : list )
{
std::cout << elem << std::endl ;
}
}
int main()
{
std::string
str1( "Hello" ),
str2( "world" );
func(1,2.5,'a',str1);
func2( {10, 20, 30, 40 }) ;
func2( {str1, str2 } ) ;
}
यदि आप उपयोग कर रहे हैं gcc
या clang
हम फ़ंक्शन के प्रकार हस्ताक्षर को प्रदर्शित करने के लिए PRETTY_FUNCTION जादू चर का उपयोग कर सकते हैं जो समझने में मददगार हो सकता है कि क्या हो रहा है। उदाहरण के लिए:
std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;
उदाहरण में चर कार्यों के लिए निम्नलिखित परिणाम प्राप्त होंगे ( इसे लाइव देखें ):
void func(T, Args...) [T = int, Args = <double, char, std::basic_string<char>>]: 1
void func(T, Args...) [T = double, Args = <char, std::basic_string<char>>]: 2.5
void func(T, Args...) [T = char, Args = <std::basic_string<char>>]: a
void func(T) [T = std::basic_string<char>]: Hello
Visual Studio में आप FUNCSIG का उपयोग कर सकते हैं ।
अद्यतन पूर्व C ++ 11
प्री सी ++ 11 एसटीडी के लिए विकल्प :: initializer_list एसटीडी होगा :: वेक्टर या अन्य मानक कंटेनरों में से एक :
#include <iostream>
#include <string>
#include <vector>
template <class T>
void func1( std::vector<T> vec )
{
for( typename std::vector<T>::iterator iter = vec.begin(); iter != vec.end(); ++iter )
{
std::cout << *iter << std::endl ;
}
}
int main()
{
int arr1[] = {10, 20, 30, 40} ;
std::string arr2[] = { "hello", "world" } ;
std::vector<int> v1( arr1, arr1+4 ) ;
std::vector<std::string> v2( arr2, arr2+2 ) ;
func1( v1 ) ;
func1( v2 ) ;
}
और के लिए वैकल्पिक variadic टेम्पलेट्स होगा variadic कार्यों हालांकि वे नहीं कर रहे हैं टाइप-सुरक्षित और सामान्य रूप में त्रुटियों की संभावना और उपयोग करने के लिए असुरक्षित हो सकता है , लेकिन केवल अन्य संभावित विकल्प का उपयोग किया जाएगा डिफ़ॉल्ट तर्क है, हालांकि यह है कि सीमित उपयोग है। नीचे दिए गए उदाहरण से जुड़े संदर्भ में नमूना कोड का एक संशोधित संस्करण है:
#include <iostream>
#include <string>
#include <cstdarg>
void simple_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
while (*fmt != '\0') {
if (*fmt == 'd') {
int i = va_arg(args, int);
std::cout << i << '\n';
} else if (*fmt == 's') {
char * s = va_arg(args, char*);
std::cout << s << '\n';
}
++fmt;
}
va_end(args);
}
int main()
{
std::string
str1( "Hello" ),
str2( "world" );
simple_printf("dddd", 10, 20, 30, 40 );
simple_printf("ss", str1.c_str(), str2.c_str() );
return 0 ;
}
वैरेडिक फ़ंक्शंस का उपयोग करने से उन तर्कों पर भी प्रतिबंध लग जाता है, जिन्हें आप पास कर सकते हैं, जो ड्राफ्ट C ++ मानक में विस्तृत है। 5.2.2
फंक्शन कॉल पैराग्राफ 7 :
जब किसी दिए गए तर्क के लिए कोई पैरामीटर नहीं होता है, तो तर्क को इस तरह से पारित किया जाता है कि प्राप्त फ़ंक्शन va_arg (18.7) को लागू करके तर्क का मूल्य प्राप्त कर सकता है। लॉवल्यू-टू-रिवल्यू (4.1), एरे-टू-पॉइंटर (4.2), और फ़ंक्शन-टू-पॉइंटर (4.3) मानक रूपांतरण तर्क अभिव्यक्ति पर किए जाते हैं। इन रूपांतरणों के बाद, यदि तर्क में अंकगणित, गणना, सूचक, सदस्य के लिए सूचक या कक्षा प्रकार नहीं है, तो कार्यक्रम बीमार है। यदि तर्क में गैर-POD वर्ग प्रकार (खंड 9) है, तो व्यवहार अपरिभाषित है। [...]
typename
बनाम class
उपयोग जानबूझकर ऊपर है? यदि हां, तो कृपया समझाएं।
initializer_list
पुनरावर्ती लेने के लिए एक समारोह बनाना संभव है ?
चूंकि C ++ 11 में वेरिएडिक टेम्प्लेट्स की शुरुआत और C ++ 17 में फोल्ड एक्सप्रेशंस, एक टेम्प्लेट-फंक्शन को परिभाषित करना संभव है, जो कॉलर साइट पर कॉल करने योग्य है, जैसे कि यह एक वैरिएड फ़ंक्शन था, लेकिन फायदे के साथ :
यहाँ मिश्रित तर्क प्रकारों के लिए एक उदाहरण है
template<class... Args>
void print(Args... args)
{
(std::cout << ... << args) << "\n";
}
print(1, ':', " Hello", ',', " ", "World!");
और सभी तर्कों के लिए लागू प्रकार मैच के साथ एक और:
#include <type_traits> // enable_if, conjuction
template<class Head, class... Tail>
using are_same = std::conjunction<std::is_same<Head, Tail>...>;
template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
void print_same_type(Head head, Tail... tail)
{
std::cout << head;
(std::cout << ... << tail) << "\n";
}
print_same_type("2: ", "Hello, ", "World!"); // OK
print_same_type(3, ": ", "Hello, ", "World!"); // no matching function for call to 'print_same_type(int, const char [3], const char [8], const char [7])'
// print_same_type(3, ": ", "Hello, ", "World!");
^
अधिक जानकारी:
template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
Head
और " Tail...
एक ही हो ", जहाँ" समान "का अर्थ होता है std::conjunction<std::is_same<Head, Tail>...>
। इस अंतिम निश्चित को पढ़ें " Head
जैसा कि सभी के लिए समान है Tail...
"।
c ++ 11 में आप कर सकते हैं:
void foo(const std::list<std::string> & myArguments) {
//do whatever you want, with all the convenience of lists
}
foo({"arg1","arg2"});
सूची आरम्भिक FTW!
C ++ 11 में वेरिएबल लॉजिक टेम्प्लेट करने का एक तरीका है जो वेरिएबल लॉजिक फंक्शन्स के लिए एक बहुत ही सुंदर और टाइप सेफ तरीका है। Bjarne खुद C ++ 11FAQ में वेरिएबल लॉजिक टेम्प्लेट का उपयोग करके प्रिंटफ का एक अच्छा उदाहरण देता है ।
व्यक्तिगत रूप से, मैं इसे इतना सुरुचिपूर्ण मानता हूं कि मैं C ++ में एक चर तर्क फ़ंक्शन के साथ भी परेशान नहीं करूंगा, जब तक कि संकलक को C ++ 11 चर तर्क टेम्पलेट के लिए समर्थन न हो।
,
ऑपरेटर को फोल्ड एक्सप्रेशंस के साथ उपयोग कर सकते हैं )। अन्यथा, मुझे ऐसा नहीं लगता।
C ++ में C- स्टाइल वैरेडिक फ़ंक्शन समर्थित हैं।
हालाँकि, अधिकांश C ++ पुस्तकालयों में एक वैकल्पिक मुहावरे का उपयोग किया जाता है, जबकि 'c' printf
फ़ंक्शन परिवर्तनशील तर्क लेता है, c++ cout
ऑब्जेक्ट <<
ओवरलोडिंग का उपयोग करता है जो कि प्रकार की सुरक्षा और ADTs (शायद कार्यान्वयन सादगी की कीमत पर) को संबोधित करता है ।
std::initializer_lists
... और यह पहले से ही एक सरल कार्य पर बड़े पैमाने पर जटिलता का परिचय दे रहा है।
वररग्स या ओवरलोडिंग के अलावा, आप अपने तर्कों को एक std :: वेक्टर या अन्य कंटेनरों (std :: map for example) में एकत्र करने पर विचार कर सकते हैं। कुछ इस तरह:
template <typename T> void f(std::vector<T> const&);
std::vector<int> my_args;
my_args.push_back(1);
my_args.push_back(2);
f(my_args);
इस तरह से आपको टाइप सेफ्टी मिलेगी और इन वैरिएड तर्कों का तार्किक अर्थ स्पष्ट होगा।
निश्चित रूप से इस दृष्टिकोण में प्रदर्शन के मुद्दे हो सकते हैं लेकिन आपको उनके बारे में चिंता नहीं करनी चाहिए जब तक कि आप सुनिश्चित नहीं हैं कि आप कीमत का भुगतान नहीं कर सकते हैं। यह सी + + के लिए आ "Pythonic" दृष्टिकोण का एक प्रकार है ...
एकमात्र तरीका सी शैली चर तर्कों के उपयोग के माध्यम से है, जैसा कि यहां वर्णित है । ध्यान दें कि यह एक अनुशंसित अभ्यास नहीं है, क्योंकि यह टाइपसाफ़ और त्रुटि-प्रवण नहीं है।
सी-स्टाइल वैरगेज़ का सहारा लिए बिना ऐसा करने का कोई मानक C ++ तरीका नहीं है (...
) का ।
कुछ डिफ़ॉल्ट तर्क हैं जो संदर्भ के आधार पर "लुक" की तरह होते हैं, जैसे चर संख्या के तर्क:
void myfunc( int i = 0, int j = 1, int k = 2 );
// other code...
myfunc();
myfunc( 2 );
myfunc( 2, 1 );
myfunc( 2, 1, 0 );
सभी चार फ़ंक्शन कॉल myfunc
अलग-अलग तर्कों के साथ कॉल करते हैं। यदि कोई नहीं दिया जाता है, तो डिफ़ॉल्ट तर्क का उपयोग किया जाता है। हालाँकि, ध्यान दें कि आप केवल अनुगामी तर्कों को छोड़ सकते हैं। कोई रास्ता नहीं है, उदाहरण के लिए i
केवल छोड़ देना और देना है j
।
यह संभव है कि आप ओवरलोडिंग या डिफ़ॉल्ट पैरामीटर चाहते हैं - डिफ़ॉल्ट फ़ंक्शन के साथ एक ही फ़ंक्शन को परिभाषित करें:
void doStuff( int a, double termstator = 1.0, bool useFlag = true )
{
// stuff
}
void doStuff( double std_termstator )
{
// assume the user always wants '1' for the a param
return doStuff( 1, std_termstator );
}
यह आपको चार अलग-अलग कॉलों में से एक के साथ विधि को कॉल करने की अनुमति देगा:
doStuff( 1 );
doStuff( 2, 2.5 );
doStuff( 1, 1.0, false );
doStuff( 6.72 );
... या आप सी से कन्वेंशन बुला रहे v_args की तलाश में हो सकते हैं।
यदि आप जानते हैं कि प्रदान किए जाने वाले तर्कों की संख्या कितनी होगी, तो आप हमेशा कुछ फ़ंक्शन का उपयोग कर सकते हैं, जैसे ओवरलोडिंग
f(int a)
{int res=a; return res;}
f(int a, int b)
{int res=a+b; return res;}
और इसी तरह...
वैरिएबल टेम्प्लेट का उपयोग करना, उदाहरण के लिए console.log
जावास्क्रिप्ट में देखा गया है:
Console console;
console.log("bunch", "of", "arguments");
console.warn("or some numbers:", 1, 2, 3);
console.error("just a prank", "bro");
फ़ाइल नाम जैसे js_console.h
:
#include <iostream>
#include <utility>
class Console {
protected:
template <typename T>
void log_argument(T t) {
std::cout << t << " ";
}
public:
template <typename... Args>
void log(Args&&... args) {
int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
cout << endl;
}
template <typename... Args>
void warn(Args&&... args) {
cout << "WARNING: ";
int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
cout << endl;
}
template <typename... Args>
void error(Args&&... args) {
cout << "ERROR: ";
int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
cout << endl;
}
};
जैसा कि अन्य लोगों ने कहा है, सी-स्टाइल वैरग। लेकिन आप डिफॉल्ट तर्कों के साथ भी कुछ ऐसा कर सकते हैं।
अब यह संभव है ... किसी भी और टेम्पलेट्स को बढ़ावा देने का उपयोग करते हुए इस मामले में, तर्क प्रकार मिश्रित हो सकते हैं
#include <boost/any.hpp>
#include <iostream>
#include <vector>
using boost::any_cast;
template <typename T, typename... Types>
void Alert(T var1,Types... var2)
{
std::vector<boost::any> a( {var1,var2...});
for (int i = 0; i < a.size();i++)
{
if (a[i].type() == typeid(int))
{
std::cout << "int " << boost::any_cast<int> (a[i]) << std::endl;
}
if (a[i].type() == typeid(double))
{
std::cout << "double " << boost::any_cast<double> (a[i]) << std::endl;
}
if (a[i].type() == typeid(const char*))
{
std::cout << "char* " << boost::any_cast<const char*> (a[i]) <<std::endl;
}
// etc
}
}
void main()
{
Alert("something",0,0,0.3);
}
सी और सी ++ समाधानों को शब्दार्थ सरलतम, निष्पादक और सबसे गतिशील विकल्प के लिए मिलाएं। यदि आप पेंच करते हैं, तो कुछ और प्रयास करें।
// spawn: allocate and initialize (a simple function)
template<typename T>
T * spawn(size_t n, ...){
T * arr = new T[n];
va_list ap;
va_start(ap, n);
for (size_t i = 0; i < n; i++)
T[i] = va_arg(ap,T);
return arr;
}
उपयोगकर्ता लिखते हैं:
auto arr = spawn<float> (3, 0.1,0.2,0.3);
शब्दार्थ, यह दिखता है और बिल्कुल एक एन-तर्क फ़ंक्शन की तरह लगता है। हुड के तहत, आप इसे एक या दूसरे तरीके से अनपैक कर सकते हैं।
int fun(int n_args, ...) {
int *p = &n_args;
int s = sizeof(int);
p += s + s - 1;
for(int i = 0; i < n_args; i++) {
printf("A1 %d!\n", *p);
p += 2;
}
}
सादा संस्करण