Is it possible to catch an exception of lambda type?











up vote
48
down vote

favorite
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
























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






  • 13




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










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










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










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















up vote
48
down vote

favorite
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
























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






  • 13




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










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










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










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













up vote
48
down vote

favorite
2









up vote
48
down vote

favorite
2






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 at 9:53









GPhilo

5,58912243




5,58912243










asked Dec 5 at 9:51









Krzysiek Karbowiak

25317




25317












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






  • 13




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










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










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










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


















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






  • 13




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










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










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










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
















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




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




13




13




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




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












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




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












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




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












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




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












4 Answers
4






active

oldest

votes

















up vote
45
down vote



accepted










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 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 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 at 19:07


















up vote
21
down vote













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






























    up vote
    5
    down vote













    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




























      up vote
      1
      down vote













      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 at 11:54










      • Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
        – Krzysiek Karbowiak
        Dec 12 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',
      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








      up vote
      45
      down vote



      accepted










      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 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 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 at 19:07















      up vote
      45
      down vote



      accepted










      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 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 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 at 19:07













      up vote
      45
      down vote



      accepted







      up vote
      45
      down vote



      accepted






      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 at 10:04









      StoryTeller

      92.4k12184249




      92.4k12184249








      • 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 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 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 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 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 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 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 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 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 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 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 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 at 19:07












      up vote
      21
      down vote













      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



























        up vote
        21
        down vote













        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

























          up vote
          21
          down vote










          up vote
          21
          down vote









          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 at 16:05

























          answered Dec 5 at 10:12









          Michael Kenzel

          3,688719




          3,688719






















              up vote
              5
              down vote













              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

























                up vote
                5
                down vote













                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























                  up vote
                  5
                  down vote










                  up vote
                  5
                  down vote









                  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 at 10:04









                  dave

                  4418




                  4418






















                      up vote
                      1
                      down vote













                      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 at 11:54










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















                      up vote
                      1
                      down vote













                      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 at 11:54










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













                      up vote
                      1
                      down vote










                      up vote
                      1
                      down vote









                      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 at 15:36









                      Yakk - Adam Nevraumont

                      181k19188368




                      181k19188368








                      • 1




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










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














                      • 1




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










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








                      1




                      1




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




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












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




                      Whoa, that's cool! And sorry for the comment, I was sure the + was a typo...
                      – Krzysiek Karbowiak
                      Dec 12 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

                      flock() on closed filehandle LOCK_FILE at /usr/bin/apt-mirror

                      Mangá

                      Eduardo VII do Reino Unido