/* callback.h*/

/* Copyright (C) 2008 Chris Vine

The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1 of
   the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License, version 2.1, for more details.

   You should have received a copy of the GNU Lesser General Public
   License, version 2.1, along with this library (see the file LGPL.TXT
   which came with this source code package in the src/utils sub-directory);
   if not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA, 02111-1307, USA.

However, it is not intended that the object code of a program whose
source code instantiates a template from this file should by reason
only of that instantiation be subject to the restrictions of use in
the GNU Lesser General Public License.  With that in mind, the words
"and instantiations of templates (of any length)" shall be treated as
inserted in the fourth paragraph of section 5 of that licence after
the words "and small inline functions (ten lines or less in length)".
This does not affect any other reason why object code may be subject
to the restrictions in that licence (nor for the avoidance of doubt
does it affect the application of section 2 of that licence to
modifications of the source code in this file).

*/

#ifndef CALLBACK_H
#define CALLBACK_H

/* These callback classes are intended for use in cases where a
   callback object may need to be handed between threads and where
   using sigc::slot would therefore be unsafe - this is because
   sigc::slot objects representing non-static methods of a class
   derived from sigc::trackable may invoke sigc::trackable functions
   at slot construction and destruction which are not thread safe.

   The templated helper callback() functions make it trivial to create
   a callback object of the correct type.  A maximum of two arguments
   to pass to the relevant class method is provided for - but this can
   be extended indefinitely.

   The templated dispatch_callback functions are called by worker
   threads and will execute a callback in the main program
   GMainContext loop.
*/

#include <glib/gmain.h>

#include <iostream>

inline void write_error(const char* message) {
  std::cerr << message;
}

/* Callback is the interface class that users will generally see, and
   is suitable for passing by void*, which if using glib and/or
   gthreads and/or pthreads is almost certain to happen at some stage.
*/
class Callback {
public:
  // because we will usually be calling through a base class pointer,
  // it would be more natural to invoke the call back through a normal
  // member function rather than by operator()()
  virtual void dispatch() const = 0;
  // but for those who want it
  void operator()() const {dispatch();}
  virtual ~Callback() {};
};

template <class T> class Callback0: public Callback {
public:
  typedef void (T::* MemFunc)();
private:
  T* obj;
  MemFunc func;
public:
  void dispatch() const {(obj->*func)();}
  Callback0(T& obj_, MemFunc func_): obj(&obj_), func(func_) {}
};

template <class T, class Arg1> class Callback1: public Callback {
public:
  typedef void (T::* MemFunc)(Arg1);
private:
  T* obj;
  MemFunc func;
  Arg1 arg1;
public:
  void dispatch() const {(obj->*func)(arg1);}
  Callback1( T& obj_, MemFunc func_, Arg1 arg1_): obj(&obj_), func(func_), arg1(arg1_) {}
};

template <class T, class Arg1, class Arg2> class Callback2: public Callback {
public:
  typedef void (T::* MemFunc)(Arg1, Arg2);
private:
  T* obj;
  MemFunc func;
  Arg1 arg1;
  Arg2 arg2;
public:
  void dispatch() const {(obj->*func)(arg1, arg2);}
  Callback2( T& obj_, MemFunc func_, Arg1 arg1_, Arg2 arg2_): obj(&obj_), func(func_),
                                                              arg1(arg1_), arg2(arg2_) {}
};

// Convenience functions making callback objects on freestore.  These
// can for example be passed as the callback argument of the gthread
// functions starting new threads. They are also used in the
// dispatch_callback() functions.

template <class T>
Callback* callback(T& t,
		   typename Callback0<T>::MemFunc func) {
  return new Callback0<T>(t, func);
}

template <class T, class Arg1>
Callback* callback(T& t,
		   typename Callback1<T, Arg1>::MemFunc func,
		   Arg1 arg1) {
  return new Callback1<T, Arg1>(t, func, arg1);
}

template <class T, class Arg1, class Arg2>
Callback* callback(T& t,
		   typename Callback2<T, Arg1, Arg2>::MemFunc func,
		   Arg1 arg1,
		   Arg2 arg2) {
  return new Callback2<T, Arg1, Arg2>(t, func, arg1, arg2);
}


