2012-04-13 6 views
8

मैं नियमित रूप से C++ में लैम्ब्डा फ़ंक्शंस को परिभाषित करने के लिए boost.lambda (और फीनिक्स) का उपयोग करता हूं। मुझे वास्तव में उनकी बहुलक संपत्ति, उनके प्रतिनिधित्व की सादगी और जिस तरह से वे सी ++ में कार्यात्मक प्रोग्रामिंग करते हैं, उतना ही आसान है। कुछ मामलों में, यह छोटे कार्यों को परिभाषित करने और स्थिर दायरे में नाम देने के लिए उनका उपयोग करने के लिए भी क्लीनर और अधिक पठनीय (यदि आप उन्हें पढ़ने के लिए उपयोग किया जाता है) है।boost.lambda या boost.phoenix से स्टेटिक फ़ंक्शंस

इन functionals स्टोर करने के लिए जिस तरह से है कि पारंपरिक कार्यों जैसा दिखता है सबसे उन्हें एक boost::function

const boost::function<double(double,double)> add = _1+_2; 

में कब्जा करने के लिए है लेकिन समस्या यह ऐसा करने का क्रम अक्षमता है। भले ही add फ़ंक्शन स्टेटलेस है, लौटा हुआ लैम्ब्डा प्रकार खाली नहीं है और इसकी sizeof 1 से अधिक है (इसलिए boost::function डिफ़ॉल्ट सीटीआर और कॉपी सीटीआर में new शामिल होगा)। मैं वास्तव में संदेह संकलक या बढ़ावा की ओर से एक तंत्र इस statelessness का पता लगाने और कोड जो का उपयोग कर के बराबर है उत्पन्न करने के लिए यह है कि:

double (* const add)(double,double) = _1+_2; //not valid right now 

एक निश्चित रूप से C++ 11 auto इस्तेमाल कर सकते हैं चर, लेकिन फिर गैर-टेम्पलेट संदर्भों के आसपास पारित नहीं किया जा सकता है।

#include <boost/lambda/lambda.hpp> 
using namespace boost::lambda; 

#include <boost/type_traits.hpp> 
#include <boost/utility/result_of.hpp> 
using namespace boost; 


template <class T> 
struct static_lambda { 

    static const T* const t; 

    // Define a static function that calls the functional t 
    template <class arg1type, class arg2type> 
    static typename result_of<T(arg1type,arg2type)>::type 
     apply(arg1type arg1,arg2type arg2){ 
     return (*t)(arg1,arg2); 
    } 

    // The conversion operator 
    template<class func_type> 
    operator func_type*() { 
     typedef typename function_traits<func_type>::arg1_type arg1type; 
     typedef typename function_traits<func_type>::arg2_type arg2type; 
     return &static_lambda<T>::apply<arg1type,arg2type>; 
    } 
}; 

template <class T> 
const T* const static_lambda<T>::t = 0; 

template <class T> 
static_lambda<T> make_static(T t) {return static_lambda<T>();} 

#include <iostream> 
#include <cstdio> 


int main() { 
    int c=5; 
    int (*add) (int,int) = make_static(_1+_2); 
    // We can even define arrays with the following syntax 
    double (*const func_array[])(double,double) = {make_static(_1+_2),make_static(_1*_2*ref(c))}; 
    std::cout<<func_array[0](10,15)<<"\n"; 
    std::fflush(stdout); 
    std::cout<<func_array[1](10,15); // should cause segmentation fault since func_array[1] has state 
} 

जीसीसी 4.6.1 इस कार्यक्रम से उत्पादन के साथ संकलित (अनुकूलन स्तर की परवाह किए बिना) है:

25 
Segmentation fault 
मैं अंत में लगभग मैं क्या करना चाहते हैं, तो निम्न दृष्टिकोण का उपयोग कर प्रबंधित किया है अपेक्षित के रूप में

। यहां, मैं लैम्ब्डा अभिव्यक्ति प्रकार (ऑप्टिमाइज़ेशन उद्देश्यों के लिए यथासंभव कॉन्स) के लिए स्थिर सूचकांक रख रहा हूं और इसे NULL पर प्रारंभ कर रहा हूं। इस तरह, यदि आप राज्य के साथ लैम्ब्डा अभिव्यक्ति को "स्थिर" करने का प्रयास करते हैं, तो आप रनटाइम त्रुटि प्राप्त कर सकते हैं। और यदि आप वास्तव में एक स्टेटलेस लैम्ब्डा अभिव्यक्ति को स्थिर करते हैं, तो सब कुछ काम करता है।

