c++ - Boost.lambda या boost.phoenix से स्थिर कार्य



boost-phoenix boost-proto (1)

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

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

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

यद्यपि एक अशक्त सूचक के माध्यम से एक सदस्य फ़ंक्शन बुला रहा है एक भयानक विचार है (ऐसा मत करो, आप अंधा हो जाएंगे), आप मूल के समान प्रकार के एक नए लैम्ब्डा ऑब्जेक्ट को डिफ़ॉल्ट बना सकते हैं । यदि आप उपरोक्त # 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 के साथ परीक्षण किया गया।

का आनंद लें!

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

परंपरागत कार्यों के समान ये फ़ंक्शंस को स्टोर करने का तरीका सबसे अधिक है: उन्हें boost::function

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

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

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

कोई निश्चित रूप से सी + 11 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. विधि थोड़ा गंदा लगता है, क्या आप किसी भी परिस्थिति के बारे में सोच सकते हैं, या कंपाइलर धारणा जो इस दुर्व्यवहार कर देगा (अपेक्षित व्यवहार: अगर लैम्ब्डा स्टेटलेस है, अन्यथा सेगफ़ॉल)।

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

एरिक निएबलर के उत्तर के बाद संपादित करें:

#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
}