Is it possible to catch an exception of lambda type?












53














While it is good practice to throw only exceptions of types derived from std::exception class, C++ makes it possible to throw anything. All below examples are valid C++:



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!


The last example is interesting, as it potentially allows passing some code to execute at catch site without having to define a separate class or function.



But is it at all possible to catch a lambda (or a closure)? catch ({} e) does not work.










share|improve this question
























  • I think we can assume catch(...){} is not desired.
    – Joshua
    Dec 5 '18 at 21:33






  • 14




    catch (...) { asm("call %rax"); } unsafe problems call for unsafe solutions
    – sudo rm -rf slash
    Dec 5 '18 at 22:27










  • @Joshua catch(...) is not desired if only for not giving access to the exception object being caught.
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:06










  • It turns out someone was already curious about lambda exceptions: stackoverflow.com/questions/42338470/catching-lambda-exception
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:37










  • @sudo rm -rf slash: I don't think you tried it. You got the x64 calling convention wrong.
    – Joshua
    Dec 6 '18 at 14:56
















53














While it is good practice to throw only exceptions of types derived from std::exception class, C++ makes it possible to throw anything. All below examples are valid C++:



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!


The last example is interesting, as it potentially allows passing some code to execute at catch site without having to define a separate class or function.



But is it at all possible to catch a lambda (or a closure)? catch ({} e) does not work.










share|improve this question
























  • I think we can assume catch(...){} is not desired.
    – Joshua
    Dec 5 '18 at 21:33






  • 14




    catch (...) { asm("call %rax"); } unsafe problems call for unsafe solutions
    – sudo rm -rf slash
    Dec 5 '18 at 22:27










  • @Joshua catch(...) is not desired if only for not giving access to the exception object being caught.
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:06










  • It turns out someone was already curious about lambda exceptions: stackoverflow.com/questions/42338470/catching-lambda-exception
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:37










  • @sudo rm -rf slash: I don't think you tried it. You got the x64 calling convention wrong.
    – Joshua
    Dec 6 '18 at 14:56














53












53








53


2





While it is good practice to throw only exceptions of types derived from std::exception class, C++ makes it possible to throw anything. All below examples are valid C++:



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!


The last example is interesting, as it potentially allows passing some code to execute at catch site without having to define a separate class or function.



But is it at all possible to catch a lambda (or a closure)? catch ({} e) does not work.










share|improve this question















While it is good practice to throw only exceptions of types derived from std::exception class, C++ makes it possible to throw anything. All below examples are valid C++:



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!


The last example is interesting, as it potentially allows passing some code to execute at catch site without having to define a separate class or function.



But is it at all possible to catch a lambda (or a closure)? catch ({} e) does not work.







c++ exception lambda






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 5 '18 at 9:53









GPhilo

5,78412243




5,78412243










asked Dec 5 '18 at 9:51









Krzysiek KarbowiakKrzysiek Karbowiak

27827




27827












  • I think we can assume catch(...){} is not desired.
    – Joshua
    Dec 5 '18 at 21:33






  • 14




    catch (...) { asm("call %rax"); } unsafe problems call for unsafe solutions
    – sudo rm -rf slash
    Dec 5 '18 at 22:27










  • @Joshua catch(...) is not desired if only for not giving access to the exception object being caught.
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:06










  • It turns out someone was already curious about lambda exceptions: stackoverflow.com/questions/42338470/catching-lambda-exception
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:37










  • @sudo rm -rf slash: I don't think you tried it. You got the x64 calling convention wrong.
    – Joshua
    Dec 6 '18 at 14:56


















  • I think we can assume catch(...){} is not desired.
    – Joshua
    Dec 5 '18 at 21:33






  • 14




    catch (...) { asm("call %rax"); } unsafe problems call for unsafe solutions
    – sudo rm -rf slash
    Dec 5 '18 at 22:27










  • @Joshua catch(...) is not desired if only for not giving access to the exception object being caught.
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:06










  • It turns out someone was already curious about lambda exceptions: stackoverflow.com/questions/42338470/catching-lambda-exception
    – Krzysiek Karbowiak
    Dec 6 '18 at 7:37










  • @sudo rm -rf slash: I don't think you tried it. You got the x64 calling convention wrong.
    – Joshua
    Dec 6 '18 at 14:56
















