मैक्रो का उपयोग करके स्रोत फ़ाइल लाइनों की गणना करें?


15

क्या स्रोत फ़ाइल के भीतर लाइनों को गिनने के लिए C / C ++ प्रीप्रोसेसर का उपयोग करना संभव है, किसी मैक्रो या किसी प्रकार के संकलन-समय-उपलब्ध मूल्य में? जैसे मैं प्रतिस्थापित कर सकता हूं MAGIC1, MAGIC2और MAGIC3निम्नलिखित में, और उपयोग करते समय मूल्य 4 किसी भी तरह प्राप्त कर सकता हूं MAGIC3?

MAGIC1 // can be placed wherever you like before the relevant 
       // lines - either right before them, or in global scope etc.
foo(); MAGIC2
bar(); MAGIC2
baz(); MAGIC2
quux(); MAGIC2
// ... possibly a bunch of code here; not guaranteed to be in same scope ...
MAGIC3

टिप्पणियाँ:

  • प्रीप्रोसेसर की क्षमताओं के लिए कम्पाइलर-विशिष्ट एक्सटेंशन स्वीकार्य हैं लेकिन अवांछनीय हैं।
  • यदि यह C ++ के निर्माण के विपरीत C ++ की मदद से ही संभव है, तो यह स्वीकार्य भी है, लेकिन अवांछनीय (यानी मैं ऐसा कुछ चाहूंगा जो C के लिए काम करेगा)।
  • जाहिर है यह कुछ बाहरी प्रोसेसर स्क्रिप्ट के माध्यम से स्रोत फ़ाइल को चलाकर किया जा सकता है, लेकिन यह वह नहीं है जो मैं पूछ रहा हूं।

6
वहाँ एक मैक्रो कहा जाता है__LINE__ कि वर्तमान लाइन संख्या का प्रतिनिधित्व करता है
फोर्सब्रु

2
खोज रहे हैं __COUNTER__और / या BOOST_PP_COUNTER?
कामिलुक

11
आपको हल करने के लिए वास्तविक समस्या क्या है ? आप इसकी आवश्यकता क्यों है?
कुछ प्रोग्रामर

1
क्या यह मदद करता है ?
user1810087

1
@PSkocik: मुझे कुछ चाहिए जो मैं एक संकलन-समय स्थिर के रूप में उपयोग कर सकता हूं, जैसे कि कहने के लिए int arr[MAGIC4]और मेरे कोड के कुछ पहले से गिने हुए खंड में लाइनों की संख्या प्राप्त करें।
ईनपोकलम

जवाबों:


15

नहीं है __LINE__पूर्वप्रक्रमक मैक्रो लाइन के लिए पर दिखाई दिया है जो आपको एक पूर्णांक देता है। आप कुछ लाइन पर इसके मूल्य को ले सकते हैं, और फिर बाद में कुछ लाइन और तुलना कर सकते हैं।

static const int BEFORE = __LINE__;
foo();
bar();
baz();
quux();
static const int AFTER = __LINE__;
static const int COUNT = AFTER - BEFORE - 1; // 4

यदि आप स्रोत लाइनों के बजाय कुछ की घटनाओं को गिनना चाहते हैं, __COUNTER__तो एक गैर-मानक विकल्प हो सकता है, जिसे कुछ संकलक जैसे कि जीसीसी और एमएसवीसी द्वारा समर्थित है ।

#define MAGIC2_2(c)
#define MAGIC2(c) MAGIC2_2(c)
static const int BEFORE = __COUNTER__;
void foo(); MAGIC2(__COUNTER__);
void bar(
    int multiple,
    float lines); MAGIC2(__COUNTER__);
void baz(); MAGIC2(__COUNTER__);
void quux(); MAGIC2(__COUNTER__);
static const int AFTER = __COUNTER__;
static const int COUNT = AFTER - BEFORE - 1; // 4

मैंने प्रारंभिक मूल्य लिया __COUNTER__क्योंकि इसका उपयोग पहले स्रोत फ़ाइल में किया जा सकता था, या कुछ में हेडर शामिल थे।

