सी / सी ++ मैक्रो में कोमा


103

कहें कि हमारे पास इस तरह एक मैक्रो है

#define FOO(type,name) type name

जिसे हम इस्तेमाल कर सकते थे

FOO(int, int_var);

लेकिन हमेशा बस के रूप में नहीं:

FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2

बेशक हम कर सकते हैं:

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK

जो बहुत एर्गोनोमिक नहीं है। प्लस प्रकार की असंगतताओं से निपटा जाना चाहिए। किसी भी विचार कैसे मैक्रो के साथ इसे हल करने के लिए?


मैं अनुमान लगा रहा हूं कि आपको अक्षर बनाने के लिए उन्हें शाब्दिक रूप से बचना होगा।
Jite

कम से कम C ++ में, आप कहीं भी एक टंकण रख सकते हैं, इसलिए मुझे यकीन नहीं है कि आप क्यों कहते हैं कि यह "पहले से" होना है।
वॉन काटो

जवाबों:


108

क्योंकि कोण कोष्ठक भी प्रतिनिधित्व करते हैं (या में होते हैं) कर सकते हैं तुलना ऑपरेटरों <, >, <=और >=, मैक्रो विस्तार कोण कोष्ठक के अंदर के लिए अल्पविराम नजरअंदाज नहीं कर सकते जैसे कि यह कोष्ठकों के भीतर है। (यह वर्ग कोष्ठक और ब्रेसिज़ के लिए भी एक समस्या है, भले ही वे आमतौर पर संतुलित जोड़े के रूप में होते हैं।) आप कोष्ठक में मैक्रो तर्क को संलग्न कर सकते हैं:

FOO((std::map<int, int>), map_var);

समस्या यह है कि पैरामीटर मैक्रो विस्तार के अंदर कोष्ठक बना रहता है, जो इसे अधिकांश संदर्भों में एक प्रकार के रूप में पढ़ने से रोकता है।

वर्कअराउंड करने के लिए एक अच्छी चाल यह है कि C ++ में, आप एक फ़ंक्शन का उपयोग करके एक कोष्ठक प्रकार के नाम से एक टाइपनेम निकाल सकते हैं:

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);

क्योंकि फ़ंक्शन प्रकार बनाने से अतिरिक्त कोष्ठक की उपेक्षा होती है, आप इस मैक्रो का उपयोग बिना कोष्ठक के साथ या उसके बिना कर सकते हैं जहाँ टाइप नाम में अल्पविराम शामिल नहीं है:

FOO((int), int_var);
FOO(int, int_var2);

सी में, निश्चित रूप से, यह आवश्यक नहीं है क्योंकि टाइप नाम में कोष्ठक के बाहर अल्पविराम नहीं हो सकता है। तो, एक क्रॉस-भाषा मैक्रो के लिए आप लिख सकते हैं:

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif

यह कमाल का है। लेकिन आपको इस बारे में कैसे पता चला? मैं कई तरह की कोशिशें कर रहा हूं और कभी सोचा भी नहीं था कि कोई फंक्शन टाइप इश्यू को ठीक कर देगा।
विल कस्टोड

@WilliamCustode जैसा कि मुझे याद है, मैं सबसे डरावने तोते की समस्या के संदर्भ में फ़ंक्शन प्रकारों और फ़ंक्शन घोषणाओं के व्याकरण का अध्ययन कर रहा हूं, इसलिए यह भाग्यशाली था कि मुझे पता था कि निरर्थक कोष्ठक उस संदर्भ में एक प्रकार से लागू किया जा सकता है।
इकतुर

टेम्प्लेट्स के साथ काम करते समय मुझे इस पद्धति में एक समस्या मिली। मान लीजिए कि मुझे जो कोड चाहिए था वह यह था: template<class KeyType, class ValueType> void SomeFunc(FOO(std::map<KeyType, ValueType>) element) {}यदि मैं इस समाधान को यहां लागू करता हूं, तो मैक्रो के पीछे की संरचना निर्भर प्रकार बन जाती है, और टाइपनाम का उपसर्ग अब प्रकार पर आवश्यक है। आप इसे जोड़ सकते हैं, लेकिन टाइप कटौती टूट गई है, इसलिए अब आपको फ़ंक्शन को कॉल करने के लिए मैन्युअल रूप से टाइप करें तर्कों को सूचीबद्ध करना होगा। मैंने कॉमा के लिए एक मैक्रो को परिभाषित करने के मंदिर की विधि का उपयोग करके समाप्त किया। यह बहुत सुंदर नहीं लग सकता है, लेकिन यह पूरी तरह से काम करता है।
रोजर सैंडर्स