I think we can assume catch(...){} is not desired.
– Joshua
Dec 5 '18 at 21:33




I think we can assume catch(...){} is not desired.
– Joshua
Dec 5 '18 at 21:33




14




14




catch (...) { asm("call %rax"); } unsafe problems call for unsafe solutions
– sudo rm -rf slash
Dec 5 '18 at 22:27




catch (...) { asm("call %rax"); } unsafe problems call for unsafe solutions
– sudo rm -rf slash
Dec 5 '18 at 22:27












@Joshua catch(...) is not desired if only for not giving access to the exception object being caught.
– Krzysiek Karbowiak
Dec 6 '18 at 7:06




@Joshua catch(...) is not desired if only for not giving access to the exception object being caught.
– Krzysiek Karbowiak
Dec 6 '18 at 7:06












It turns out someone was already curious about lambda exceptions: stackoverflow.com/questions/42338470/catching-lambda-exception
– Krzysiek Karbowiak
Dec 6 '18 at 7:37




It turns out someone was already curious about lambda exceptions: stackoverflow.com/questions/42338470/catching-lambda-exception
– Krzysiek Karbowiak
Dec 6 '18 at 7:37












@sudo rm -rf slash: I don't think you tried it. You got the x64 calling convention wrong.
– Joshua
Dec 6 '18 at 14:56




@sudo rm -rf slash: I don't think you tried it. You got the x64 calling convention wrong.
– Joshua
Dec 6 '18 at 14:56












4 Answers
4






active

oldest

votes


















50














Exception handlers are matched based on type, and the implicit conversions done to match an exception object to a handler are more limited than in other contexts.



Each lambda expression introduces a closure type that is unique to the surrounding scope. So your naive attempt cannot work, for {} has an entirely different type in the throw expression and the handler!



But you are correct. C++ allows you to throw any object. So if you explicitly convert the lambda before-hand to a type that matches an exception handler, it will allow you to call that arbitrary callable. For instance:



try {
throw std::function<void()>{ {} }; // Note the explicit conversion
} catch(std::function<void()> const& f) {
f();
}


This may have interesting utility, but I'd caution against throwing things not derived from std::exception. A better option would probably be to create a type that derives from std::exception and can hold a callable.






share|improve this answer

















  • 7




    Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
    – Krzysiek Karbowiak
    Dec 5 '18 at 10:25








  • 4




    @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
    – StoryTeller
    Dec 5 '18 at 10:27






  • 2




    The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
    – Silvio Mayolo
    Dec 5 '18 at 19:07



















23














C++ allows you to throw anything. And It allows you to catch whatever you throw. You can, of course, throw a lambda. The only problem is that, to catch something, you need to know the type or at least a parent type of that something. Since lambdas do not derive from a common base, you have to know the type of your lambda to catch a lambda. The main issue with that is that every lambda expression will give you an rvalue of a distinct type. That means that both your throw and your catch need to be based on the same lambda expression (note: the same expression, not just some expression that looks exactly the same). One way I can think of to make this work to some degree would be to encapsulate the creation of the lambda to throw in a function. That way, you can call the function in your throw expression, and use the return type of the function to deduce the type to 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);
}
}


Try it out here.



You could also just use std::function like suggested in some of the other answers, which is potentially a more practical approach. The downsides of that, however, would be




  • It means you don't actually throw a lambda. You throw an std::function, which is not really what you asked for 😉

  • The creation of an std::function object from a lambda can throw an exception






