views:

419

answers:

3

Is the following supported across *nix platforms?

    #include <cstdio>
    #include <sys/types.h>
    #include <signal.h>
    #include <unistd.h>

    class SignalProcessor
    {
     public:
      static void OnMySignal(int sig_num)
      {
          printf("Caught %d signal\n", sig_num);
          fflush(stdout);

          return;
      } 
    }; 
    using namespace std;

   int main()
   {

          signal(SIGINT,SingalProcessor::OnMySignal);
          printf("Ouch\n");

          pause();

          return 0;
   }
A: 

I do the equivalent with Windows thead procedures and other assorted callbacks, and RTX interrupts all the time. The only real gotchas are that the members have to be static (which you already figured out), and that you have to make sure your routine is set to use the standard C/system call calling convention. Sadly, how you do that is platform dependent. In Win32 it is with the "__stdcall" modifier.

Note that you can use the passback-in pointer paramteter to "convert" such calls into normal class method calls. Like so ("self_calling_callback" is the static method):

unsigned long __stdcall basic_thread::self_calling_callback (void *parameter) {
   if (parameter) {
      basic_thread * thread = reinterpret_cast<basic_thread *>(parameter);
      thread->action_callback();
   }
   return 0; 
   // The value returned only matters if someone starts calling GetExitCodeThread
   // to retrieve it.
}

basic_thread::basic_thread () {
   // Start thread.
   m_Handle = CreateThread(NULL,
                           0,
                           self_calling_callback,
                           (PVOID)this,
                           0,
                           &m_ThreadId );
   if( !IsHandleValid() )
      throw StartException("CreateThread() failed", GetLastError());
}
T.E.D.
A: 

That should work just fine. In fact, you could expand that function to call specific instances of that class dependent on the signal caught. For example, if you add a non-static method Process to you class, you can do something like this:

SignalProcessor* sp[MAX_SIGNALS];

static void SignalProcessor::OnMySignal(int sig_num)
{
      printf("Caught %d signal\n", sig_num);

      if (0 < sp[sig_num])
            sp[sig_num]->Process();

      fflush(stdout);

      return;

}
Mark Jones
Ugh, it took me way too long to parse "0 < sp[sig_num]". You should use != instead of <, and I would also advocate using NULL instead of 0, which very clearly signals that you're dealing with pointers, not integers.
Adam Rosenfield
+4  A: 

Technically no you can't.

You just happen to be getting lucky that your compiler is using the same calling convention that it uses for 'C' functions. As the C++ ABI is not defined the next version of the compiler is free to use a completely different calling convention and this will mess with your code with no warning from the compiler.

See: http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2
See the note at the end of this section

Note: static member functions do not require an actual object to be invoked, so pointers-to-static-member-functions are usually type-compatible with regular pointers-to-functions. However, although it probably works on most compilers, it actually would have to be an extern "C" non-member function to be correct, since "C linkage" doesn't only cover things like name mangling, but also calling conventions, which might be different between C and C++.

Edit:
To answer the comment by Sasha:

Using threading as an example:

#include <iostream>
class Thread
{    public:   virtual void run()  = 0; };

extern "C" void* startThrerad(void* data)
{
    Thread*  thread = reinterpret_cast<Thread*>(data);
    try
    {
        thread->run();
    }
    catch(...)
    {    /* Log if required. Don't let thread exit with exception. */ }
    return NULL;
}
class MyJob: public Thread
{
    public: virtual void run() {std::cout << "HI\n";}
};
int main()
{
    MyJob     job; // MyJob inherits from Thread
    pthread_t th;

    // In most situation you do not need to dynamic cast.
    // But if you use multiple inheritance then things may get
    // interesting, as such best to always use it.
    pthread_create(&th,NULL,startThrerad,dynamic_cast<Thread*>(&job));

    void*     result;
    pthread_join(th,&result);
}
Martin York
The implication here, is that static methods are not equivalent to standard functions, and can not be used as normal function pointers. If this is true, many, many programs will break, as it is a common paradigm to use a static method as the callback function to a C api, where the object has been embedded somehow and is accessible from the callback.(A Windows callback system can work this way, where the object can be put into the window's long storage).So, even though it may not be guaranteed in the standard, I am betting it will stay valid for as long as C++ lives.
Simon Parker
I don't see it as a 'common paradigm'. I see inexperienced programmers use it now and then and immediate get slapped down during code review and told to replace it with a C function. The thing is most callbacks take a void* as a parameter to help specialise them. Just make this a poitner to an object so your C function calls a method on your object.
Martin York
What is the best design for this sort of call backs function in the context of classes? where do you put them?