2015-08-15 5 views
9

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

__thiscall थोड़ा महंगा है इसलिए जब भी संभव हो मैं इसे टालना चाहूंगा, और मैं लैम्ब्डा प्रकार के आधार पर विभिन्न कॉल जनरेशन फ़ंक्शंस का उपयोग करने से बचना चाहूंगा।

मैंने टेम्पलेट्स और एसएफआईएनएई के माध्यम से लैम्ब्डा प्रकार का पता लगाने के कई तरीकों की कोशिश की और सभी असफल रहे।

गैर-कैप्चरिंग लैम्बडास में ::operator function_type* है जो कि उन्हें फ़ंक्शन पॉइंटर्स में बदलने के लिए उपयोग कर सकते हैं, जबकि लैम्बडास कैप्चरिंग नहीं करते हैं।

प्रासंगिक सी ++ कल्पना: http://en.cppreference.com/w/cpp/language/lambda

कोई भी विचार?

संपादित मैं एक समाधान है कि बनाम 2013/2015 के लिए काम करता है करना चाहते हैं, जीसीसी और बजना

टेस्ट कोड इस प्रकार

#include <utility> 

    //this doesn't work 
    template < class C, class T > 
    struct HasConversion { 
     static int test(decltype(std::declval<C>().operator T*, bool()) bar) { 
      return 1; 
     } 

     static int test(...) { 
      return 0; 
     } 
    }; 

    template <class C> 
    void lambda_pointer(C lambda) { 
     int(*function)() = lambda; 

     printf("Lambda function: %p without context\n", function); 
    } 

    template <class C> 
    void lambda_pointer_ctx(C lambda) { 
     int(C::*function)() const = &C::operator(); 

     void* context = &lambda; 

     printf("Lambda function: %p with context: %p\n", function, context); 
    } 

    int main() { 
     int a; 

     auto l1 = [] { 
      return 5; 
     }; 

     auto l2 = [a] { 
      return a; 
     }; 


     //non capturing case 

     //works as expected 
     lambda_pointer(l1); 

     //works as expected (ctx is meaningless and not used) 
     lambda_pointer_ctx(l1); 



     //lambda with capture (needs context) 

     //fails as expected 
     lambda_pointer(l1); 

     //works as expected (ctx is a pointer to class containing the captures) 
     lambda_pointer_ctx(l1); 

     /* 
     //this doesn't work :< 
     typedef int afunct() const; 

     HasConversion<decltype(l1), afunct>::test(0); 
     HasConversion<decltype(l2), afunct>::test(0); 
     */ 


     return 0; 
    } 
+0

आप लैम्ब्डा के हस्ताक्षर को जानते हो? यदि आप ऐसा करते हैं तो यह थोड़ा क्लीनर बनाता है। – Yakk

+0

यदि आप हस्ताक्षर जानते थे, तो आप 'std :: is_assignable {}', या, अपने मामले में 'std :: is_assignable {} '' typedef void afunct (int) के साथ; ' –

+0

नहीं, इसे किसी भी हस्ताक्षर के लिए काम करना है - मैं –

उत्तर

5

आप एक समारोह आप चाहते हैं अपने लैम्ब्डा के लिए परिवर्तित किया के हस्ताक्षर जानते हैं, आप लाभ उठा सकते हैं std::is_assignable विशेषता:

auto lambda = [] (char, double) -> int { return 0; }; 
using signature = int(char, double); 
static_assert(std::is_assignable<signature*&, decltype(lambda)>::value, "!"); 

DEMO

इस तरह यह जेनेरिक लैम्बडास के साथ भी काम कर सकता है।


मैं एक समाधान है कि बनाम 2013/2015, जीसीसी और बजना

के लिए काम करता है करने के लिए आप हस्ताक्षर नहीं जानते, तो चाहते हैं, यहाँ एक दृष्टिकोण है कि यह जांचने का विकल्प है कि + एक अंतर्निहित रूपांतरण ट्रिगर करता है या नहीं। यह std::is_assignable परीक्षण का शोषण करता है और यह सत्यापित करता है कि लैम्ब्डा फ़ंक्शन कॉल ऑपरेटर के समान हस्ताक्षर के साथ एक लम्बाडा फ़ंक्शन पॉइंटर को असाइन करने योग्य है या नहीं। (यूनरी ऑपरेटर प्लस के साथ एक परीक्षण की तरह, यह जेनेरिक लैम्बडास के साथ काम नहीं करता है। लेकिन सी ++ में कोई सामान्य लैम्बडा नहीं है)।

