लैम्ब्डा के भीतर C ++ लैम्ब्डा फ़ंक्शन का पता कैसे प्राप्त करें?


53

मैं यह पता लगाने की कोशिश कर रहा हूं कि लैम्बडा फ़ंक्शन का पता कैसे प्राप्त किया जाए। यहाँ एक नमूना कोड है:

[]() {
    std::cout << "Address of this lambda function is => " << ????
}();

मुझे पता है कि मैं एक चर में लैम्ब्डा को कैप्चर कर सकता हूं और पता प्रिंट कर सकता हूं, लेकिन मैं इसे उस स्थान पर करना चाहता हूं जब यह अनाम फ़ंक्शन निष्पादित हो रहा हो।

क्या ऐसा करने का एक सरल तरीका है?


24
क्या यह सिर्फ जिज्ञासा के लिए है, या एक अंतर्निहित समस्या है जिसे आपको हल करने की आवश्यकता है? यदि कोई अंतर्निहित समस्या है, तो कृपया (हमारे लिए) अज्ञात समस्या के एक एकल संभावित समाधान के बारे में पूछने के बजाय सीधे इसके बारे में पूछें।
कुछ प्रोग्रामर ने

41
... XY- समस्या की प्रभावी रूप से पुष्टि।
नीलजर्न 11

8
आप लैंबडा को मैन्युअल रूप से लिखे गए फ़नकार वर्ग के साथ बदल सकते हैं, और फिर उपयोग कर सकते हैं this
होलीब्लैककट

28
"अपने भीतर एक लांबा फ़ंक्शन का पता प्राप्त करना" एक समाधान है , एक समाधान जिस पर आप ध्यान केंद्रित करते हैं। अन्य समाधान हो सकते हैं, जो बेहतर हो सकते हैं। लेकिन हम आपकी मदद नहीं कर सकते क्योंकि हम नहीं जानते कि वास्तविक समस्या क्या है। हम यह भी नहीं जानते कि आप किस पते के लिए उपयोग करेंगे। मैं जो भी करने की कोशिश कर रहा हूं वह आपकी वास्तविक समस्या के साथ मदद करना है।
कुछ प्रोग्रामर

8
@Someprogrammerdude जबकि आप जो कह रहे हैं, उसमें से अधिकांश समझदार है, मुझे " एक्स एक्स कैसे किया जा सकता है?" पूछने के साथ कोई समस्या नहीं दिखती है । X यहां "अपने भीतर से एक लंबोदर का पता प्राप्त कर रहा है"। इससे कोई फर्क नहीं पड़ता कि आपको पता नहीं है कि पते का उपयोग किस लिए किया जाएगा और इससे कोई फर्क नहीं पड़ता कि "बेहतर" समाधान हो सकता है, किसी और की राय में जो अज्ञात कोड आधार में संभव हो सकता है या नहीं हो सकता है ( हमें)। एक बेहतर विचार केवल बताई गई समस्या पर ध्यान देना है। यह या तो उल्लेखनीय है या यह नहीं है। अगर है, तो कैसे ? यदि नहीं, तो यह उल्लेख नहीं है और कुछ और सुझाव दिया जा सकता है, IMHO।
code_dredd

जवाबों:


32

यह सीधे संभव नहीं है।

हालाँकि, लैम्ब्डा कैप्चर कक्षाएं हैं और एक वस्तु का पता इसके पहले सदस्य के पते के साथ मेल खाता है। इसलिए, यदि आप पहले कैप्चर के रूप में मान द्वारा एक वस्तु को कैप्चर करते हैं, तो पहले कैप्चर का पता लंबोदर के पते से मेल खाता है:

int main() {
    int i = 0;
    auto f = [i]() { printf("%p\n", &i); };
    f();
    printf("%p\n", &f);
}

आउटपुट:

0x7ffe8b80d820
0x7ffe8b80d820

वैकल्पिक रूप से, आप एक डेकोरेटर डिज़ाइन पैटर्न लैम्बडा बना सकते हैं जो लैम्बडा कैप्चर के संदर्भ को इसके कॉल ऑपरेटर में पास करता है:

template<class F>
auto decorate(F f) {
    return [f](auto&&... args) mutable {
        f(f, std::forward<decltype(args)>(args)...);
    };
}

int main() {
    auto f = decorate([](auto& that) { printf("%p\n", &that); });
    f();
}

15
"किसी वस्तु का पता उसके पहले सदस्य के पते से मेल खाता है" क्या यह कहीं निर्दिष्ट है कि कैप्चर का आदेश दिया गया है, या कि कोई अदृश्य सदस्य नहीं हैं?
एन। 'सर्वनाम' मी।

