2012-04-23 17 views
12

इनलाइनिंग मैंने पहले function overloading based on whether the arguments are constexpr के बारे में पूछा था। मैं एक मजबूत जोरदार कार्य करने के लिए उस प्रश्न के निराशाजनक उत्तर के आसपास काम करने की कोशिश कर रहा हूं। यह मोटे तौर पर मैं क्या करने की कोशिश कर रहा हूँ है:constexpr, static_assert, और

inline void smart_assert (bool condition) { 
    if (is_constexpr (condition)) 
     static_assert (condition, "Error!!!"); 
    else 
     assert (condition); 
} 

असल में, यह विचार है कि एक संकलन समय जांच हमेशा एक रन-टाइम की जांच अगर यह संकलन समय पर जांच करने के लिए संभव है की तुलना में बेहतर है। हालांकि, इनलाइनिंग और निरंतर फोल्डिंग जैसी चीजों के कारण, मैं हमेशा यह नहीं जानता कि संकलन समय जांच संभव है या नहीं। इसका मतलब है कि ऐसे मामले हो सकते हैं जहां assert (condition)assert(false) तक संकलित हो जाएं और कोड बस मुझे चलाने के लिए इंतजार कर रहा है और मुझे पता चलने से पहले उस पथ को निष्पादित करने में कोई त्रुटि है।

इसलिए, अगर यह जांचने का कोई तरीका है कि स्थिति एक कॉन्स्टेक्सप्रस है (इनलाइनिंग या अन्य अनुकूलन के कारण), तो संभव हो तो मैं static_assert पर कॉल कर सकता हूं, और अन्यथा रन-टाइम पर जोर देता हूं। सौभाग्य से, जीसीसी में आंतरिक __builtin_constant_p (exp) है, जो exp एक कॉन्स्टेक्सर है, जो सच हो जाता है। मुझे नहीं पता कि अन्य कंपाइलर्स के पास यह आंतरिक है, लेकिन मैं उम्मीद कर रहा था कि इससे मेरी समस्या हल हो जाएगी।

#include <cassert> 
#undef IS_CONSTEXPR 

#if defined __GNUC__ 
    #define IS_CONSTEXPR(exp) __builtin_constant_p (exp) 
#else 
    #define IS_CONSTEXPR(exp) false 
#endif 
// TODO: Add other compilers 

inline void smart_assert (bool const condition) { 
    static_assert (!IS_CONSTEXPR(condition) or condition, "Error!!!"); 
    if (!IS_CONSTEXPR(condition)) 
     assert (condition); 
} 

#undef IS_CONSTEXPR 

static_assertor के शॉर्ट सर्किट व्यवहार पर निर्भर करता है: यह कोड है कि मैं के साथ आया है। यदि IS_CONSTEXPR सत्य है, तो static_assert का उपयोग किया जा सकता है, और स्थिति !true or condition है, जो कि condition के समान है। यदि IS_CONSTEXPR गलत है, तो static_assert का उपयोग नहीं किया जा सकता है, और स्थिति !false or condition है, जो true के समान है और static_assert को अनदेखा किया गया है। यदि static_assert चेक नहीं किया जा सकता है क्योंकि condition कोई कॉन्स्टेक्स नहीं है, तो मैं अंतिम कोड के रूप में अपने कोड में रन-टाइम assert जोड़ता हूं। हालांकि, यह काम नहीं करता है, not being able to use function arguments in a static_assert, even if the arguments are constexpr के लिए धन्यवाद।

विशेष रूप से, यह है कि अगर मैं जीसीसी के साथ संकलन करने की कोशिश क्या होता है:

// main.cpp 
int main() { 
    smart_assert (false); 
    return 0; 
} 

g++ main.cpp -std=c++0x -O0

सब कुछ ठीक है, सामान्य रूप से संकलित करता है। अनुकूलन के साथ कोई इनलाइनिंग नहीं है, इसलिए IS_CONSTEXPR गलत है और static_assert अनदेखा किया गया है, इसलिए मुझे बस रन-टाइम assert कथन (जो विफल रहता है) मिलता है। हालांकि,