#include <type_traits> 

template <typename T> 
struct identity { using type = T; }; 

template <typename...> 
using void_t = void; 

template <typename F> 
struct call_operator; 

template <typename C, typename R, typename... A> 
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {}; 

template <typename C, typename R, typename... A> 
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {}; 

template <typename F> 
using call_operator_t = typename call_operator<F>::type; 

template <typename, typename = void_t<>> 
struct is_convertible_to_function 
    : std::false_type {}; 

template <typename L> 
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>> 
    : std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {}; 

टेस्ट:

int main() 
{ 
    auto x = [] { return 5; }; 
    auto y = [x] { return x(); }; 

    static_assert(is_convertible_to_function<decltype(x)>::value, "!"); 
    static_assert(!is_convertible_to_function<decltype(y)>::value, "!"); 
} 

GCC, VC++, Clang++

+1

मैं एक समान (लेकिन कम अच्छी तरह लिखित) समाधान का उपयोग कर समाप्त हुआ। बेहतर पोर्टेबिलिटी के कारण स्वीकार करना। –

2

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

template<class...> using void_t = void; 

template <class T, class = void> 
struct has_capture : std::true_type {}; 

template <class T> 
struct has_capture<T, void_t<decltype(+std::declval<T>())>> : std::false_type {}; 

int main() { 
    auto f1 = []{}; 
    auto f2 = [&f1]{}; 

    static_assert(!has_capture<decltype(f1)>{}, ""); 
    static_assert(has_capture<decltype(f2)>{}, ""); 
} 
+0

परेशान, एमएसवीसी के एक संस्करण ने इसे तोड़ दिया: यह कई कॉलिंग सम्मेलनों की आपूर्ति करता है, और यूनरी '+ 'अस्पष्ट हो गया। 2015 पूर्व रिलीज शायद? डुनो अगर यह भेज दिया। – Yakk

+0

@Yakk कोई विचार नहीं है, लेकिन यदि यह सच है, तो यह बेकार है:/ – Quentin

+0

यह वास्तव में वीएस 2013 पर टूट गया है, –

3

HasConversion दृष्टिकोण जो आप जा रहे हैं वह C++ 03 से होल्डओवर है। test के ओवरलोड के अलग-अलग रिटर्न प्रकारों का उपयोग करने का विचार था (उदाहरण के लिए, char और दूसरा long) लौटाएं और जांचें कि रिटर्न प्रकार के sizeof() आपकी अपेक्षाओं से मेल खाते हैं।

एक बार जब हम सी ++ 11 पर हों, तो बहुत बेहतर तरीके हैं। हम, उदाहरण के लिए, void_t उपयोग कर सकते हैं:

template <typename... > 
using void_t = void; 

एक प्रकार विशेषता लिखने के लिए:

template <typename T, typename = void> 
struct has_operator_plus : std::false_type { }; 

template <typename T> 
struct has_operator_plus<T, 
    void_t<decltype(+std::declval<T>())>> 
: std::true_type { }; 

int main() { 
    auto x = []{ return 5; }; 
    auto y = [x]{ return x(); }; 

    std::cout << has_operator_plus<decltype(x)>::value << std::endl; // 1 
    std::cout << has_operator_plus<decltype(y)>::value << std::endl; // 0 
} 
+3

सी ++ 17 तक प्रतीक्षा नहीं कर सकता: 'टेम्पलेट अवधारणा बूल has_operator_plus = आवश्यक है (टी टी) { + t;}; ' – Columbo

+0

अफसोस की बात है, ऐसा लगता है कि बनाम 2013 (और 2015) पर कुछ समस्या है जो इसे हमेशा वापस कर देता है 1. हालांकि यह जीसीसी 5.2 पर परीक्षण करते समय सही तरीके से काम करता है। Void_t चाल के लिए Thx! मैं अभी तक सी ++ 11 पैटर्न के बराबर नहीं हूं, यहां तक ​​कि मेरा सी ++ 03 भी बहुत जंगली है। –

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