उत्तर पर एक छोटी सी समस्या: यह बताता है कि कॉमा को अंदर से अनदेखा किया जाता है [] और {}, वे नहीं हैं, यह केवल ()दुख के साथ काम करता है । देखें: हालाँकि, वर्ग कोष्ठक या ब्रेसिज़ को संतुलित करने की कोई आवश्यकता नहीं है ...
VinGarcia

दुर्भाग्य से यह MSVC में काम नहीं करता है: godbolt.org/z/WPjYW8 । ऐसा लगता है कि MSVC एकाधिक पार्न्स को जोड़ने की अनुमति नहीं देता है और इसे पार्स करने में विफल रहता है। एक समाधान जो उतना सुंदर नहीं है, लेकिन तेज़ (कम टेम्पलेट तात्कालिकता) अल्पविराम तर्क को एक आवरण मैक्रो में लपेटने के लिए है #define PROTECT(...) argument_type<void(__VA_ARGS__)>::type:। कई मैक्रोज़ के माध्यम से भी पासिंग की दलील अब आसानी से संभव है और सरल प्रकार के लिए आप प्रॉजेक्ट को प्राप्त कर सकते हैं। हालाँकि फ़ंक्शन प्रकार इस तरह मूल्यांकन किए जाने पर फ़ंक्शन पॉइंटर्स बन जाते हैं
फ्लेमफायर

119

यदि आप कोष्ठक का उपयोग नहीं कर सकते हैं और आपको माइक का SINGLE_ARG समाधान पसंद नहीं है, तो बस एक COMMA को परिभाषित करें:

#define COMMA ,

FOO(std::map<int COMMA int>, map_var);

यह भी मदद करता है अगर आप मैक्रो तर्कों में से कुछ को कड़ा करना चाहते हैं, जैसे कि

#include <cstdio>
#include <map>
#include <typeinfo>

#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
    " has typeid name \"%s\"", typeid(type).name())

int main()
{
    FOO(std::map<int COMMA int>, std::printf);
}

जो प्रिंट करता है std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE"


16
#define COMMA वाह, आपने मुझे काम से बचाया है ... मैंने इस साल पहले क्यों नहीं सोचा। इस विचार को साझा करने के लिए धन्यवाद। यह भी मुझे मैक्रोज़ बनाने की अनुमति दे रहा है जो अलग-अलग तर्क के साथ सेटअप फ़ंक्शन पूरी तरह से मायने रखता है।
मौलीद

28
इसके अलावा 1 हॉरर के लिए
namezero

1
@kiw यदि आप #define STRVX(...) STRV(__VA_ARGS__)और #define STRV(...) # __VA_ARGS__, फिर std::cout << STRV(type<A COMMA B>) << std::endl;प्रिंट करेंगे type<A COMMA B>और std::cout << STRVX(type<A COMMA B>) << std::endl;प्रिंट करेंगे type<A , B>। ( STRV"वैरेडिक स्ट्रिफ़ाइज़" के लिए है, और STRVX"विस्तारित वैरेडिक स्ट्रिफ़ाइज़" के लिए है।)
-ए-यूज़र

1
@ नहीं-एक उपयोगकर्ता हाँ, लेकिन वैरिएड मैक्रोज़ के साथ आपको COMMAपहली जगह में मैक्रो की आवश्यकता नहीं है। यही मैंने खत्म किया।
kiw

मैं इसका इस्तेमाल कभी नहीं करूँगा, लेकिन प्रफुल्लित होने के लिए +1।
राफेल बैप्टिस्टा

58

यदि आपका प्रीप्रोसेसर वैरिएड मैक्रोज़ का समर्थन करता है:

#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name

FOO(SINGLE_ARG(std::map<int, int>), map_var);

अन्यथा, यह थोड़ा अधिक थकाऊ है:

#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need

FOO(SINGLE_ARG2(std::map<int, int>), map_var);

ओह, भगवान ... क्यों? केवल कोष्ठकों में संलग्न क्यों नहीं?

15
@VladLazarenko: क्योंकि आप हमेशा कोष्ठक में कोड के मनमाने टुकड़े नहीं डाल सकते। विशेष रूप से, आप एक घोषणापत्र में टाइप नाम के आसपास कोष्ठक नहीं डाल सकते हैं, जो वास्तव में यह तर्क बन जाता है।
माइक सेमोर