C ++ के बजाय C में निरंतर चर पर सीमाएँ हैं, इसलिए enumइसके बजाय इसका उपयोग किया जा सकता है।

enum MyEnum
{
    FOO = COUNT // C: error: enumerator value for ‘FOO’ is not an integer constant
};

के साथ कास्ट की जगह enum:

enum {BEFORE = __LINE__};
foo();
bar();
baz();
quux();
enum { COUNT = __LINE__ - BEFORE - 1};
enum MyEnum
{
    FOO = COUNT // OK
};

मुझे लगता है कि यह निकटतम के बारे में है जैसा कि आप सिर्फ प्रीप्रोसेसर के साथ प्राप्त कर सकते हैं। प्रीप्रोसेसर एक-पास है, इसलिए आप बाद में गणना किए गए मान को बैकपैक नहीं कर सकते हैं, लेकिन वैश्विक-चर संदर्भ काम करेंगे और उसी का अनुकूलन करना चाहिए। वे केवल पूर्णांक स्थिरांक अभिव्यक्तियों पर काम नहीं करेंगे, लेकिन कोड की संरचना करना संभव हो सकता है ताकि उन लोगों के लिए गणना की आवश्यकता न हो।
PSkocik

2
__COUNTER__C या C ++ में मानक नहीं है। यदि आप जानते हैं कि यह विशेष संकलक के साथ काम करता है, तो उन्हें निर्दिष्ट करें।
पीटर

@einpoklum नहीं, BEFOREऔर AFTERमैक्रोज़ नहीं हैं
एलन

इस समाधान के गैर-काउंटर संस्करण के साथ एक समस्या है: स्रोत लाइनों के रूप में पहले और बाद में केवल उसी दायरे में उपयोग करने योग्य हैं। यह दिखाने के लिए मेरे "उदा" स्निपेट को संपादित किया कि यह एक मुद्दा है।
ईनपोकलम

1
@ user694733 सच प्रश्न को टैग किया गया था [C ++]। सी एनम स्थिरांक के लिए काम करते हैं।
फायर लांसर

9

मुझे पता है कि ओपी का अनुरोध मैक्रोज़ का उपयोग करना है, लेकिन मैं ऐसा करने का एक और तरीका जोड़ना चाहूंगा जिसमें मैक्रोज़ का उपयोग करना शामिल नहीं है।

C ++ 20 उस source_locationवर्ग का परिचय देता है जो स्रोत कोड, जैसे फ़ाइल नाम, लाइन नंबर और फ़ंक्शन नामों के बारे में कुछ जानकारी का प्रतिनिधित्व करता है। हम इस मामले में बहुत आसानी से उपयोग कर सकते हैं।

#include <iostream>
#include <source_location>

static constexpr auto line_number_start = std::source_location::current().line();
void foo();
void bar();
static constexpr auto line_number_end = std::source_location::current().line();

int main() {
    std::cout << line_number_end - line_number_start - 1 << std::endl; // 2

    return 0;
}

और यहाँ जीवंत उदाहरण है


मैक्रोज़ के बिना मैक्रोज़ से भी बेहतर है। हालाँकि - इस दृष्टिकोण के साथ, मैं केवल उसी गणना में रेखा गणना का उपयोग कर सकता हूं, जिस रेखा को मैंने गिना है। इसके अलावा - source_locationC ++ 20 में प्रयोगात्मक होने के साथ?
ईनपोकलुम

मैं मानता हूँ कि मैक्रोज़ के बिना समाधान मैक्रोज़ की तुलना में कहीं बेहतर है। source_locationअब आधिकारिक तौर पर C ++ 20 का हिस्सा है। चेक यहाँ । मैं सिर्फ gbol संकलक का संस्करण godbolt.org पर नहीं पा सका, जो पहले से ही गैर प्रयोगात्मक अर्थों में इसका समर्थन करता है। क्या आप कृपया अपने कथन को थोड़ा और स्पष्ट कर सकते हैं - मैं केवल उसी गणना में रेखा गणना का उपयोग कर सकता हूं जिस रेखा को मैंने गिना है ?
नटक्रैकर

