2009-07-10 30 views
5

पर विचार करें निम्नलिखित कोड:सी ++ समारोह कॉल पहचानकर्ता

void Foo() { 
    ...... 
    LOG_ERROR("I'm error 1") // call 1 
    ..... 
    LOG_ERROR("I'm error 2") // call 2 
    ..... 

} 

LOG_ERROR() एक मैक्रो है। LOG_ERROR() को कोड में पहचानने की स्ट्रिंग मुद्रित करनी चाहिए, जबकि धारणा यह है कि कोड बदल सकता है, लेकिन A::Foo() अपरिवर्तित रहेगा। कोड परिवर्तनों के दौरान पहचानकर्ता को बनाए रखना चाहिए।

यह LOG_ERROR(), को तर्क के रूप में जोड़ने त्रुटि कोड द्वारा हल किया जा सकता है, लेकिन हम त्रुटि का प्रबंधन करने के कोड बोझ प्रोग्रामर से निकालना चाहते हैं।

__LINE__ का उपयोग करना कोई जवाब नहीं है, क्योंकि Foo() बिल्ड से निर्माण में स्थानांतरित हो सकता है।

इसलिए मैं LOG_ERROR() रिश्तेदार की पहचान Foo() के शुरू करने के लिए के बारे में सोचा:

  • एक। फ़ाइल नाम (__FILE__) द्वारा पहचानें + फ़ंक्शन नाम (__FUNCTION__) + LOG_ERROR() की लाइन संख्या Foo() से संबंधित है।
  • बी। फ़ाइल नाम (__FILE__) द्वारा पहचानें + फ़ंक्शन नाम (__FUNCTION__) + LOG_ERROR()Foo() में कॉल नंबर।

समाधान कुलपति ++ 2008 और जी ++ 4.1.1 कम से कम के साथ काम किया जाना चाहिए।

एक प्रस्तावित समाधान (link text) है:

#define ENABLE_LOG_ERROR static const int LOG_ERROR_start_line = __LINE__ 
#define LOG_ERROR(s) cerr << "error #" << (__LINE__ - LOG_ERROR_start_line) \ 
    << " in " << __func__ << ": " << s << endl 

void Foo() { 
    ENABLE_LOG_ERROR; 
    //... 
    LOG_ERROR("error 1"); 
    int i; 
    LOG_ERROR("error 2"); 
} 

यह LOG_ERROR() और वहाँ कई इस तरह के कार्यों से युक्त प्रत्येक कार्य की शुरुआत में ENABLE_LOG_ERROR लिखने के लिए उपयोगकर्ता के लिए बाध्य करेगा।

क्या कार्य पूरा करने का कोई और तरीका है?

+0

@idimba, यदि यह बहुत अधिक परेशानी नहीं है, तो आप प्रत्येक फ़ंक्शन नाम के ठीक बाद 'ENABLE_LOG_ERROR;' स्वचालित रूप से जोड़ने के लिए एक स्क्रिप्ट का उपयोग कर सकते हैं। मैं उस चाल का उपयोग करता हूं जब मैं * तृतीय पक्ष कोड का पता लगाता हूं। –

+0

अच्छा विचार है, लेकिन यह __LINE__ को प्रभावित करेगा जिसे हम अपने लॉगिंग उप sustem में व्यापक रूप से उपयोग करते हैं। – dimba

उत्तर

0

स्टैक विचार के संशोधन के द्वारा, std::map का उपयोग std::string से गणना के लिए करें, और फ़ंक्शन नाम देखें।

std::map<std::string, int> LOG_ERROR_count_map; 
#define LOG_ERROR(s) {\ 
    int count = ++LOG_ERROR_count_map[ __func__ ];\ 
    std::cout << count << " in " __func__ ": " s << std::endl;\ 
} 

यह आपको ENABLE_LOG_ERROR की जरूरत नहीं है, लेकिन प्रत्येक लॉग के लिए एक मानचित्र लुक-अप की कीमत पर होता है। (यह आसानी से उपयोग और समय के बीच एक व्यापार बंद है।)

+0

मेरे समाधान और जीएमएन दोनों समारोह में LOG_ERROR का nth उदाहरण नहीं देते हैं, लेकिन समारोह में LOG_ERROR की nth कॉल। इसलिए ब्रांचिंग होने पर उन्हें उपयोगों को आसानी से उपयोग करने के लिए उपयोग नहीं किया जा सकता है (जो शायद इच्छित उपयोग है)। गतिशील गिनती विधि का कोई भी रूप काम नहीं करेगा। –

