c++ - লাম্বদা ধরণের একটি ব্যতিক্রম ধরা কি সম্ভব?




exception lambda (3)

যদিও std::exception শ্রেণি থেকে প্রাপ্ত কেবলমাত্র ব্যতিক্রমগুলি ছুঁড়ে ফেলা ভাল অনুশীলন, তবে সি ++ কিছু ফেলে দেওয়া সম্ভব করে। নীচের সমস্ত উদাহরণ বৈধ সি ++:

throw "foo";  // throws an instance of const char*
throw 5;      // throws an instance of int

struct {} anon;
throw anon;   // throws an instance of not-named structure

throw []{};   // throws a lambda!

শেষ উদাহরণটি আকর্ষণীয়, কারণ এটি কোনও পৃথক শ্রেণি বা ফাংশন সংজ্ঞায়িত না করে কিছু কোড পাসের জায়গায় কার্যকর করতে অনুমতি দেয়।

তবে কোনও ল্যাম্বডা (বা একটি বন্ধ) ধরা কি আদৌ সম্ভব? catch ([]{} e) কাজ করে না।


আপনি একটি std::function নিক্ষেপ এবং ধরতে পারেন:

#include <iostream>
#include <functional>

void f() {
        throw std::function<void(void)>([]{std::cout << "lambda\n"; });
}

int main()
{
        try{ f(); }
        catch( std::function<void(void)> &e)
        {
                e();
                std::cout << "catch\n";
        }
}

আউটপুট:

lambda
catch

একটি ল্যাম্বদা একটি অনন্য বেনামি ধরণের। ল্যাম্বডা ইনস্ট্যান্টের ধরণের নাম রাখার একমাত্র উপায় হ'ল এটি একটি ভেরিয়েবলে সংরক্ষণ করা, তারপরে সেই পরিবর্তনশীল প্রকারে একটি decltype করুন।

কয়েকটি উপায় রয়েছে যা আপনি নিক্ষিপ্ত ল্যাম্বডাকে ধরতে পারবেন।

try  {
  throw []{};
} catch(...) {
}

এই ক্ষেত্রে আপনি এটি আবার ছোঁড়া ছাড়া অন্যটি ব্যবহার করতে পারবেন না।

try  {
  throw +[]{};
} catch(void(*f)()) {
}

একটি স্টেটলেস ল্যাম্বডা একটি ফাংশন পয়েন্টারে রূপান্তরিত হতে পারে।

try  {
  throw std::function<void()>([]{});
} catch(std::function<void()> f) {
}

আপনি এটি একটি std::function রূপান্তর করতে পারেন। std::function হ'ল এটি বৃহত্তর ল্যাম্বডাসের জন্য বরাদ্দ দেয়, যা তাত্ত্বিকভাবে এটি নিক্ষেপ করতে পারে।

আমরা সেই গাদা বরাদ্দটি দূর করতে পারি:

template<class Sig>
struct callable;

template<class R, class...Args>
struct callable<R(Args...)> {
  void* state = nullptr;
  R(*action)(void*, Args&&...) = nullptr;
  R operator()(Args...args) const {
    return action( state, std::forward<Args>(args)... );
  }
};

template<class Sig, class F>
struct lambda_wrapper;
template<class R, class...Args, class F>
struct lambda_wrapper<R(Args...), F>
:
  F,
  callable<R(Args...)>
{
  lambda_wrapper( F fin ):
    F(std::move(fin)),
    callable<R(Args...)>{
      static_cast<F*>(this),
      [](void* self, Args&&...args)->R {
        return static_cast<R>( (*static_cast<F*>(self))( std::forward<Args>(args)... ) );
      }
    }
  {}
  lambda_wrapper(lambda_wrapper && o):
    F(static_cast<F&&>(o)),
    callable<R(Args...)>( o )
  {
    this->state = static_cast<F*>(this);
  }
  lambda_wrapper& operator=(lambda_wrapper && o)
  {
    static_cast<F&>(*this) = (static_cast<F&&>(o));
    static_cast<callable<R(Args...)>&>(*this) = static_cast<callable<R(Args...)>&>( o );
    this->state = static_cast<F*>(this);
  }
};

template<class Sig, class F>
lambda_wrapper<Sig, F> wrap_lambda( F fin ) {
  return std::move(fin);
}

এখন আপনি করতে পারেন:

try {
  throw wrap_lambda<void()>([]{});
} catch( callable<void()> const& f ) {
}

callable std::function চেয়ে "হালকা ওজন" টাইপ এররেজোর কারণ এটি নতুন হিপ মেমরি বরাদ্দ করতে পারে না।

সরাসরি উদাহরণ


সি ++ আপনাকে কিছু ফেলে দিতে দেয় allows এবং এটি আপনাকে যা ছুঁড়ে তা ধরতে দেয়। আপনি অবশ্যই একটি ল্যাম্বডা ফেলতে পারেন। একমাত্র সমস্যাটি হ'ল, কোনও কিছু ধরার জন্য আপনাকে সেই কিছুর টাইপ বা কমপক্ষে কোনও পিতামাতার ধরণেরটি জানতে হবে। যেহেতু ল্যাম্বডাস একটি সাধারণ বেস থেকে উদ্ভূত হয় না, তাই আপনার ল্যাম্বদা ধরার জন্য আপনার ল্যাম্বডার ধরণটি জানতে হবে। এটির সাথে মুখ্য বিষয়টি হ'ল প্রতিটি ল্যাম্বডা এক্সপ্রেশন আপনাকে একটি স্বতন্ত্র ধরণের মূল্য দেয়। এর অর্থ হ'ল আপনার থ্রো এবং আপনার ক্যাচ উভয়ই একই ল্যাম্বডা অভিব্যক্তির উপর ভিত্তি করে হওয়া উচিত (দ্রষ্টব্য: একই অভিব্যক্তি, কেবলমাত্র কিছু মত প্রকাশ যা একেবারে একই দেখাচ্ছে)। এই কাজটিকে কিছুটা ডিগ্রি করার জন্য আমি যেভাবে ভাবতে পারি তা হ'ল কোনও ফাংশনে নিক্ষেপ করার জন্য ল্যাম্বডা তৈরির সজ্জিত করা। এইভাবে, আপনি আপনার throw এক্সপ্রেশনটিতে ফাংশনটি কল করতে পারেন এবং catch জন্য টাইপটি অনুমান করতে ফাংশনের রিটার্ন টাইপটি ব্যবহার করতে পারেন:

#include <utility>

auto makeMyLambda(int some_arg)
{
    return [some_arg](int another_arg){ return some_arg + another_arg; };
}

void f()
{
    throw makeMyLambda(42);
}

int main()
{
    try
    {
        f();
    }
    catch (const decltype(makeMyLambda(std::declval<int>()))& l)
    {
        return l(23);
    }
}

here চেষ্টা করে দেখুন।

আপনি অন্যান্য std::function অন্যান্য উত্তরগুলির মধ্যে যেমন প্রস্তাবিত ব্যবহার করতে পারেন, এটি সম্ভবত আরও ব্যবহারিক পদ্ধতির। এর ডাউনসাইডগুলি অবশ্য হবে

  • এর অর্থ আপনি আসলে ল্যাম্বডা ফেলবেন না। আপনি একটি std::function নিক্ষেপ করেন যা আপনি যা চেয়েছিলেন তা আসলে নয় 😉
  • ল্যাম্বডা থেকে একটি std::function অবজেক্ট তৈরি করা ব্যতিক্রম ছুঁড়ে ফেলতে পারে




lambda