मान लीजिए कि मैंने आपके सुझाव को एक फ़ंक्शन के भीतर रखा (यानी गिनाई गई लाइनें इनवोकेशन हैं, घोषणाएं नहीं)। यह काम करता है - लेकिन मेरे पास केवल line_number_startऔर line_number_endइस दायरे में, कहीं और नहीं है। यदि मैं इसे कहीं और चाहता हूं तो मुझे इसे रन-टाइम में पारित करने की आवश्यकता है - जो उद्देश्य को हरा देता है।
ईनपोक्लुम

उदाहरण देखें कि मानक यहां क्या प्रदान करता है । यदि यह डिफ़ॉल्ट तर्क है, तो यह अभी भी संकलन-समय का हिस्सा है, है ना?
नटक्रैकर

हां, लेकिन यह line_number_endइसके दायरे के बाहर संकलन-समय पर दिखाई नहीं देता है। यदि मैं गलत हूं तो मुझे सही करों।
ईनपोकलम

7

पूर्णता के लिए: यदि आप MAGIC2प्रत्येक पंक्ति के बाद जोड़ना चाहते हैं , तो आप उपयोग कर सकते हैं __COUNTER__:

#define MAGIC2 static_assert(__COUNTER__ + 1, "");

/* some */     MAGIC2
void source(); MAGIC2
void lines();  MAGIC2

constexpr int numberOfLines = __COUNTER__;

int main()
{
    return numberOfLines;
}

https://godbolt.org/z/i8fDLx (रिटर्न 3)

आप इसे शुरू और अंत के मूल्यों को संग्रहीत करके पुन: प्रयोज्य बना सकते हैं __COUNTER__

कुल मिलाकर यह वास्तव में बोझिल है। आप उन पंक्तियों की भी गणना नहीं कर पाएंगे जिनमें प्रीप्रोसेसर निर्देश हैं या जो //टिप्पणियों के साथ समाप्त होते हैं । मैं __LINE__इसके बजाय उपयोग करना चाहता हूं , अन्य उत्तर देखें।


1
आप उपयोग क्यों करते हैं static_assert?
आईडीक्लेव ४६३०३५18१

1
यह स्रोत फ़ाइल में "9" दिया मैंने इसे गिरा दिया, आप यह नहीं मान सकते __COUNTER__कि अभी भी शुरू में शून्य है अन्य हेडर के रूप में, आदि इसका उपयोग कर सकते हैं।
फायर लांसर

आपको के मूल्य का उपयोग करना होगा __COUNTER__ दो बार होगा और अंतर लेना होगा
idclev 463035818

1
@ formerlyknownas_463035818 __COUNTER__ की अनुमति नहीं होगी, और इसे किसी चीज़ के लिए विस्तारित करने की आवश्यकता है या इसकी गणना नहीं होगी (मैं इस पर नियमों को 100% याद नहीं रख सकता)।
फायर लांसर

7

एक और अधिक मजबूत समाधान, विभिन्न काउंटरों के लिए अनुमति देता है (जब तक वे रुक-रुक कर नहीं आते हैं, और __COUNTER__अन्य कार्यों के लिए कोई उपयोग नहीं होता है):

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)

#define COUNT_THIS_LINE static_assert(__COUNTER__ + 1, "");
#define START_COUNTING_LINES(count_name) \
  enum { EXPAND_THEN_CONCATENATE(count_name, _start) = __COUNTER__ };
#define FINISH_COUNTING_LINES(count_name) \
  enum { count_name = __COUNTER__ - EXPAND_THEN_CONCATENATE(count_name, _start) - 1 };

यह कार्यान्वयन विवरण छुपाता है (हालांकि यह उन्हें मैक्रोज़ के अंदर छुपाता है ...)। यह @ MaxLanghof के उत्तर का सामान्यीकरण है। ध्यान दें कि __COUNTER__जब हम एक गिनती शुरू करते हैं तो एक गैर-शून्य मान हो सकता है।

यहाँ इसका उपयोग कैसे किया जाता है:

START_COUNTING_LINES(ze_count)