+0

यह संभवतः स्पष्ट है, लेकिन यह इंगित करने के लायक है कि आप फ़ंक्शन नाम के आधार पर स्थिर गणनाओं के लिए प्रीप्रोसेसर का उपयोग नहीं कर सकते हैं क्योंकि फ़ंक्शन बदलते समय पता लगाने का कोई तरीका नहीं है - तारों की तुलना नहीं की जा सकती है। –

1

यह समाधान अमानक है, लेकिन दोनों MSVC और जीसीसी समर्थन __COUNTER__ है, जो हर बार यह शुरू हो जाती है वृद्धि की जाती है।

#define LOG_ERROR(s) cerr << "error #" << (__COUNTER__) << " in " \ 
<< __func__ << ": " << s << endl 

ध्यान दें कि __COUNTER__ प्रत्येक संकलन इकाई में रीसेट, और केवल एक संकलन इकाई में हो जाएगा। तो यदि Foo() में 7 LOG_ERROR() मैक्रोज़ हैं, बाद में फ़ंक्शन Bar()__COUNTER__ का मान LOG_ERROR() के पहले उपयोग के लिए 7 होगा।

+0

हालांकि यह कार्यों के बीच काम नहीं करेगा। – GManNickG

+0

वास्तविक समस्या यह है कि यदि आप एक नया संदेश डालते हैं, तो नीचे दिए गए सभी को पुनर्नामित किया जाता है। –

+0

@GMan: आप सही हैं कि यह प्रत्येक फ़ंक्शन के लिए रीसेट नहीं होगा। मैंने इसे स्पष्ट करने के लिए संपादित किया है। @ एरविकर: यह भी सही है। यह एक काफी भंगुर समाधान है। यह मानता है कि 'LOG_ERROR() 'को केवल उसी संकलन इकाई में पिछले सभी आमंत्रणों के बाद जोड़ा जाता है। – Geerad

1

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

मान लीजिए कि आप अपने कार्यक्रम की प्रत्येक रिलीज में एक अद्वितीय निर्माण संस्करण एम्बेड कर रहे हैं (जो सामान्य रूप से एक अच्छा विचार है)। आइए यह भी मान लें कि आप एक स्रोत कोड नियंत्रण तंत्र का उपयोग कर रहे हैं जो आपके स्रोत कोड का इतिहास रखता है (जो कि वैसे भी करने का भी एक अच्छा विचार है) और जो आपको स्रोत के साथ प्रस्तुत कर सकता है क्योंकि यह किसी भी अनुरोधित निर्माण संस्करण के लिए था कार्यक्रम।

यदि उन धारणाओं को सत्य माना जाता है, तो एक समाधान यह है कि आपका प्रोग्राम लॉग फ़ाइल में इसका वर्तमान संस्करण लिखना है। फिर प्रत्येक व्यक्तिगत लॉगिंग प्रविष्टि __LINE__ के माध्यम से लाइन नंबर रिकॉर्ड कर सकती है।

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

इसके अलावा, इस तरह से काम करने का लाभ यह है कि यह मानने की आवश्यकता है कि कोई भी कार्य अपरिवर्तित रहेगा, जैसा कि मूल रूप से प्रश्न का हिस्सा था। तो इस विधि का एक बहुत बड़ा आवेदन है।


जहां तक ​​कार्यान्वयन के रूप में चला जाता है, आप या तो लॉग इन कर कार्यक्रम संस्करण जब कार्यक्रम शुरू होता है या आप कर सकते हैं प्रवेश मैक्रो प्रत्येक प्रविष्टि में शामिल।

यदि प्रोग्राम संस्करण सामान्य रूप से कहीं भी सामान्य स्रोत कोड में आसानी से पहुंचा नहीं जाता है, तो आप एक पूर्व-निर्माण चरण बना सकते हैं जो संस्करण निकालेगा और इसे एक साधारण संस्करण.h फ़ाइल में #define या const के रूप में लिख देगा स्ट्रिंग। फिर लॉगिंग कोड या मैक्रो स्वचालित रूप से प्रोग्राम के वर्तमान संस्करण को आउटपुट करने के लिए इसका उपयोग कर सकता है।

संबंधित मुद्दे