Is it possible to catch an exception of lambda type?
up vote
48
down vote
favorite
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
|
show 1 more comment
up vote
48
down vote
favorite
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
I think we can assumecatch(...){}
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
@Joshuacatch(...)
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
|
show 1 more comment
up vote
48
down vote
favorite
up vote
48
down vote
favorite
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
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
c++ exception lambda
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 assumecatch(...){}
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
@Joshuacatch(...)
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
|
show 1 more comment
I think we can assumecatch(...){}
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
@Joshuacatch(...)
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
|
show 1 more comment
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.
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 asstd::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 matchcatch
expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown asthrow {};
, 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
add a comment |
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
add a comment |
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
add a comment |
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.
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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.
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 asstd::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 matchcatch
expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown asthrow {};
, 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
add a comment |
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.
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 asstd::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 matchcatch
expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown asthrow {};
, 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
add a comment |
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.
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.
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 asstd::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 matchcatch
expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown asthrow {};
, 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
add a comment |
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 asstd::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 matchcatch
expressions are available in this answer. Sadly, it looks like there's no way to catch the lambda if it's thrown asthrow {};
, 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
add a comment |
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
add a comment |
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
add a comment |
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
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
edited Dec 5 at 16:05
answered Dec 5 at 10:12
Michael Kenzel
3,688719
3,688719
add a comment |
add a comment |
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
add a comment |
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
add a comment |
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
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
answered Dec 5 at 10:04
dave
4418
4418
add a comment |
add a comment |
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.
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
add a comment |
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.
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
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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