प्रश्न (ओं) को पर:

  1. विधि थोड़ा गंदा लगता है, आप किसी भी परिस्थिति, या संकलक धारणा है कि इस दुर्व्यवहार कर देगा के बारे में सोच सकते हैं (अपेक्षित व्यवहार: अच्छा काम करता है, तो लैम्ब्डा है स्टेटलेस, segfault अन्यथा)।

  2. आप किसी भी तरह से सोच सकते हैं कि इस प्रयास कर जब लैम्ब्डा अभिव्यक्ति राज्य है एक संकलक त्रुटि के बजाय एक segfault का कारण होगा? एरिक Niebler के जवाब के बाद

संपादित करें:

#include <boost/phoenix.hpp> 
using namespace boost::phoenix; 
using namespace boost::phoenix::arg_names; 

#include <boost/type_traits.hpp> 
#include <boost/utility/result_of.hpp> 
using boost::function_traits; 

template <class T> 
struct static_lambda { 
    static const T t; 

    // A static function that simply applies t 
    template <class arg1type, class arg2type> 
    static typename boost::result_of<T(arg1type,arg2type)>::type 
    apply(arg1type arg1,arg2type arg2){ 
    return t(arg1,arg2); 
    } 

    // Conversion to a function pointer 
    template<class func_type> 
    operator func_type*() { 
    typedef typename function_traits<func_type>::arg1_type arg1type; 
     typedef typename function_traits<func_type>::arg2_type arg2type; 
     return &static_lambda<T>::apply<arg1type,arg2type>; 
    } 
}; 

template <class T> 
const T static_lambda<T>::t; // Default initialize the functional 

template <class T> 
static_lambda<T> make_static(T t) {return static_lambda<T>();} 

#include <iostream> 
#include <cstdio> 


int main() { 
    int (*add) (int,int) = make_static(_1+_2); 

    std::cout<<add(10,15)<<"\n"; 

    int c=5; 

    // int (*add_with_ref) (int,int) = make_static(_1+_2+ref(c)); causes compiler error as desired 
} 
+1

आईआईआरसी, यदि आप फीनिक्स का उपयोग कर रहे हैं, तो आप परिणाम को 'बूस्ट :: फ़ंक्शन' के बजाय 'बूस्ट :: फीनिक्स :: फ़ंक्शन' के अंदर स्टोर कर सकते हैं और कुछ दक्षता हानि को कम कर सकते हैं ('boost :: फीनिक्स: : फंक्शन 'पीओडी प्रकार हैं और संकलित समय पर स्थैतिक रूप से प्रारंभ किया जा सकता है)। – ildjarn

+0

@ildjarn 'boost :: फीनिक्स :: फ़ंक्शन' के बारे में प्रमुखों के लिए धन्यवाद, जो कई मामलों में उपयोगी होने के लिए बाध्य है। मैं अभी भी देशी समकक्ष (रनटाइम-प्रदर्शन के अनुसार) लैम्ब्डा फ़ंक्शन कैप्चरिंग प्राप्त करने में रूचि रखता हूं। मुझे यकीन नहीं है कि यह उत्पादन गुणवत्ता बनाने के लिए संभव है, लेकिन मुझे पीछा दिलचस्प लगता है। – enobayram

उत्तर