share|improve this answer































    6














    You can throw and catch a std::function:



    #include <iostream>
    #include <functional>

    void f() {
    throw std::function<void(void)>({std::cout << "lambdan"; });
    }

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


    Output:



    lambda
    catch





    share|improve this answer





























      1














      A lambda is a unique anonymous type. The only way to name a lambda instance's type is to store it in a variable, then do a decltype on that variable type.



      There are a few ways you can catch a thrown lambda.



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


      in this case you cannot use it, other than throwing it again.



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


      a stateless lambda can be converted to a function pointer.



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


      you can convert it to a std::function. The downside with std::function is that it heap allocates for larger lambdas, which could in theory cause it to throw.



      We can eliminate that heap allocation:



      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);
      }


      now you can do:



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


      callable is "lighter weight" type erasure than std::function as it cannot cause new heap memory to be allocated.



      Live example.






      share|improve this answer

















      • 1




        @krzy you are missing the +; I never said implicitly. throw +{};
        – Yakk - Adam Nevraumont
        Dec 11 '18 at 11:54










      • Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
        – Krzysiek Karbowiak
        Dec 12 '18 at 7:45











      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53629430%2fis-it-possible-to-catch-an-exception-of-lambda-type%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      50














      Exception handlers are matched based on type, and the implicit conversions done to match an exception object to a handler are more limited than in other contexts.



      Each lambda expression introduces a closure type that is unique to the surrounding scope. So your naive attempt cannot work, for {} has an entirely different type in the throw expression and the handler!



      But you are correct. C++ allows you to throw any object. So if you explicitly convert the lambda before-hand to a type that matches an exception handler, it will allow you to call that arbitrary callable. For instance:



      try {
      throw std::function<void()>{ {} }; // Note the explicit conversion
      } catch(std::function<void()> const& f) {
      f();
      }


      This may have interesting utility, but I'd caution against throwing things not derived from std::exception. A better option would probably be to create a type that derives from std::exception and can hold a callable.






      share|improve this answer

















      • 7




        Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
        – Krzysiek Karbowiak
        Dec 5 '18 at 10:25








      • 4




        @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
        – StoryTeller
        Dec 5 '18 at 10:27






      • 2




        The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
        – Silvio Mayolo
        Dec 5 '18 at 19:07
















      50














      Exception handlers are matched based on type, and the implicit conversions done to match an exception object to a handler are more limited than in other contexts.



      Each lambda expression introduces a closure type that is unique to the surrounding scope. So your naive attempt cannot work, for {} has an entirely different type in the throw expression and the handler!



      But you are correct. C++ allows you to throw any object. So if you explicitly convert the lambda before-hand to a type that matches an exception handler, it will allow you to call that arbitrary callable. For instance:



      try {
      throw std::function<void()>{ {} }; // Note the explicit conversion
      } catch(std::function<void()> const& f) {
      f();
      }


      This may have interesting utility, but I'd caution against throwing things not derived from std::exception. A better option would probably be to create a type that derives from std::exception and can hold a callable.






      share|improve this answer

















      • 7




        Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
        – Krzysiek Karbowiak
        Dec 5 '18 at 10:25








      • 4




        @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
        – StoryTeller
        Dec 5 '18 at 10:27






      • 2




        The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
        – Silvio Mayolo
        Dec 5 '18 at 19:07














      50












      50








      50






      Exception handlers are matched based on type, and the implicit conversions done to match an exception object to a handler are more limited than in other contexts.



      Each lambda expression introduces a closure type that is unique to the surrounding scope. So your naive attempt cannot work, for {} has an entirely different type in the throw expression and the handler!



      But you are correct. C++ allows you to throw any object. So if you explicitly convert the lambda before-hand to a type that matches an exception handler, it will allow you to call that arbitrary callable. For instance:



      try {
      throw std::function<void()>{ {} }; // Note the explicit conversion
      } catch(std::function<void()> const& f) {
      f();
      }


      This may have interesting utility, but I'd caution against throwing things not derived from std::exception. A better option would probably be to create a type that derives from std::exception and can hold a callable.






      share|improve this answer












      Exception handlers are matched based on type, and the implicit conversions done to match an exception object to a handler are more limited than in other contexts.



      Each lambda expression introduces a closure type that is unique to the surrounding scope. So your naive attempt cannot work, for {} has an entirely different type in the throw expression and the handler!



      But you are correct. C++ allows you to throw any object. So if you explicitly convert the lambda before-hand to a type that matches an exception handler, it will allow you to call that arbitrary callable. For instance:



      try {
      throw std::function<void()>{ {} }; // Note the explicit conversion
      } catch(std::function<void()> const& f) {
      f();
      }


      This may have interesting utility, but I'd caution against throwing things not derived from std::exception. A better option would probably be to create a type that derives from std::exception and can hold a callable.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Dec 5 '18 at 10:04









      StoryTellerStoryTeller

      94.4k12191254




      94.4k12191254








      • 7




        Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
        – Krzysiek Karbowiak
        Dec 5 '18 at 10:25








      • 4




        @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
        – StoryTeller
        Dec 5 '18 at 10:27






      • 2




        The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
        – Silvio Mayolo
        Dec 5 '18 at 19:07














      • 7




        Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
        – Krzysiek Karbowiak
        Dec 5 '18 at 10:25








      • 4




        @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
        – StoryTeller
        Dec 5 '18 at 10:27






      • 2




        The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
        – Silvio Mayolo
        Dec 5 '18 at 19:07








      7




      7




      Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
      – Krzysiek Karbowiak
      Dec 5 '18 at 10:25






      Sure, I would not use this in production code. Rather, I was exploring intricacies of the language. And while I did try to catch by pointer-to-function, it did not occur to me to throw and catch as std::function.
      – Krzysiek Karbowiak
      Dec 5 '18 at 10:25






      4




      4




      @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
      – StoryTeller
      Dec 5 '18 at 10:27




      @KrzysiekKarbowiak - If the type is designed carefully I don't see why you can't do it in production. It may have interesting utility, like I noted. Ingenuity is after all taking what is known already and using it in a novel way :)
      – StoryTeller
      Dec 5 '18 at 10:27




      2




      2




      The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
      – Silvio Mayolo
      Dec 5 '18 at 19:07




      The exact rules used to match catch expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown as throw {};, as there's no public base class and the type is not a pointer so the pointer rules don't apply.
      – Silvio Mayolo
      Dec 5 '18 at 19:07













      23














      C++ allows you to throw anything. And It allows you to catch whatever you throw. You can, of course, throw a lambda. The only problem is that, to catch something, you need to know the type or at least a parent type of that something. Since lambdas do not derive from a common base, you have to know the type of your lambda to catch a lambda. The main issue with that is that every lambda expression will give you an rvalue of a distinct type. That means that both your throw and your catch need to be based on the same lambda expression (note: the same expression, not just some expression that looks exactly the same). One way I can think of to make this work to some degree would be to encapsulate the creation of the lambda to throw in a function. That way, you can call the function in your throw expression, and use the return type of the function to deduce the type to 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);
      }
      }


      Try it out here.



      You could also just use std::function like suggested in some of the other answers, which is potentially a more practical approach. The downsides of that, however, would be




      • It means you don't actually throw a lambda. You throw an std::function, which is not really what you asked for 😉

      • The creation of an std::function object from a lambda can throw an exception






      share|improve this answer




























        23














        C++ allows you to throw anything. And It allows you to catch whatever you throw. You can, of course, throw a lambda. The only problem is that, to catch something, you need to know the type or at least a parent type of that something. Since lambdas do not derive from a common base, you have to know the type of your lambda to catch a lambda. The main issue with that is that every lambda expression will give you an rvalue of a distinct type. That means that both your throw and your catch need to be based on the same lambda expression (note: the same expression, not just some expression that looks exactly the same). One way I can think of to make this work to some degree would be to encapsulate the creation of the lambda to throw in a function. That way, you can call the function in your throw expression, and use the return type of the function to deduce the type to 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);
        }
        }


        Try it out here.



        You could also just use std::function like suggested in some of the other answers, which is potentially a more practical approach. The downsides of that, however, would be




        • It means you don't actually throw a lambda. You throw an std::function, which is not really what you asked for 😉

        • The creation of an std::function object from a lambda can throw an exception






        share|improve this answer


























          23












          23








          23






          C++ allows you to throw anything. And It allows you to catch whatever you throw. You can, of course, throw a lambda. The only problem is that, to catch something, you need to know the type or at least a parent type of that something. Since lambdas do not derive from a common base, you have to know the type of your lambda to catch a lambda. The main issue with that is that every lambda expression will give you an rvalue of a distinct type. That means that both your throw and your catch need to be based on the same lambda expression (note: the same expression, not just some expression that looks exactly the same). One way I can think of to make this work to some degree would be to encapsulate the creation of the lambda to throw in a function. That way, you can call the function in your throw expression, and use the return type of the function to deduce the type to 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);
          }
          }


          Try it out here.



          You could also just use std::function like suggested in some of the other answers, which is potentially a more practical approach. The downsides of that, however, would be




          • It means you don't actually throw a lambda. You throw an std::function, which is not really what you asked for 😉

          • The creation of an std::function object from a lambda can throw an exception






          share|improve this answer














          C++ allows you to throw anything. And It allows you to catch whatever you throw. You can, of course, throw a lambda. The only problem is that, to catch something, you need to know the type or at least a parent type of that something. Since lambdas do not derive from a common base, you have to know the type of your lambda to catch a lambda. The main issue with that is that every lambda expression will give you an rvalue of a distinct type. That means that both your throw and your catch need to be based on the same lambda expression (note: the same expression, not just some expression that looks exactly the same). One way I can think of to make this work to some degree would be to encapsulate the creation of the lambda to throw in a function. That way, you can call the function in your throw expression, and use the return type of the function to deduce the type to 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);
          }
          }


          Try it out here.



          You could also just use std::function like suggested in some of the other answers, which is potentially a more practical approach. The downsides of that, however, would be




          • It means you don't actually throw a lambda. You throw an std::function, which is not really what you asked for 😉

          • The creation of an std::function object from a lambda can throw an exception







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Dec 5 '18 at 16:05

























          answered Dec 5 '18 at 10:12









          Michael KenzelMichael Kenzel

          4,043719




          4,043719























              6














              You can throw and catch a std::function:



              #include <iostream>
              #include <functional>

              void f() {
              throw std::function<void(void)>({std::cout << "lambdan"; });
              }

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


              Output:



              lambda
              catch





              share|improve this answer


























                6














                You can throw and catch a std::function:



                #include <iostream>
                #include <functional>

                void f() {
                throw std::function<void(void)>({std::cout << "lambdan"; });
                }

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


                Output:



                lambda
                catch





                share|improve this answer
























                  6












                  6








                  6






                  You can throw and catch a std::function:



                  #include <iostream>
                  #include <functional>

                  void f() {
                  throw std::function<void(void)>({std::cout << "lambdan"; });
                  }

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


                  Output:



                  lambda
                  catch





                  share|improve this answer












                  You can throw and catch a std::function:



                  #include <iostream>
                  #include <functional>

                  void f() {
                  throw std::function<void(void)>({std::cout << "lambdan"; });
                  }

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


                  Output:



                  lambda
                  catch






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Dec 5 '18 at 10:04









                  davedave

                  4718




                  4718























                      1














                      A lambda is a unique anonymous type. The only way to name a lambda instance's type is to store it in a variable, then do a decltype on that variable type.



                      There are a few ways you can catch a thrown lambda.



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


                      in this case you cannot use it, other than throwing it again.



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


                      a stateless lambda can be converted to a function pointer.



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


                      you can convert it to a std::function. The downside with std::function is that it heap allocates for larger lambdas, which could in theory cause it to throw.



                      We can eliminate that heap allocation:



                      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);
                      }


                      now you can do:



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


                      callable is "lighter weight" type erasure than std::function as it cannot cause new heap memory to be allocated.



                      Live example.






                      share|improve this answer

















                      • 1




                        @krzy you are missing the +; I never said implicitly. throw +{};
                        – Yakk - Adam Nevraumont
                        Dec 11 '18 at 11:54










                      • Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                        – Krzysiek Karbowiak
                        Dec 12 '18 at 7:45
















                      1














                      A lambda is a unique anonymous type. The only way to name a lambda instance's type is to store it in a variable, then do a decltype on that variable type.



                      There are a few ways you can catch a thrown lambda.



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


                      in this case you cannot use it, other than throwing it again.



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


                      a stateless lambda can be converted to a function pointer.



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


                      you can convert it to a std::function. The downside with std::function is that it heap allocates for larger lambdas, which could in theory cause it to throw.



                      We can eliminate that heap allocation:



                      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);
                      }


                      now you can do:



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


                      callable is "lighter weight" type erasure than std::function as it cannot cause new heap memory to be allocated.



                      Live example.






                      share|improve this answer

















                      • 1




                        @krzy you are missing the +; I never said implicitly. throw +{};
                        – Yakk - Adam Nevraumont
                        Dec 11 '18 at 11:54










                      • Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                        – Krzysiek Karbowiak
                        Dec 12 '18 at 7:45














                      1












                      1








                      1






                      A lambda is a unique anonymous type. The only way to name a lambda instance's type is to store it in a variable, then do a decltype on that variable type.



                      There are a few ways you can catch a thrown lambda.



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


                      in this case you cannot use it, other than throwing it again.



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


                      a stateless lambda can be converted to a function pointer.



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


                      you can convert it to a std::function. The downside with std::function is that it heap allocates for larger lambdas, which could in theory cause it to throw.



                      We can eliminate that heap allocation:



                      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);
                      }


                      now you can do:



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


                      callable is "lighter weight" type erasure than std::function as it cannot cause new heap memory to be allocated.



                      Live example.






                      share|improve this answer












                      A lambda is a unique anonymous type. The only way to name a lambda instance's type is to store it in a variable, then do a decltype on that variable type.



                      There are a few ways you can catch a thrown lambda.



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


                      in this case you cannot use it, other than throwing it again.



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


                      a stateless lambda can be converted to a function pointer.



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


                      you can convert it to a std::function. The downside with std::function is that it heap allocates for larger lambdas, which could in theory cause it to throw.



                      We can eliminate that heap allocation:



                      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);
                      }


                      now you can do:



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


                      callable is "lighter weight" type erasure than std::function as it cannot cause new heap memory to be allocated.



                      Live example.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Dec 10 '18 at 15:36









                      Yakk - Adam NevraumontYakk - Adam Nevraumont

                      183k19189374




                      183k19189374








                      • 1




                        @krzy you are missing the +; I never said implicitly. throw +{};
                        – Yakk - Adam Nevraumont
                        Dec 11 '18 at 11:54










                      • Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                        – Krzysiek Karbowiak
                        Dec 12 '18 at 7:45














                      • 1




                        @krzy you are missing the +; I never said implicitly. throw +{};
                        – Yakk - Adam Nevraumont
                        Dec 11 '18 at 11:54










                      • Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                        – Krzysiek Karbowiak
                        Dec 12 '18 at 7:45








                      1




                      1




                      @krzy you are missing the +; I never said implicitly. throw +{};
                      – Yakk - Adam Nevraumont
                      Dec 11 '18 at 11:54




                      @krzy you are missing the +; I never said implicitly. throw +{};
                      – Yakk - Adam Nevraumont
                      Dec 11 '18 at 11:54












                      Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                      – Krzysiek Karbowiak
                      Dec 12 '18 at 7:45




                      Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                      – Krzysiek Karbowiak
                      Dec 12 '18 at 7:45


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53629430%2fis-it-possible-to-catch-an-exception-of-lambda-type%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Berounka

                      Sphinx de Gizeh

                      Different font size/position of beamer's navigation symbols template's content depending on regular/plain...