กลับจากการเรียกใช้ฟังก์ชันภายในแลมบ์ดา

Lambdas เป็นวิธีที่ยอดเยี่ยมในการสร้างโค้ดที่นำมาใช้ซ้ำได้ภายในฟังก์ชัน/เมธอด โดยไม่กระทบต่อคลาสพาเรนต์ เป็นการทดแทนมาโครแบบ C ที่ใช้งานได้ดีเกือบตลอดเวลา

อย่างไรก็ตาม มีน้ำตาลเชิงวากยสัมพันธ์เล็กน้อยจากมาโครที่ฉันไม่สามารถทำซ้ำด้วยแลมบ์ดาได้ และนั่นคือความสามารถในการออกจากฟังก์ชันที่มีอยู่ ตัวอย่างเช่น หากฉันต้องการส่งคืนพร้อมตรวจสอบช่วงของชุดข้อมูล ints ฉันสามารถทำสิ่งนั้นได้อย่างง่ายดายด้วยมาโคร:

const int xmin(1), xmax(5);
#define CHECK_RANGE(x) { if((x) < xmin || (x) > xmax) return false; }

bool myFunc(int myint) {
    CHECK_RANGE(myint);
    int anotherint = myint + 2;
    CHECK_RANGE(anotherint);
    return true;
}

แน่นอนว่านี่เป็นตัวอย่างที่ง่ายเกินไป แต่หลักพื้นฐานก็คือ ฉันกำลังตรวจสอบตัวแปรต่างๆ ซ้ำแล้วซ้ำอีก และฉันคิดว่าการสรุปการตรวจสอบและทางออกที่เกี่ยวข้องจะอ่านได้ง่ายกว่า อย่างไรก็ตาม ฉันทราบดีว่า มาโครไม่ปลอดภัยนัก โดยเฉพาะอย่างยิ่งเมื่อมีความซับซ้อนมาก . อย่างไรก็ตาม เท่าที่ฉันสามารถบอกได้ การพยายามทำแลมบ์ดาที่เทียบเท่านั้นจำเป็นต้องมีการตรวจสอบเพิ่มเติมที่น่าอึดอัดใจ ดังนี้:

const int xmin(1), xmax(5);
auto check_range = [&](int x) -> bool { return !(x < xmin || x > xmax); };

bool myFunc(int myint) {
    if(!check_range(myint)) return false;
    int anotherint = myint + 2;
    if(!check_range(anotherint)) return false;
    return true;
}

มีวิธีการทำเช่นนี้กับแลมบ์ดาหรือไม่? หรือฉันขาดวิธีแก้ปัญหาอื่นไป?

แก้ไข: ฉันรับรู้ว่า การกลับมาจากภายในมาโครโดยทั่วไปเป็นความคิดที่ไม่ดี เว้นแต่ มีการใช้มาตรการป้องกันที่สำคัญ ฉันแค่สงสัยว่ามันเป็นไปได้หรือไม่


person Phlucious    schedule 31.07.2017    source แหล่งที่มา
comment
มีน้ำตาลเชิงวากยสัมพันธ์อยู่เล็กน้อย - ใช่ เพื่อทำให้โค้ดไม่สามารถเข้าใจได้   -  person    schedule 01.08.2017
comment
ไม่ มันเป็นไปไม่ได้ คงจะดีถ้ามีวิธีการทำเช่นนี้ แต่ตอนนี้ยังไม่มี   -  person Justin    schedule 01.08.2017
comment
ฉันจะเข้าข้างจัสตินก่อนที่จะคิดมากว่า std::longjmp สามารถทำได้อย่างไร ช่วย.   -  person Quentin    schedule 01.08.2017
comment
@Quentin ฉันจะชอบมาโครมากกว่า longjmp แต่จริงๆ แล้ว สิ่งเดียวที่แย่เกี่ยวกับมาโครก็คือ มันไม่ตรวจสอบประเภทและไวยากรณ์ มาโครจะสร้างซอร์สโค้ดที่กระบวนการคอมไพเลอร์ของคุณหลังจากนั้น ไม่มีความไม่ปลอดภัยหากใช้มาโครอย่างถูกต้อง มีข้อผิดพลาดในการเขียนมาโครเช่น พารามิเตอร์ที่ถูกนำมาใช้ซ้ำพร้อมกับตัวดำเนินการส่วนเพิ่ม.. หรือไม่มีพาเรนต์อยู่รอบๆ นิพจน์เหล่านั้น   -  person Swift - Friday Pie    schedule 01.08.2017
comment
คุณสามารถลองตั้งค่านี้โดยมีข้อยกเว้น ซึ่งอาจจะดีกว่า longjmp ทั้งนี้ขึ้นอยู่กับกรณีการใช้งาน   -  person Daniel H    schedule 01.08.2017
comment
อย่างไรก็ตาม มีน้ำตาลเชิงวากยสัมพันธ์อยู่เล็กน้อย... -- เป็นวิธีการสะกดที่ตลกดี   -  person rlbond    schedule 01.08.2017
comment
@ manni66 อาจจะในบางสถานการณ์ แต่ในกรณีนี้ ดูเหมือน มาก ชัดเจนกว่าในสถานการณ์นี้ (สำหรับฉันแล้ว) ในการใช้มาโคร อีกตัวอย่างหนึ่งคือการตรวจสอบสถานะของ iostream หลังจากอ่านทุกครั้ง ดูเหมือนว่าฉันจะอ่านได้ง่ายกว่ามากในการสรุปการอ่านด้วยการตรวจสอบสถานะ โดยเฉพาะอย่างยิ่งเมื่อการเปลี่ยนแปลงในภายหลังจำเป็นต้องแก้ไขทุกการตรวจสอบสถานะ   -  person Phlucious    schedule 01.08.2017
comment
แมโครชื่อ CHECK_RANGE ที่แทรกการส่งคืนลงในโค้ดนั้นไม่สะอาดกว่าแน่นอน   -  person    schedule 01.08.2017
comment
@Swift ใช่แล้ว นั่นเป็นคำแนะนำที่ค่อนข้างลิ้นเพื่อให้คนที่รู้ std::setjmp สามารถทำให้ตัวเองกลัวได้ - ฉันอยากจะใช้มาโครด้วย ;)   -  person Quentin    schedule 01.08.2017