35
@ n.'pronouns'm। नहींं, यह एक गैर-पोर्टेबल समाधान है। कैप्चर कार्यान्वयन संभावित रूप से सदस्यों को पैडिंग को कम से कम करने के लिए सबसे बड़े से लेकर छोटे स्तर तक आदेश दे सकता है, मानक स्पष्ट रूप से इसके लिए अनुमति देता है।
मैक्सिम इगोरुस्किन 14

14
पुन:, "यह एक गैर-पोर्टेबल समाधान है।" यह अपरिभाषित व्यवहार
सोलोमन स्लो

1
@ruohola हार्ड बताने के लिए। "ऑब्जेक्ट का पता उसके पहले सदस्य के पते से मेल खाता है" मानक-लेआउट प्रकारों के लिए सही है । यदि आपने परीक्षण किया कि क्या मेमना का प्रकार यूबी को लागू किए बिना मानक-लेआउट था, तो आप इसके बाद यूबी को प्रभावित किए बिना कर सकते थे। परिणामी कोड में कार्यान्वयन-निर्भर व्यवहार होगा। बस पहली वैधता का परीक्षण किए बिना चाल चल रहा है, हालांकि, यूबी।
बेन वोइगट

4
मेरा मानना ​​है कि यह अनिर्दिष्ट है , जैसा कि .2.१.५.२, १५ के अनुसार: जब
एरब्योर का कहना है कि मोनिका

51

लैम्ब्डा के भीतर लैम्ब्डा ऑब्जेक्ट का पता प्राप्त करने का कोई तरीका नहीं है।

अब, जैसा कि होता है यह काफी उपयोगी है। पुनरावृत्ति करने के लिए सबसे आम उपयोग है।

उन y_combinatorभाषाओं से आता है जहाँ आप अपने बारे में बात नहीं कर सकते जब तक आप परिभाषित नहीं करते हैं। इसे में बहुत आसानी से लागू किया जा सकता है :

template<class F>
struct y_combinator {
  F f;
  template<class...Args>
  decltype(auto) operator()(Args&&...args) const {
    return f( f, std::forward<Args>(args)... );
  }
  template<class...Args>
  decltype(auto) operator()(Args&&...args) {
    return f( f, std::forward<Args>(args)... );
  }
};

अब आप यह कर सकते हैं:

y_combinator{ [](auto& self) {
  std::cout<<"Address of this lambda function is => "<< &self;
} }();

इसके विभिन्न रूपों में शामिल हो सकते हैं:

template<class F>
struct y_combinator {
  F f;
  template<class...Args>
  decltype(auto) operator()(Args&&...args) const {
    return f( *this, std::forward<Args>(args)... );
  }
  template<class...Args>
  decltype(auto) operator()(Args&&...args) {
    return f( *this, std::forward<Args>(args)... );
  }
};

जहां पहले तर्क के रूप selfमें पारित किए बिना पारित किया जा सकता selfहै।

दूसरा वास्तविक वाई कॉम्बिनेटर (उर्फ तय बिंदु कॉम्बीनेटर) से मेल खाता है, मेरा मानना ​​है। जो आप चाहते हैं वह इस बात पर निर्भर करता है कि 'लैम्बडा के पते' से आपका क्या मतलब है।


3
वाह, वाई कॉम्बीनेटर आपके सिर को लिस्प / जावास्क्रिप्ट / पायथन जैसी गतिशील-टाइप भाषाओं में चारों ओर लपेटने के लिए काफी कठिन हैं। मैंने कभी नहीं सोचा था कि मैं सी ++ में एक देखूंगा।
जेसन एस

13
मुझे ऐसा लगता है कि यदि आप C ++ में ऐसा करते हैं तो आप गिरफ्तार होने लायक हैं
user541686

3
@MSalters अनिश्चित। यदि Fमानक लेआउट y_combinatorनहीं है, तो नहीं है, इसलिए लेन की गारंटी प्रदान नहीं की जाती है।
यक्क - एडम नेवेरुमोंट 14

2
@ शीर्ष उत्तर वहाँ तभी काम करता है जब आपका लैम्ब्डा स्कोप में रहता है, और आपको ओवरहैड ओवरहेड टाइप करने में कोई आपत्ति नहीं है। तीसरा उत्तर है वाई कॉम्बिनेटर। दूसरा उत्तर एक मैनुअल यॉम्बिनेटर है।
यक्क - एडम नेवरामॉन्ट

2
@kaz C ++ 17 फीचर। 11/14 में आप एक मेक फंक्शन लिखेंगे, जो F को डेडिकेट करेगा; 17 में आप टेम्पलेट नाम (और कभी कभी कटौती गाइड) के साथ यह मान सकते हैं
Yakk - एडम Nevraumont

25

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

तब आप thisबिना किसी फ़ंफ़र वाले को एक चर में असाइन किए बिना भी पता प्राप्त कर सकते हैं :

#include <iostream>

