Alex Rivera | Logout

Anonymous function C++

Asked 2012-05-30T12:16:44.067
15

I am trying to use the function signal(int,void(*)(int)) from <csignal> to handle the floating point exception SIGFPE. I'd like to be able to print some useful diagnostics besides just a message saying "Floating point exception" or something to that effect. This means the function I pass as the handler to signal needs access to some of the data in my code. Therein lies the rub.

The function must return void and accept only 1 parameter of type int. I cannot make the handler a member function of my data storage class since then the type would be void(Foo::*)(int) due to the hidden this pointer.

I thought about using lambdas to try and make an anonymous function like this;

void handler(int nSig, Foo data)
{
    // do something
}
// snip
Foo data;
signal(SIGFPE, [&](int nSig)->void{handler(nSig,data);});

however because the lambda captures the variable data from outside the compiler will not let it be cast to a pointer to void(*)(int) (which is a shame as this seems like an ideal use for lambdas).

I could simply make data a global variable which could then be seen in handler but I am loath to do this for obvious reasons.

So my question is thus; what is the best way of mimicking anonymous functions in C++?

Note: I would prefer a native C++ solution and not to have to use boost or equivalent.

Edit
Report

2 Answers

14

This is indeed a good question. Let's figure out what is going before blaming C++ though. Just think about how lambdas are implemented.

The most simple lambda is when no data is captured. If that is the case, its underlying type becomes a simple plain function. For example, a lambda like this:

[] (int p0) {}

will be an equivalent of a simple function:

void foo(int p0)
{
}

That actually perfectly works in case you want that lambda to become a function pointer. For example:

#include <string>
#include <csignal>
#include <iostream>

int main()
{
    int ret;
    signal(SIGINT, [](int signal) {
            std::cout << "Got signal " << signal << std::endl;
        });
    std::cin >> ret;
    return ret;
}

So far so good. But now you want to associate some data with your signal handler (by the way, the code above is undefined behavior as you can only execute signal-safe code inside a signal handler). So you want a lambda like:

#include <string>
#include <csignal>
#include <iostream>

struct handler_context {
    std::string code;
    std::string desc;
};

int main()
{
    int ret;
    handler_context ctx({ "SIGINT", "Interrupt" });
    signal(SIGINT, [&](int signal) {
            std::cout << "Got signal " << signal
                      << " (" << ctx.code << ": " << ctx.desc
                      << ")\n" << std::flush;
        });
    std::cin >> ret;
    return ret;
}

Let's forget for a moment about a syntactic sugar of C++ lambdas. It is no secret that you can "mimic" lambda even in C or assembler. So how would that look, actually? "Lambda" in C-style could look like this (this is still C++):

#i
answered 2012-05-30T13:31:31.763
0

You cannot easily create a new static function in runtime, some JIT compilers libs are able to do this. If you need only a reasonable number of pointers, you can create some pool of static functions by specializing a template.

So easiest way is to wrap C++ Functors by a static function. The problem here is that there is no something like user data parameter. There is only one parameter, that is a number of signal. Since there are only 64 signals, you can create a static array of std::function< void(int) > and call each depending on signal number. Some simple example:

typedef std::function< void(int) > SignalFunc;

static std::array< SignalFunc, 64 > signalsFunc;

static void cHandler(int nSig)
{
    signalsFunc.at(nSig)(nSig);
}

SignalFunc RegisterSystemSignal( int sig, SignalFunc func )
{
    if( signal( sig, func ? &cHandler : (sighandler_t)SIG_DFL ) != SIG_ERR )
    { 
        func.swap( signalsFunc.at( sig ) );
        return func;
    }
    throw some_error();
}

So now you can do that:

RegisterSystemSignal(SIGFPE, [&](int nSig)->void{handler(nSig,data);});

There is also a sigaction witch have more features.

answered 2012-05-30T15:38:13.123

Your Answer