คำตอบ (4)


คุณพูดถูก ไม่มีทางที่ผู้โทรจะกลับจากภายในแลมบ์ดาได้ เนื่องจากสามารถจับแลมบ์ดาและจัดเก็บไว้เพื่อเรียกในภายหลัง จากภายในผู้โทรตามอำเภอใจ การทำเช่นนี้จะส่งผลให้เกิดพฤติกรรมที่คาดเดาไม่ได้

class Foo
{
    Foo(std::function<void(int)> const& callMeLater) : func(callMeLater) {}
    void CallIt(int* arr, int count)
    {
        for (index = count; index--;)
            func(count);
        // do other stuff here.
    }
    std::function<void(int)> func;
};

int main()
{
    auto find3 = [](int arr) 
    {
        if (arr == 3)
            return_from_caller; // making up syntax here.
    };

    Foo foo(find3);
};
person Drew Carlson    schedule 31.07.2017

มีวิธีการทำเช่นนี้กับแลมบ์ดาหรือไม่?

ไม่เหมือนกับมาโครทุกประการ แต่แลมบ์ดาของคุณ แทนที่จะส่งคืน bool สามารถ throw เป็นข้อยกเว้นพิเศษได้ (ตามตัวอย่างประเภท bool)

auto check_range
   = [](int x) { if ( (x < xmin) || (x > xmax) ) throw bool{false}; };

และฟังก์ชัน myFunc() สามารถสกัดกั้นประเภทพิเศษนี้ได้

bool myFunc (int myint)
 {
   try
    {
      check_range(myint);
      int anotherint = myint + 2;
      check_range(anotherint);
      return true;
    }
   catch ( bool e )
    { return e; }
 }

สำหรับการโทร check_range() ครั้งเดียว นี่เป็น (ฉันคิดว่า) เป็นความคิดที่ไม่ดี ถ้าคุณมีสายเข้าเยอะ ฉันคิดว่ามันน่าสนใจ

ต่อไปนี้เป็นตัวอย่างการทำงานแบบเต็ม

#include <iostream>

constexpr int xmin{1}, xmax{5};

auto check_range
   = [](int x) { if ( (x < xmin) || (x > xmax) ) throw bool{false}; };

bool myFunc (int myint)
 {
   try
    {
      check_range(myint);
      int anotherint = myint + 2;
      check_range(anotherint);
      return true;
    }
   catch ( bool e )
    { return e; }
 }

int main ()
 {
   std::cout << myFunc(0) << std::endl; // print 0
   std::cout << myFunc(3) << std::endl; // print 1
   std::cout << myFunc(7) << std::endl; // print 0
 }
person max66    schedule 01.08.2017
comment
แทนที่จะโยน bool โดยทั่วไปแล้ว (และอ่านง่ายกว่า) มักจะชอบมากกว่าที่จะโยน struct/class แบบกำหนดเองแทน โดยเฉพาะอย่างยิ่งการเรียกใช้ที่มาจาก std::exception (เช่น std::out_of_range หรือ std::range_error) จากนั้นจับสิ่งนั้นแทนแล้วใช้ return false โดยตรง - person Remy Lebeau; 01.08.2017
comment
+1 เพราะนี่คือวิธีแก้ปัญหาที่ฉันกำลังมองหา แต่คำตอบที่ได้รับการยอมรับนั้นตอบคำถามได้ดีกว่า - person Phlucious; 01.08.2017

ไม่มีวิธีใดที่จะดีไปกว่าการใช้ค่าส่งคืนของแลมบ์ดาแล้วตามด้วย return จากฟังก์ชันการโทร มาโครเหมาะสำหรับสิ่งนี้

เนื่องจากเป็นภาษา C++ นั่นคือวิธีการออกจากฟังก์ชันที่ใช้เงื่อนไขอื่นเพื่อพิจารณาว่าจะออกหรือไม่

person Curious    schedule 31.07.2017

ไม่ใช่ C++11 แต่ผู้คนได้แฮ็ก C++2a coroutines เพื่อทำสิ่งนี้โดยพื้นฐาน

มันจะมีลักษณะดังนี้:

co_await check_range(foo);

โดยที่คีย์เวิร์ด co_await ระบุว่าในบางกรณี โครูทีนนี้อาจกลับมาก่อนกำหนดพร้อมกับผลลัพธ์ที่ไม่สมบูรณ์ ในกรณีของคุณ ผลลัพธ์ที่ไม่สมบูรณ์นี้อาจเป็นข้อผิดพลาดที่ไม่สามารถดำเนินการต่อได้

การเล่นรอบๆ ที่ฉันเห็นนั้นมีตัวเลือกเสริม และจำเป็นต้องใช้ ptr ที่ใช้ร่วมกัน แต่สิ่งต่างๆ อาจปรับปรุงได้ก่อนที่จะกลายเป็นมาตรฐาน

person Yakk - Adam Nevraumont    schedule 31.07.2017