/*
The dispatch_callback() template functions are intended as a means of
passing a callback from a worker thread to the main program loop for
execution by the main program loop.  In that respect, it provides an
alternative to the Glib::Dispatcher class.

Advantages as against Glib::Dispatcher:

1. If there are a lot of different events requiring callbacks to be
   dispatched in the program from worker threads to the main thread,
   this avoids having separate Glib::Dispatcher objects for each
   event.

2. It is easier to pass arguments with varying values - they can be
   passed as templated arguments to the relevant Callback object and
   no special synchronisation is normally required (the call to
   g_idle_add() invokes locking of the main loop which will have the
   effect of ensuring memory visibility).  With a Glib::Dispatcher
   object it may be necessary to use an asynchronous queue to pass
   variable values (or to bind a reference to the data, thus normally
   requiring separate synchronisation).

Disadvantages as against Glib::Dispatcher:

1. Less efficient, as a new callback object has to be created on
   freestore every time the callback is invoked.

2. Callbacks represent non-static methods of a class, and it is
   possible that when the main GUI thread executes the slot, the
   relevant object no longer exists.  Either additional steps need to
   be taken to tie object lifetime to the completion of execution of
   the callback, or the function should only be used for methods of
   objects which it is known will be in existence during the whole of
   the time that the program main loop will be running.

3. Multiple callbacks relevant to a single event cannot be invoked
   from a single call for the event - each callback has to be
   separately dispatched.
*/


extern "C" gboolean callback_wrapper(void*);

inline gboolean callback_wrapper(void* data) {
  const Callback* cb = static_cast<Callback*>(data);
  try {
    cb->dispatch();
  }
  // we can't propagate exceptions from functions with C linkage
  catch (...) {
    write_error("Exception thrown in callback_wrapper() for dispatch_callback() function\n");
  }
  delete cb;
  return false;
}

template <class T>
void dispatch_callback(T& t,
		       typename Callback0<T>::MemFunc func) {
  g_idle_add(callback_wrapper, callback(t, func));
}

template <class T, class Arg1>
void dispatch_callback(T& t,
		       typename Callback1<T, Arg1>::MemFunc func,
		       Arg1 arg1) {
  g_idle_add(callback_wrapper, callback(t, func, arg1));
}

template <class T, class Arg1, class Arg2>
void dispatch_callback(T& t,
		       typename Callback2<T, Arg1, Arg2>::MemFunc func,
		       Arg1 arg1,
		       Arg2 arg2) {
  g_idle_add(callback_wrapper, callback(t, func, arg1, arg2));
}

---- 8X------------------------- snip here ---------------------------
/* main1.cpp */

#endif
#include <iostream>
#include <ostream>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <glib/gthread.h>

#include "callback.h"

/*
  This demonstrates the use of the callback() templated functions (the
  Callback object created by the function is executed by a thread
  created with g_thread_create())
*/

class MyClass {
public:
  void message(char* text) {
    std::cout << text << std::flush;
    free(text);
  }
};

extern "C" void* thread_func(void* data) {
  const Callback* cb = static_cast<Callback*>(data);
  cb->dispatch();
  delete cb;
  return 0;
}

int main(void) {
 
  MyClass myclass;
  g_thread_init(0);
  for(;;) {
    g_thread_create(thread_func,
		    callback(myclass, &MyClass::message, strdup("Hello world\n")),
		    false, 0);
    sleep(1);
  }
}

---- 8X------------------------- snip here ---------------------------
/* main2.cpp */

#include <iostream>
#include <ostream>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <glib/gthread.h>
#include <glib/gmain.h>

#include "callback.h"

/*
  This demonstrates the use of the dispatch_callback() templated
  functions (the thread created with g_thread_create() executes
  callbacks in the main program loop by calling dispatch_callback())
*/

class MyClass {
public:
  void message(char* text) {
    std::cout << text << std::flush;
    free(text);
  }
} my_class;

extern "C" void* thread_func(void*) {
  for(;;) {
    dispatch_callback(my_class, &MyClass::message, strdup("Hello world\n"));
    sleep(1);
  }
}

int main(void) {
  
  g_thread_init(0);
  g_thread_create(thread_func, 0, false, 0);
  g_main_loop_run(g_main_loop_new(0, true));
}

---- 8X------------------------- snip here ---------------------------
/* Makefile */

CC=g++
CXXFLAGS=-fexceptions -frtti -fsigned-char -Wall -O2

all: dispatch_test1 dispatch_test2

dispatch_test1: main1.cpp callback.h
	$(CC) main1.cpp $(CXXFLAGS) `pkg-config glib-2.0 gthread-2.0 --libs --cflags` -o dispatch_test1

dispatch_test2: main2.cpp callback.h
	$(CC) main2.cpp $(CXXFLAGS) `pkg-config glib-2.0 gthread-2.0 --libs --cflags` -o dispatch_test2

clean:
	rm -f dispatch_test1 dispatch_test2