[[email protected] test]$ g++ main.cpp -std=c++0x -O1 
In file included from main.cpp:1:0: 
smart_assert.hpp: In function ‘void smart_assert(bool)’: 
smart_assert.hpp:12:3: error: non-constant condition for static assertion 
smart_assert.hpp:12:3: error: ‘condition’ is not a constant expression 

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

यह smart_assert बनाने जैसा प्रतीत नहीं होता है जैसे फ़ंक्शन-जैसी मैक्रो सामान्य स्थिति में समस्या का समाधान करेगा। यह स्पष्ट रूप से इस सरल उदाहरण में काम करेगा, लेकिन condition कॉल ग्राफ़ के दो स्तरों पर एक फ़ंक्शन से आ सकता है (लेकिन अभी भी संकलक के कारण constexpr के रूप में संकलक के रूप में जाना जाता है), जो कि एक का उपयोग करने की एक ही समस्या में चलता है static_assert में फ़ंक्शन पैरामीटर।

+0

शुरू यह संभव है एक मैक्रो निर्धारित करता है कि एक अभिव्यक्ति एक वर्ग परिभाषा का विस्तार करने के इस तरह के एक मैक्रो की जरूरत नहीं होगी मानक C++ –

+0

में लगातार @ JohannesSchaub-litb है कि क्या लिखने के लिए ? यह 'constexpr' फ़ंक्शन के अंदर प्रयोग योग्य नहीं होगा। – Potatoswatter

+0

@pot इसे कक्षा परिभाषा में विस्तार करने की आवश्यकता क्यों होगी? –

उत्तर

7

स्पष्ट अच्छा है, सामान्य रूप से अंतर्निहित बुरा है।

प्रोग्रामर हमेशा static_assert का प्रयास कर सकता है।

यदि संकलन समय पर स्थिति का मूल्यांकन नहीं किया जा सकता है, तो यह विफल हो जाता है, और प्रोग्रामर को assert में बदलना होगा।

आप एक सामान्य रूप प्रदान करके इसे करना आसान बना सकते हैं ताकि परिवर्तन उदा। STATIC_ASSERT(x+x == 4)DYNAMIC_ASSERT(x+x == 4), बस एक नामकरण।

कहा यही कारण है, के बाद से अपने मामले में आप केवल प्रोग्रामर ’ समय कि अगर अनुकूलन उपलब्ध, यानी है जब से तुम शायद सब compilers के साथ हमेशा एक ही परिणाम प्राप्त करने के बारे में ’ टी देखभाल डॉन, आप कर सकते थे की एक अनुकूलन चाहते हैं हमेशा कुछ और नरक की तरह कोशिश करें;

#include <iostream> 
using namespace std; 

void foo(void const*) { cout << "compile time constant" << endl; } 
void foo(...) { cout << "hm, run time,,," << endl; } 

#define CHECK(e) cout << #e << " is "; foo(long((e)-(e))) 

int main() 
{ 
    int x = 2134; 
    int const y  = 2134; 

    CHECK(x); 
    CHECK(y); 
} 

यदि आप करते हैं, तो कृपया हमें बताएं कि यह कैसे बाहर निकला है।

नोट: उपरोक्त कोड एमएसवीसी 10.0 और जी ++ 4.6 के साथ विभिन्न परिणाम उत्पन्न करता है।


अद्यतन: मैंने सोचा कैसे के बारे में कैसे काम करता है इसके बाद के संस्करण कोड, इतने सारे upvotes मिला टिप्पणी। मैंने सोचा कि शायद वह कुछ कह रहा है जिसे मैं समझ में नहीं आता। इसलिए मैंने ओपी के काम को करने के लिए तैयार किया, यह जांचकर कि विचार कैसा रहा।

इस बिंदु मुझे लगता है कि अगर constexpr समारोह बात हो जी के साथ काम करने के लिए किया जा सकता है पर ++, तो यह संभव नहीं तो, केवल अन्य compilers के लिए जी ++ के लिए भी समस्या को हल करने, है।

उपर्युक्त है जहां तक ​​मुझे g ++ समर्थन मिला है। यह प्रस्तुत किए गए विचार का उपयोग करके, विज़ुअल सी ++ के लिए यह अच्छी तरह से काम करता है (ओपी की समस्या हल करता है)। लेकिन नहीं के साथ जी ++:

जी के साथ ++ समस्या का
#include <assert.h> 
#include <iostream> 
using namespace std; 

#ifdef __GNUC__ 
    namespace detail { 
     typedef double (&Yes)[1]; 
     typedef double (&No)[2]; 

     template< unsigned n > 
     Yes foo(char const (&)[n]); 

     No foo(...); 
    } // namespace detail 

    #define CASSERT(e)          \ 
     do {             \ 
      char a[1 + ((e)-(e))];        \ 
      enum { isConstExpr = sizeof(detail::foo(a)) == sizeof(detail::Yes) }; \ 
      cout << "isConstExpr = " << boolalpha << !!isConstExpr << endl; \ 
      (void)(isConstExpr? 1/!!(e) : (assert(e), 0)); \ 
     } while(false) 
#else 
    namespace detail { 
     struct IsConstExpr 
     { 
      typedef double (&YesType)[1]; 
      typedef double (&NoType)[2]; 

      static YesType check(void const*); 
      static NoType check(...); 
     }; 
    } // namespace detail 

    #define CASSERT(e)           \ 
     do {              \ 
      enum { isConstExpr =         \ 
       (sizeof(detail::IsConstExpr::check(e - e)) == \ 
        sizeof(detail::IsConstExpr::YesType)) };  \ 
      (void)(isConstExpr? 1/!!(e) : (assert(e), 0));  \ 
     } while(false) 
#endif 

int main() 
{ 
#if defined(STATIC_TRUE) 
    enum { x = true }; 
    CASSERT(x); 
    cout << "This should be displayed, OK." << endl; 
#elif defined(STATIC_FALSE) 
    enum { x = false }; 
    CASSERT(x); 
    cerr << "!This should not even have compiled." << endl; 
#elif defined(DYNAMIC_TRUE) 
    bool x = true; 
    CASSERT(x); 
    cout << "This should be displayed, OK." << endl; 
#elif defined(DYNAMIC_FALSE) 
    bool x = false; 
    CASSERT(x); 
    cout << "!Should already have asserted." << endl; 
#else 
    #error "Hey, u must define a test case symbol." 
#endif 
} 

उदाहरण:

 
[D:\dev\test] 
> g++ foo.cpp -Werror=div-by-zero -D DYNAMIC_FALSE 

[D:\dev\test] 
> a 
isConstExpr = true 
!Should already have asserted. 

[D:\dev\test] 
> _ 