11
  1. इस क्लीनर बनाने के लिए कोई तरीका नहीं है। आप एक नल सूचक के माध्यम से एक सदस्य समारोह बुला रहे हैं। यह सभी प्रकार के अपरिभाषित व्यवहार है, लेकिन आप पहले से ही जानते हैं।
  2. आप नहीं जानते कि बूस्ट। लैम्ब्डा फ़ंक्शन स्टेटलेस है या नहीं। यह एक काला बॉक्स है। Boost.Phoenix एक अलग कहानी है। यह Boost.Proto, एक डीएसएल टूलकिट पर बनाया गया है, और फीनिक्स अपनी व्याकरण प्रकाशित करता है और आप लैम्ब्डा भाव उत्पन्न आत्मनिरीक्षण करने के लिए हुक देता है। आप काफी आसानी से स्टेटफुल टर्मिनलों के लिए लग रही है और अगर यह किसी भी पाता संकलन समय पर बाहर बम से उड़ाने की एक आद्य एल्गोरिथ्म लिख सकते हैं। (लेकिन यह मेरा उत्तर # 1 ऊपर नहीं बदलता है।)

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

=== अद्यतन ===

हालांकि एक अशक्त सूचक के माध्यम से एक सदस्य फ़ंक्शन को कॉल एक भयानक विचार है (यह मत करो, तुम अंधे जायेंगे), तो आप कर सकते हैं default- एक ही प्रकार के मूल के नई लैम्ब्डा ऑब्जेक्ट का निर्माण करें। यदि आप उपरोक्त # 2 में अपने सुझाव के साथ गठबंधन करते हैं, तो आप जो भी प्राप्त कर सकते हैं उसे प्राप्त कर सकते हैं।

#include <iostream> 
#include <type_traits> 
#include <boost/mpl/bool.hpp> 
#include <boost/mpl/and.hpp> 
#include <boost/phoenix.hpp> 

namespace detail 
{ 
    using namespace boost::proto; 
    namespace mpl = boost::mpl; 

    struct is_stateless 
     : or_< 
      when<terminal<_>, std::is_empty<_value>()>, 
      otherwise< 
       fold<_, mpl::true_(), mpl::and_<_state, is_stateless>()> 
      > 
     > 
    {}; 

    template<typename Lambda> 
    struct static_lambda 
    { 
     template<typename Sig> 
     struct impl; 

     template<typename Ret, typename Arg0, typename Arg1> 
     struct impl<Ret(Arg0, Arg1)> 
     { 
      static Ret apply(Arg0 arg0, Arg1 arg1) 
      { 
       return Lambda()(arg0, arg1); 
      } 
     }; 

     template<typename Fun> 
     operator Fun*() const 
     { 
      return &impl<Fun>::apply; 
     } 
    }; 

    template<typename Lambda> 
    inline static_lambda<Lambda> make_static(Lambda const &l) 
    { 
     static_assert(
      boost::result_of<is_stateless(Lambda)>::type::value, 
      "Lambda is not stateless" 
     ); 
     return static_lambda<Lambda>(); 
    } 
} 

using detail::make_static; 

int main() 
{ 
    using namespace boost::phoenix; 
    using namespace placeholders; 

    int c=5; 
    int (*add)(int,int) = make_static(_1+_2); 

    // We can even define arrays with the following syntax 
    static double (*const func_array[])(double,double) = 
    { 
     make_static(_1+_2), 
     make_static(_1*_2) 
    }; 
    std::cout << func_array[0](10,15) << "\n"; 
    std::cout << func_array[1](10,15); 

    // If you try to create a stateless lambda from a lambda 
    // with state, you trigger a static assertion: 
    int (*oops)(int,int) = make_static(_1+_2+42); // ERROR, not stateless 
} 

अस्वीकरण:: मैं फीनिक्स के लेखक नहीं हूँ यहाँ कोड है। मुझे नहीं पता कि सभी स्टेटलेस लैम्ब्स के लिए डिफ़ॉल्ट-निर्माण योग्यता की गारंटी है या नहीं।

एमएसवीसी -10.0 के साथ परीक्षण किया गया।

आनंद लें!

+2

+1, आधिकारिक उत्तर की तरह कुछ भी नहीं है। : -] – ildjarn

+0

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

+2

सावधान रहें। यह न केवल स्टेटलेस फीनिक्स लैम्बडास है जो डिफ़ॉल्ट-रचनात्मक हैं; आपका समाधान लैम्बडा को नहीं पकड़ पाएगा जो स्थानीय लोगों को मूल्य से कैप्चर करता है, जब तक कि उन स्थानीय लोगों के प्रकार स्वयं डिफॉल्ट-रचनात्मक होते हैं। आपको वास्तव में ऊपर लिखा गया 'is_stateless' प्रोटो एल्गोरिदम का उपयोग करने की आवश्यकता है। –

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