int hello(int x) {
    x++;
    /* some */     COUNT_THIS_LINE
    void source(); COUNT_THIS_LINE
    void lines();  COUNT_THIS_LINE
    return x;
}

FINISH_COUNTING_LINES(ze_count)

int main()
{
    return ze_count;
}

साथ ही, यह मान्य C है - यदि आपका पूर्वप्रक्रमक समर्थन करता है __COUNTER__, अर्थात।

गॉडबोल्ट पर काम करता है

यदि आप C ++ का उपयोग कर रहे हैं, तो आप इस समाधान को संशोधित कर सकते हैं ताकि वैश्विक नेमस्पेस को प्रदूषित न किया जा सके - काउंटरों को भीतर namespace macro_based_line_counts { ... }, या namespace detailआदि में रखकर ।


5

अपनी टिप्पणी के आधार पर, यदि आप C या C ++ में a (संकलन-समय) सरणी आकार निर्दिष्ट करना चाहते हैं, तो आप कर सकते हैं

int array[]; //incomplete type
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
/*lines to be counted; may use array (though not sizeof(array)) */
/*...*/
int array[ __LINE__ - LINE0 ]; //complete the definition of int array[]

यदि आपको sizeof(array)हस्तक्षेप करने वाली लाइनों में आवश्यकता होती है, तो आप इसे एक स्थिर चर संदर्भ के साथ बदल सकते हैं (जब तक कि इसे पूर्णांक स्थिर अभिव्यक्ति होने की आवश्यकता न हो) और एक अनुकूलन करने वाले कंपाइलर को इसे एक ही मानना ​​चाहिए (स्थिर चर की आवश्यकता को समाप्त करना चाहिए) याद में)

int array[]; static int count;
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
//... possibly use count here
enum { LINEDIFF = __LINE__ - LINE0 }; 
int array[ LINEDIFF ]; /*complete the definition of int array[]*/ 
static int count = LINEDIFF; //fill the count in later

__COUNTER__ आधारित समाधान (यदि वह विस्तार उपलब्ध है) के रूप में एक- __LINE__आधारित के विपरीत एक ही काम करेगा।

constexprC ++ में s को साथ ही साथ काम करना चाहिए enum, लेकिन enumसादे C में भी काम करेगा (मेरा समाधान ऊपर एक सादे C का समाधान है)।


यह तभी काम करेगा जब लाइन-काउंट का मेरा उपयोग गिने-चुने लाइनों के समान दायरे में हो। IIANM। नोट मैंने एक समस्या हो सकती है पर जोर देने के लिए अपने प्रश्न को थोड़ा संपादित किया।
ईनपोक्लुम

1
@einpoklum एक __COUNTER__आधारित समाधान में भी समस्याएँ हैं: आपको बेहतर उम्मीद है कि आपका मैजिक मैक्रो एकमात्र उपयोगकर्ता है __COUNTER__, कम से कम इससे पहले कि आप इसका उपयोग करें __COUNTER__। समस्या मूल रूप से सभी सरल तथ्यों के लिए आती है जो __COUNTER__/__LINE__प्रीप्रोसेसर फीचर्स हैं और प्रीप्रोसेसर एक पास में काम करता है, इसलिए आप पूर्णांक स्थिर अभिव्यक्ति को बाद में __COUNTER__/ के आधार पर बैकपैक नहीं कर सकते हैं __LINE__। एकमात्र तरीका (सी में कम से कम) पहली जगह में आवश्यकता से बचने के लिए है, उदाहरण के लिए, आकार के बिना आगे की सरणी घोषणाओं का उपयोग करके (अपूर्ण टाइप किए गए ऐलान)।
PSkocik

1
रिकॉर्ड के लिए, \ प्रभावित नहीं करता है __LINE__- यदि कोई लाइन ब्रेक है, तो __LINE__बढ़ जाती है। उदाहरण 1 , उदाहरण 2
मैक्स लैंगहॉफ

@MaxLanghof धन्यवाद। इसका एहसास नहीं हुआ। फिक्स्ड।
PSkocik
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.