है, जी ++ रिपोर्ट (यहां तक ​​कि उसके अंतर्निहित समारोह के माध्यम से, और यहां तक ​​कि wrt VLA बनाने या नहीं।) एक गैर कि - const` चर जो कि यह मान जानता है, निरंतर है, लेकिन फिर यह उस ज्ञान को पूर्णांक विभाजन के लिए लागू करने में विफल रहता है, ताकि वह चेतावनी उत्पन्न करने में विफल रहता है।

अरग।


अद्यतन 2: ठीक है मैं बेवकूफ हूँ: निश्चित रूप से मैक्रो सिर्फ एक साधारण assert किसी भी मामले में वहाँ है करने के लिए जोड़ सकते हैं। चूंकि ओपी केवल स्थिर स्थिरता प्राप्त करने में रूचि रखता है, जब यह उपलब्ध है, जो कुछ कोने मामलों में g ++ के लिए नहीं है। समस्या हल हो गई, और मूल रूप से हल किया गया था।

+7

जी ++ में पार्सर/अर्थशास्त्रीय विश्लेषण में आक्रामक निरंतर फोल्डिंग है, जिसका अर्थ है कि '(ई) - (ई) 'तुरंत' 0' 'का मूल्यांकन किया जाता है और इस प्रकार इसे शून्य सूचक मूल्य के रूप में उपयोग करने योग्य माना जाता है ... भले ही यह अर्थात् यह है 'int'। हालांकि क्लैंग 'x' केस को फोल्ड नहीं करेगा, और 'int' को निश्चित रूप से सूचक मूल्य में परिवर्तित नहीं किया जा सकता है, इसलिए' x' नहीं है। –

+0

@downvoter: कृपया अपने डाउनवोट को समझाएं, ताकि अन्य इसे अनदेखा कर सकें –

+0

@MatthieuM .: हाँ, यह पता लगाने के लिए कि यह कोड पूरी तरह से है, कोड को क्या करने के लिए डिज़ाइन किया गया है। मुझे समझ में नहीं आ रहा है कि लोगों ने आपकी टिप्पणी क्यों उभरी है, जब तक कि वे रेडडिट बच्चे न हों। यह पागल है –

1

अन्य प्रश्न का उत्तर निराशाजनक कैसे है? यह लगभग सटीक रूप से लागू होता है जो आप वर्तमान में वर्णन करते हैं, सिवाय इसके कि संकलक नैदानिक ​​संदेश में पाठ को प्रिंट करता है।

कारण यह throw साथ किया जाना चाहिए एक संदर्भ जो रनटाइम पर मूल्यांकन किया जा सकता है में constexpr की कि संकलन समय मूल्यांकन वैकल्पिक है।उदाहरण के लिए, कार्यान्वयन आपको डीबगिंग मोड में constexpr कोड से आगे बढ़ने का विकल्प चुन सकता है।

constexpr कार्यों की एक कमजोर विशेषता (घोषणा विनिर्देशक) है कि फ़ंक्शन का उपयोग करके अभिव्यक्ति के परिणामस्वरूप को बदल नहीं सकता है। यह गारंटी है कि रनटाइम पर अर्थपूर्ण अर्थ संकलन समय पर तय किया गया है, लेकिन आपको एक विशेष संकलन-समय शॉर्टकट निर्दिष्ट करने की अनुमति नहीं देता है।

अमान्य शर्तों को चिह्नित करने के लिए के रूप में, throw एक उपसूचक जो एक स्थिर अभिव्यक्ति, जब ?:, &&, या || की unevaluated पक्ष में छिपा को छोड़कर के रूप में अमान्य है। भाषा गारंटी देता है कि इसे संकलित समय पर ध्वजांकित किया जाएगा, भले ही डीबगर आपको रनटाइम पर इसके माध्यम से कदम उठाए, और केवल तभी ध्वज वास्तव में ट्रिगर हो।

भाषा को यहां चीज़ें मिलती हैं। दुर्भाग्य से static_assert की विशेष नैदानिक ​​संदेश सुविधा के साथ मेल नहीं किया जा सकता है या constexpr -ness पर ब्रांचिंग नहीं किया जा सकता है।

+0

मुझे कुछ गलत समझना चाहिए, क्योंकि मुझे समझ में नहीं आता कि किसी अन्य उत्तर ने मेरी समस्या का समाधान कैसे किया है। मैं नहीं देखता कि कैसे फेंकने का उपयोग मेरी समस्या हल करता है, या तो, क्योंकि मैं अभी भी 'static_assert' का उपयोग नहीं कर सकता, जो कि संपूर्ण बिंदु है। 'Static_assert' के बिना, मेरे पास सामान्य 'जोर' /' फेंक 'है। –

8

यह मदद करनी चाहिए आप

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) { 
    return t; 
} 

#define isprvalconstexpr(e) noexcept(makeprval(e)) 
+0

विज़ुअल सी ++ अभी तक 'constexpr' का समर्थन नहीं करता है (सीईपी 2012 के रूप में, दृश्य सी ++ 11.0 के साथ)। इस प्रकार, अभी तक यह थोड़ा अव्यवहारिक है ... –

+3

बहुत अच्छा जोहान्स। यह क्यों काम करता है? क्या प्रत्येक constexpr एक rvalue संदर्भ है? नाम क्यों? पीआर क्या है बनाते हैं? वैल? क्या कोई बूल वापस नहीं करता है? ठीक है, मैं बिना किसी विचार को देखता हूं, लेकिन किसी भी विस्तार को आभारी रूप से प्राप्त किया जाएगा। इसमें आपके लिए एक अपवर्त है;) – user2023370

+1

यह क्लैंग के साथ काम नहीं करता है। क्या आप वाकई सही हैं? – user2023370

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