4

How can I catch a lambda thrown as an exception? I tried the following:

#include <functional>
int main() {
    try {
        throw [](){};
    } catch (std::function<void()> & fn) {
        fn();
    }
}

However the output is

terminate called after throwing an instance of 'main::{lambda()#1}'

Is it possible to catch thrown lambda exception?

Anonymous Entity
  • 3,254
  • 3
  • 27
  • 41
  • 3
    Remember that each lambda is its own unique type, and it doesn't have any common base-type. Besides, throwing a lambda seems like a bad idea. – Some programmer dude Feb 20 '17 at 07:14
  • 3
    Furthermore, what is the *actual* problem you want to solve by throwing a lambda? You show us a wanted solution to an unknown problem, and ask us for help with that solution without telling us what it's supposed to solve. A typical [XY problem](http://xyproblem.info/). – Some programmer dude Feb 20 '17 at 07:16
  • I wouldn't actually code like that, I'm just interested in knowing whether it could be made to work in theory. I usually avoid exceptions in my real code whenever possible, so don't worry about that ;) – Anonymous Entity Feb 20 '17 at 07:21
  • There's always `catch(...)`, if you don't care about the actual value, but need to catch it. You could even do tricks with `std::exception_ptr` later. – milleniumbug Feb 20 '17 at 07:35

4 Answers4

4

You can throw an std::function explicitly:

int main() {
    try {
        throw std::function<void()>([](){std::cout << "Hello there!";});
    } catch (std::function<void()> & fn) {
        fn();
    }
}
SingerOfTheFall
  • 29,228
  • 8
  • 68
  • 105
1
int main() {
    try {
        throw [](){};
    } catch (std::function<void()> & fn) {
        fn();
    }
}

Two reasons why that exception handler will not be executed:

  1. You are catching your exception by lvalue reference to an object of std::function<void()>, but the thrown object isn't of that type neither is it a base class of the thrown object.

  2. Even if you changed the parameter to value, std::function<void()> will not be constructed from the lambda in exception handling. See this


However, there are ways to make it "work". See answers by SingerOfTheFall and by skypjack

Community
  • 1
  • 1
WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
1

The lambda has its own type that is not std::function. Therefore you are not catching the lambda, you are catching something else that is never thrown and can be assigned to a std::function.

To work around it, you can wrap the lambda directly in a std::function or a handler class an throw it.
As a minimal, working example (using a wrapper, that is a bit funnier to write):

#include <functional>
#include<utility>
#include<type_traits>
#include<iostream>

struct Base {
    virtual void operator()() = 0;
};

template<typename F>
struct Lambda: F, Base {
    Lambda(F &&f): F{std::forward<F>(f)} {}
    void operator()() override { F::operator()(); }
};

template<typename F>
auto create(F &&f) {
    return Lambda<std::decay_t<F>>{std::forward<F>(f)};
}

int main() {
    try {
        throw create([](){ std::cout << "doh" << std::endl; });
    } catch (Base &fn) {
        fn();
    }
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
0

The main requirement when catching an exception is to have a specific type for the object you're catching. Lambda's don't have a specific, clean type that you can use. A clean way to do this is to wrap your Lambda with an std::function, and then you exactly know what you're catching.

Community
  • 1
  • 1
The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189