class Functor
{
public:
    void operator()() {
        std::cout << "Address of this functor is => " << this;
    }
};

int main()
{
    Functor()();
    return 0;
}

आउटपुट:

Address of this functor is => 0x7ffd4cd3a4df

इसका यह फायदा है कि यह 100% पोर्टेबल है, और इसके बारे में तर्क करने और समझने में बेहद आसान है।


9
फ़नकार को लंबोदर की तरह भी घोषित किया जा सकता है:struct { void operator()() { std::cout << "Address of this functor is => " << this << '\n'; } } f;
इरॉनस

-1

लंबोदर को पकड़ें:

std::function<void ()> fn = [&fn]() {
  std::cout << "My lambda is " << &fn << std::endl;
}

1
एक के लचीलेपन की std::functionयहाँ आवश्यकता नहीं है, और यह काफी लागत पर आता है। साथ ही, उस ऑब्जेक्ट को कॉपी / मूव करना उसे तोड़ देगा।
Deduplicator

@ डेड्यूप्लिकेटर की आवश्यकता क्यों नहीं है, क्योंकि यह एकमात्र एवरर है जो मानक-अनुरूप है? कृपया एक ऐसी एवर दें जो काम करे और जिसे std :: function की जरूरत न हो।
विंसेंट फोरमंड

यह एक बेहतर और स्पष्ट समाधान प्रतीत होता है, जब तक कि एकमात्र बिंदु लैम्ब्डा का पता प्राप्त करना नहीं है (जो कि खुद से बहुत मतलब नहीं है)। एक सामान्य उपयोग का मामला पुनरावृत्ति उद्देश्य के लिए स्वयं के अंदर लैम्ब्ला तक पहुंचना होगा। उदाहरण के लिए देखें: stackoverflow.com/questions/2067988/… जहां फ़ंक्शन के रूप में घोषणात्मक विकल्प को समाधान के रूप में व्यापक रूप से स्वीकार किया गया था :)
आरईआरटी

-6

यह संभव है लेकिन अत्यधिक प्लेटफॉर्म और कंपाइलर ऑप्टिमाइज़ेशन पर निर्भर करता है।

मुझे पता है कि ज्यादातर आर्किटेक्चर पर, रजिस्टर पॉइंटर कहा जाता है। जब हम फ़ंक्शन के अंदर होते हैं, तो इस समाधान का बिंदु इसे निकालना है।

Amd64 पर निम्नलिखित कोड आपको फ़ंक्शन एक के करीब पते देना चाहिए।

#include <iostream>

void* foo() {
    void* n;
    asm volatile("lea 0(%%rip), %%rax"
      : "=a" (n));
    return n;
}

auto boo = [](){
    void* n;
    asm volatile("lea 0(%%rip), %%rax"
       : "=a" (n));
    return n;
};

int main() {
    std::cout<<"foo"<<'\n'<<((void*)&foo)<<'\n'<<foo()<<std::endl;  
    std::cout<<"boo"<<'\n'<<((void*)&boo)<<'\n'<<boo()<<std::endl;
}

लेकिन उदाहरण के लिए gcc https://godbolt.org/z/dQXmHm पर -O3ऑप्टिमाइज़ेशन लेवल फंक्शन इनलाइन हो सकता है।


2
मैं उत्थान करना पसंद करूंगा, लेकिन मैं बहुत ज्यादा नहीं हूं और समझ नहीं पा रहा हूं कि यहां क्या हो रहा है। यह कैसे काम करता है तंत्र की कुछ व्याख्या वास्तव में मूल्यवान होगी। इसके अलावा, " फ़ंक्शन के करीब पते" से आपका क्या मतलब है ? क्या कोई निरंतर / अपरिभाषित ऑफसेट है?
R2RT

2
@majkrzak यह "सही" उत्तर नहीं है, क्योंकि यह सभी पोस्ट का सबसे कम पोर्टेबल है। यह स्वयं मेमने का पता वापस करने की गारंटी नहीं है।
अनाम

यह बताता है, लेकिन "यह संभव नहीं है" उत्तर गलत है asnwer
majkrzak

निर्देश सूचक का उपयोग स्वचालित या thread_localसंग्रहण अवधि वाली वस्तुओं के पते प्राप्त करने के लिए नहीं किया जा सकता है । यहां आप जो पाने की कोशिश कर रहे हैं, वह फ़ंक्शन का रिटर्न एड्रेस है, ऑब्जेक्ट नहीं। लेकिन यहां तक ​​कि यह काम नहीं करेगा क्योंकि कंपाइलर जेनरेट किया गया फंक्शन प्रोलॉग स्टैक को पुश करता है और स्टैक पॉइंटर को एडजस्ट करता है ताकि दूसरे वैरिएबल के लिए जगह बनाई जा सके।
मैक्सिम एगोरुस्किन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.