2
... और यह भी क्योंकि आप केवल मैक्रो परिभाषा को संशोधित करने में सक्षम हो सकते हैं और उन सभी स्थानों को नहीं जो इसे कहते हैं (जो आपके नियंत्रण में नहीं हो सकता है, या विभिन्न फ़ाइलों में फैला हो सकता है, आदि)। यह तब होता है, उदाहरण के लिए, जब एक समान नाम वाले फ़ंक्शन से कर्तव्यों को लेने के लिए मैक्रो जोड़ते हैं।
BeeOnRope

32

बस के FOOरूप में परिभाषित करें

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

फिर इसे हमेशा टाइप तर्क के आसपास कोष्ठक के साथ लागू करें, जैसे

FOO( (std::map<int, int>), map_var );

यह निश्चित रूप से मैक्रो परिभाषा पर एक टिप्पणी में चालान का उदाहरण देने के लिए एक अच्छा विचार हो सकता है।


यकीन नहीं होता कि यह इतना नीचे क्यों है, यह माइक सेमर्स की तुलना में बहुत अच्छा समाधान है। यह त्वरित और सरल है और पूरी तरह से उपयोगकर्ता से छिपा हुआ है।
फ्रिलिचट

3
@iFreilicht: यह एक साल बाद थोड़ा पोस्ट किया गया था। ;-)
चीयर्स और एचटीटी। - अल्फ

5
और क्योंकि यह भी समझना मुश्किल है कि यह कैसे और क्यों काम करता है
VinGarcia

@VinGarcia, आप बता सकते हैं कि यह क्यों / कैसे काम करता है? इसे बुलाते समय कोष्ठक की आवश्यकता क्यों होती है? UNPACKइस तरह से इस्तेमाल होने पर क्या करें ) UNPACK type name? typeसही तरीके से टाइप क्यों किया जाता है जब इसका इस्तेमाल किया जाता है ) UNPACK type name? बस यहीं क्या हो रहा है?
उपयोगकर्ता

कोई @user, शायद चीयर्स और hth आप जवाब दे सकते हैं
VinGarcia

4

ऐसा करने के लिए कम से कम दो तरीके हैं। सबसे पहले, आप एक मैक्रो को परिभाषित कर सकते हैं जो कई तर्क देता है:

#define FOO2(type1, type2, name) type1, type2, name

यदि आप ऐसा करते हैं, तो आप पा सकते हैं कि आप अधिक तर्कों को संभालने के लिए अधिक मैक्रो को परिभाषित करते हैं।

दूसरा, आप तर्क के आसपास कोष्ठक डाल सकते हैं:

#define FOO(type, name) type name
F00((std::map<int, int>) map_var;

यदि आप ऐसा करते हैं, तो आप पा सकते हैं कि अतिरिक्त कोष्ठक परिणाम के सिंटैक्स को पेंच करते हैं।


पहले समाधान के लिए, प्रत्येक मैक्रो का एक अलग नाम होगा, क्योंकि मैक्रोज़ ओवरलोड नहीं करते हैं। और दूसरे के लिए, यदि आप एक प्रकार के नाम से गुजर रहे हैं, तो एक बहुत अच्छा मौका है कि इसका उपयोग एक चर (या एक टाइपडिफ़) घोषित करने के लिए किया जाएगा, इसलिए कोष्ठक समस्याओं का कारण होगा।
जेम्स कान्जे

4

यह P99 के साथ संभव है :

#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()

उपरोक्त कोड प्रभावी रूप से तर्क सूची में केवल अंतिम अल्पविराम है। के साथ जांचें clang -E(P99 को C99 कंपाइलर की आवश्यकता है)।


3

इसका सरल उत्तर यह है कि आप ऐसा नहीं कर सकते। यह <...>टेम्पलेट तर्कों की पसंद का एक साइड इफेक्ट है ; <और >भी असंतुलित संदर्भों में दिखाई देते हैं तो मैक्रो तंत्र उन्हें संभालने के लिए जैसे कि यह कोष्ठकों संभालती बढ़ाया नहीं जा सका। (समिति के कुछ सदस्यों ने एक अलग टोकन के लिए तर्क दिया था, कहते हैं (^...^), लेकिन वे अधिकांश समस्याओं का उपयोग करने में सक्षम नहीं थे <...>।)


2
(^...^)यह एक खुश चेहरा है :)
CygnusX1
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.