diff mbox

[RFA,1/4] Convert observers to C++

Message ID 1475468542-11446-2-git-send-email-tom@tromey.com
State New
Headers show

Commit Message

Tom Tromey Oct. 3, 2016, 4:22 a.m. UTC
This converts observers from using a special source-generating script
to be plain C++.

This translation is less ideal than I'd like in a couple of ways.
Still, I think it's a mild improvement, on the basis that ordinary
code is preferable to an ad hoc code generator.

First, it introduces a gdb analogue to std::function, the real one not
being available until C++11.

Second, it works around the lack of variadic templates in a mildly
ugly way.

In C++11 it would be ~300 lines of code shorter.

2016-10-02  Tom Tromey  <tom@tromey.com>

	* observer.sh: Remove.
	* observer.h: New file.
	* observer.c (namespace gdb_observers): Define new objects.
	(observer_debug): Move into gdb_observers namespace.
	(struct observer, struct observer_list, xalloc_observer_list_node)
	(xfree_observer_list_node, generic_observer_attach)
	(generic_observer_detach, generic_observer_notify): Remove.
	(_initialize_observer): Update.
	Don't include observer.inc.
	* gdb_function.h: New file.
	* Makefile.in (generated_files): Remove observer.h, observer.inc.
	(clean mostlyclean): Likewise.
	(observer.h, observer.inc): Remove targets.
	* .gitignore: Remove observer.h.

2016-10-02  Tom Tromey  <tom@tromey.com>

	* observer.texi: Remove.
---
 gdb/.gitignore        |   1 -
 gdb/ChangeLog         |  17 +++
 gdb/Makefile.in       |  10 +-
 gdb/doc/ChangeLog     |   4 +
 gdb/doc/observer.texi | 309 ---------------------------------------
 gdb/gdb_function.h    | 149 +++++++++++++++++++
 gdb/observer.c        | 236 ++++++++++++------------------
 gdb/observer.h        | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/observer.sh       | 200 -------------------------
 9 files changed, 658 insertions(+), 662 deletions(-)
 delete mode 100644 gdb/doc/observer.texi
 create mode 100644 gdb/gdb_function.h
 create mode 100644 gdb/observer.h
 delete mode 100755 gdb/observer.sh

Comments

Trevor Saunders Oct. 3, 2016, 5:39 p.m. UTC | #1
On Sun, Oct 02, 2016 at 10:22:19PM -0600, Tom Tromey wrote:
> This converts observers from using a special source-generating script
> to be plain C++.
> 
> This translation is less ideal than I'd like in a couple of ways.
> Still, I think it's a mild improvement, on the basis that ordinary
> code is preferable to an ad hoc code generator.
> 
> First, it introduces a gdb analogue to std::function, the real one not
> being available until C++11.
> 
> Second, it works around the lack of variadic templates in a mildly
> ugly way.
> 
> In C++11 it would be ~300 lines of code shorter.

fwiw I think you can reduce the boilerplate in the observer classes with
something like this.

template<typename FuncType>
class observer_base
{
public:
  void add_observer (const FuncType &func)
  {
    m_observers.push_front (func);
  }

  void remove_observer (const FuncType &func)
  {
    m_observers.remove (func);
  }

protected:
  typedef std::forward_list<FuncType>::iterator iter_type;
  std::forward_list<FuncType> m_observers;
};

template<typename A>
class observer_1 FINAL : public observer_base
{
public:
  observer_1 (const char *name) : m_name (name) {}

  void notify (A a)
  {
    // loop over m_observers
  }

private:
  const char *m_name;
  };

unfortunately you need to keep the constructors in the subclasses
because you don't have inheriting constructors.

Its perhaps more complicated, but with final I believe all the notify
calls will get devirtualized by a reasonable compiler and you will end up
with code basically identical to what you wrote.

Trev
diff mbox

Patch

diff --git a/gdb/.gitignore b/gdb/.gitignore
index e7e1087..b9b2c6f 100644
--- a/gdb/.gitignore
+++ b/gdb/.gitignore
@@ -21,7 +21,6 @@ 
 /jv-exp.c
 /m2-exp.c
 /objc-exp.c
-/observer.h
 /observer.inc
 /p-exp.c
 /rust-exp.c
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index ffbdc3d..69d8a82 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,20 @@ 
+2016-10-02  Tom Tromey  <tom@tromey.com>
+
+	* observer.sh: Remove.
+	* observer.h: New file.
+	* observer.c (namespace gdb_observers): Define new objects.
+	(observer_debug): Move into gdb_observers namespace.
+	(struct observer, struct observer_list, xalloc_observer_list_node)
+	(xfree_observer_list_node, generic_observer_attach)
+	(generic_observer_detach, generic_observer_notify): Remove.
+	(_initialize_observer): Update.
+	Don't include observer.inc.
+	* gdb_function.h: New file.
+	* Makefile.in (generated_files): Remove observer.h, observer.inc.
+	(clean mostlyclean): Likewise.
+	(observer.h, observer.inc): Remove targets.
+	* .gitignore: Remove observer.h.
+
 2016-09-29  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	PR gdb/20609 - attach of JIT-debug-enabled inf 7.11.1 regression
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d0a968a..cf242b3 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1121,7 +1121,7 @@  DISTSTUFF = $(YYFILES)
 
 
 # All generated files which can be included by another file.
-generated_files = config.h observer.h observer.inc ada-lex.c jit-reader.h \
+generated_files = config.h ada-lex.c jit-reader.h \
 	$(GNULIB_H) $(NAT_GENERATED_FILES) gcore
 
 .c.o:
@@ -1450,7 +1450,7 @@  tags: TAGS
 clean mostlyclean: $(CONFIG_CLEAN)
 	@$(MAKE) $(FLAGS_TO_PASS) DO=clean "DODIRS=$(CLEANDIRS)" subdir_do
 	rm -f *.o *.a $(ADD_FILES) *~ init.c-tmp init.l-tmp version.c-tmp
-	rm -f init.c version.c observer.h observer.inc
+	rm -f init.c version.c
 	rm -f gdb$(EXEEXT) core make.log
 	rm -f gdb[0-9]$(EXEEXT)
 	rm -f test-cp-name-parser$(EXEEXT)
@@ -1631,12 +1631,6 @@  version.c: Makefile version.in $(srcdir)/../bfd/version.h $(srcdir)/common/creat
 	$(SHELL) $(srcdir)/common/create-version.sh $(srcdir) \
 	    $(host_alias) $(target_alias) version.c
 
-observer.h: observer.sh doc/observer.texi
-	${srcdir}/observer.sh h ${srcdir}/doc/observer.texi observer.h
-
-observer.inc: observer.sh doc/observer.texi
-	${srcdir}/observer.sh inc ${srcdir}/doc/observer.texi observer.inc
-
 lint: $(LINTFILES)
 	$(LINT) $(INCLUDE_CFLAGS) $(LINTFLAGS) $(LINTFILES) \
 	   `echo $(DEPFILES) $(CONFIG_OBS) | sed 's/\.o /\.c /g'`
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index d0b071a..cf5a7af 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,7 @@ 
+2016-10-02  Tom Tromey  <tom@tromey.com>
+
+	* observer.texi: Remove.
+
 2016-09-28  Tom Tromey  <tom@tromey.com>
 
 	* gdb.texinfo (Packets) <z0>: Use "software breakpoint" rather
diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi
deleted file mode 100644
index fc7aac4..0000000
--- a/gdb/doc/observer.texi
+++ /dev/null
@@ -1,309 +0,0 @@ 
-@c -*-texinfo-*-
-
-@c This file is part of the GDB manual.
-@c
-@c Copyright (C) 2003-2016 Free Software Foundation, Inc.
-@c
-@c See the file gdbint.texinfo for copying conditions.
-@c
-@c Also, the @deftypefun lines from this file are processed into a
-@c header file during the GDB build process.  Permission is granted
-@c to redistribute and/or modify those lines under the terms of the
-@c GNU General Public License as published by the Free Software
-@c Foundation; either version 3 of the License, or (at your option)
-@c any later version.
-
-@node GDB Observers
-@appendix @value{GDBN} Currently available observers
-
-@section Implementation rationale
-@cindex observers implementation rationale
-
-An @dfn{observer} is an entity which is interested in being notified
-when GDB reaches certain states, or certain events occur in GDB.
-The entity being observed is called the @dfn{subject}.  To receive
-notifications, the observer attaches a callback to the subject.
-One subject can have several observers.
-
-@file{observer.c} implements an internal generic low-level event
-notification mechanism.  This generic event notification mechanism is
-then re-used to implement the exported high-level notification
-management routines for all possible notifications.
-
-The current implementation of the generic observer provides support
-for contextual data.  This contextual data is given to the subject
-when attaching the callback.  In return, the subject will provide
-this contextual data back to the observer as a parameter of the
-callback.
-
-Note that the current support for the contextual data is only partial,
-as it lacks a mechanism that would deallocate this data when the
-callback is detached.  This is not a problem so far, as this contextual
-data is only used internally to hold a function pointer.  Later on, if
-a certain observer needs to provide support for user-level contextual
-data, then the generic notification mechanism will need to be
-enhanced to allow the observer to provide a routine to deallocate the
-data when attaching the callback.
-
-The observer implementation is also currently not reentrant.
-In particular, it is therefore not possible to call the attach
-or detach routines during a notification.
-
-@section Debugging
-Observer notifications can be traced using the command @samp{set debug
-observer 1} (@pxref{Debugging Output, , Optional messages about
-internal happenings, gdb, Debugging with @var{GDBN}}).
-
-@section @code{normal_stop} Notifications
-@cindex @code{normal_stop} observer
-@cindex notification about inferior execution stop
-
-@value{GDBN} notifies all @code{normal_stop} observers when the
-inferior execution has just stopped, the associated messages and
-annotations have been printed, and the control is about to be returned
-to the user.
-
-Note that the @code{normal_stop} notification is not emitted when
-the execution stops due to a breakpoint, and this breakpoint has
-a condition that is not met.  If the breakpoint has any associated
-commands list, the commands are executed after the notification
-is emitted.
-
-The following interfaces are available to manage observers:
-
-@deftypefun extern struct observer *observer_attach_@var{event} (observer_@var{event}_ftype *@var{f})
-Using the function @var{f}, create an observer that is notified when
-ever @var{event} occurs, return the observer.
-@end deftypefun
-
-@deftypefun extern void observer_detach_@var{event} (struct observer *@var{observer});
-Remove @var{observer} from the list of observers to be notified when
-@var{event} occurs.
-@end deftypefun
-
-@deftypefun extern void observer_notify_@var{event} (void);
-Send a notification to all @var{event} observers.
-@end deftypefun
-
-The following observable events are defined:
-
-@deftypefun void normal_stop (struct bpstats *@var{bs}, int @var{print_frame})
-The inferior has stopped for real.  The  @var{bs} argument describes
-the breakpoints were are stopped at, if any.  Second argument
-@var{print_frame} non-zero means display the location where the
-inferior has stopped.
-@end deftypefun
-
-@deftypefun void signal_received (enum gdb_signal @var{siggnal})
-The inferior was stopped by a signal.
-@end deftypefun
-
-@deftypefun void end_stepping_range (void)
-We are done with a step/next/si/ni command.
-@end deftypefun
-
-@deftypefun void signal_exited (enum gdb_signal @var{siggnal})
-The inferior was terminated by a signal.
-@end deftypefun
-
-@deftypefun void exited (int @var{exitstatus})
-The inferior program is finished.
-@end deftypefun
-
-@deftypefun void no_history (void)
-Reverse execution: target ran out of history info.
-@end deftypefun
-
-@deftypefun void sync_execution_done (void)
-A synchronous command finished.
-@end deftypefun
-
-@deftypefun void command_error (void)
-An error was caught while executing a command.
-@end deftypefun
-
-@deftypefun void target_changed (struct target_ops *@var{target})
-The target's register contents have changed.
-@end deftypefun
-
-@deftypefun void executable_changed (void)
-The executable being debugged by GDB has changed: The user decided
-to debug a different program, or the program he was debugging has
-been modified since being loaded by the debugger (by being recompiled,
-for instance).
-@end deftypefun
-
-@deftypefun void inferior_created (struct target_ops *@var{objfile}, int @var{from_tty})
-@value{GDBN} has just connected to an inferior.  For @samp{run},
-@value{GDBN} calls this observer while the inferior is still stopped
-at the entry-point instruction.  For @samp{attach} and @samp{core},
-@value{GDBN} calls this observer immediately after connecting to the
-inferior, and before any information on the inferior has been printed.
-@end deftypefun
-
-@deftypefun void record_changed (struct inferior *@var{inferior}, int @var{started}, const char *@var{method}, const char *@var{format})
-The status of process record for inferior @var{inferior} in
-@value{GDBN} has changed.  The process record is started if
-@var{started} is true, and the process record is stopped if
-@var{started} is false.
-
-When @var{started} is true, @var{method} indicates the short name of the method
-used for recording.  If the method supports multiple formats, @var{format}
-indicates which one is being used, otherwise it is NULL.  When @var{started} is
-false, they are both NULL.
-@end deftypefun
-
-@deftypefun void solib_loaded (struct so_list *@var{solib})
-The shared library specified by @var{solib} has been loaded.  Note that
-when @value{GDBN} calls this observer, the library's symbols probably
-haven't been loaded yet.
-@end deftypefun
-
-@deftypefun void solib_unloaded (struct so_list *@var{solib})
-The shared library specified by @var{solib} has been unloaded.
-Note that when @value{GDBN} calls this observer, the library's
-symbols have not been unloaded yet, and thus are still available.
-@end deftypefun
-
-@deftypefun void new_objfile (struct objfile *@var{objfile})
-The symbol file specified by @var{objfile} has been loaded.
-Called with @var{objfile} equal to @code{NULL} to indicate
-previously loaded symbol table data has now been invalidated.
-@end deftypefun
-
-@deftypefun void free_objfile (struct objfile *@var{objfile})
-The object file specified by @var{objfile} is about to be freed.
-@end deftypefun
-
-@deftypefun void new_thread (struct thread_info *@var{t})
-The thread specified by @var{t} has been created.
-@end deftypefun
-
-@deftypefun void thread_exit (struct thread_info *@var{t}, int @var{silent})
-The thread specified by @var{t} has exited.  The @var{silent} argument
-indicates that @value{GDBN} is removing the thread from its tables
-without wanting to notify the user about it.
-@end deftypefun
-
-@deftypefun void thread_stop_requested (ptid_t @var{ptid})
-An explicit stop request was issued to @var{ptid}.  If @var{ptid}
-equals @var{minus_one_ptid}, the request applied to all threads.  If
-@code{ptid_is_pid(ptid)} returns true, the request applied to all
-threads of the process pointed at by @var{ptid}.  Otherwise, the
-request applied to the single thread pointed at by @var{ptid}.
-@end deftypefun
-
-@deftypefun void target_resumed (ptid_t @var{ptid})
-The target was resumed.  The @var{ptid} parameter specifies which
-thread was resume, and may be RESUME_ALL if all threads are resumed.
-@end deftypefun
-
-@deftypefun void about_to_proceed (void)
-The target is about to be proceeded.
-@end deftypefun
-
-@deftypefun void breakpoint_created (struct breakpoint *@var{b})
-A new breakpoint @var{b} has been created.
-@end deftypefun
-
-@deftypefun void breakpoint_deleted (struct breakpoint *@var{b})
-A breakpoint has been destroyed.  The argument @var{b} is the
-pointer to the destroyed breakpoint.
-@end deftypefun
-
-@deftypefun void breakpoint_modified (struct breakpoint *@var{b})
-A breakpoint has been modified in some way.  The argument @var{b}
-is the modified breakpoint.
-@end deftypefun
-
-@deftypefun void traceframe_changed (int @var{tfnum}, int @var{tpnum})
-The trace frame is changed to @var{tfnum} (e.g., by using the
-@code{tfind} command).  If @var{tfnum} is negative, it means
-@value{GDBN} resumes live debugging.  The number of the tracepoint
-associated with this traceframe is @var{tpnum}.
-@end deftypefun
-
-@deftypefun void architecture_changed (struct gdbarch *@var{newarch})
-The current architecture has changed.  The argument @var{newarch} is
-a pointer to the new architecture.
-@end deftypefun
-
-@deftypefun void thread_ptid_changed (ptid_t @var{old_ptid}, ptid_t @var{new_ptid})
-The thread's ptid has changed.  The @var{old_ptid} parameter specifies
-the old value, and @var{new_ptid} specifies the new value.
-@end deftypefun
-
-@deftypefun void inferior_added (struct inferior *@var{inf})
-The inferior @var{inf} has been added to the list of inferiors.  At
-this point, it might not be associated with any process.
-@end deftypefun
-
-@deftypefun void inferior_appeared (struct inferior *@var{inf})
-The inferior identified by @var{inf} has been attached to a process.
-@end deftypefun
-
-@deftypefun void inferior_exit (struct inferior *@var{inf})
-Either the inferior associated with @var{inf} has been detached from the
-process, or the process has exited.
-@end deftypefun
-
-@deftypefun void inferior_removed (struct inferior *@var{inf})
-The inferior @var{inf} has been removed from the list of inferiors.
-This method is called immediately before freeing @var{inf}.
-@end deftypefun
-
-@deftypefun void memory_changed (struct inferior *@var{inferior}, CORE_ADDR @var{addr}, ssize_t @var{len}, const bfd_byte *@var{data})
-Bytes from @var{data} to @var{data} + @var{len} have been written
-to the @var{inferior} at @var{addr}.
-@end deftypefun
-
-@deftypefun void before_prompt (const char *@var{current_prompt})
-Called before a top-level prompt is displayed.  @var{current_prompt} is
-the current top-level prompt.
-@end deftypefun
-
-@deftypefun void gdb_datadir_changed (void)
-Variable gdb_datadir has been set.  The value may not necessarily change.
-@end deftypefun
-
-@deftypefun void command_param_changed (const char *@var{param}, const char *@var{value})
-The parameter of some @code{set} commands in console are changed.  This
-method is called after a command @code{set @var{param} @var{value}}.
-@var{param} is the parameter of @code{set} command, and @var{value}
-is the value of changed parameter.
-@end deftypefun
-
-@deftypefun void tsv_created (const struct trace_state_variable *@var{tsv})
-The new trace state variable @var{tsv} is created.
-@end deftypefun
-
-@deftypefun void tsv_deleted (const struct trace_state_variable *@var{tsv})
-The trace state variable @var{tsv} is deleted.  If @var{tsv} is
-@code{NULL}, all trace state variables are deleted.
-@end deftypefun
-
-@deftypefun void tsv_modified (const struct trace_state_variable *@var{tsv})
-The trace state value @var{tsv} is modified.
-@end deftypefun
-
-@deftypefun void inferior_call_pre (ptid_t @var{thread}, CORE_ADDR @var{address})
-An inferior function at @var{address} is about to be called in thread
-@var{thread}.
-@end deftypefun
-
-@deftypefun void inferior_call_post (ptid_t @var{thread}, CORE_ADDR @var{address})
-The inferior function at @var{address} has just been called.  This observer
-is called even if the inferior exits during the call.  @var{thread} is the
-thread in which the function was called, which may be different from the
-current thread.
-@end deftypefun
-
-@deftypefun void register_changed (struct frame_info *@var{frame}, int @var{regnum})
-A register in the inferior has been modified by the @value{GDBN} user.
-@end deftypefun
-
-@deftypefun void test_notification (int @var{somearg})
-This observer is used for internal testing.  Do not use.  
-See testsuite/gdb.gdb/observer.exp.
-@end deftypefun
-
diff --git a/gdb/gdb_function.h b/gdb/gdb_function.h
new file mode 100644
index 0000000..2b8b5b1
--- /dev/null
+++ b/gdb/gdb_function.h
@@ -0,0 +1,149 @@ 
+/* Functions
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#if !defined (GDB_FUNCTION_H)
+#define GDB_FUNCTION_H
+
+// Replacements for std::function.  This only implements the subset of
+// the API needed by gdb.  Because we don't have variadic templates,
+// there is a separate template for each required number of arguments.
+// Also, this doesn't do perfect forwarding correctly, so should only
+// be used with C types.
+
+template<typename R>
+class gdb_function0
+{
+  typedef R self_func_type ();
+
+ public:
+
+  gdb_function0 () : m_func (NULL)
+  {
+  }
+
+  gdb_function0 (self_func_type *func) : m_func (func)
+  {
+  }
+
+  R operator() () const
+  {
+    return m_func ();
+  }
+
+  bool operator== (const gdb_function0 &other)
+  {
+    return m_func == other.m_func;
+  }
+
+ private:
+
+  self_func_type *m_func;
+};
+
+template<typename R, typename A>
+class gdb_function1
+{
+  typedef R self_func_type (A);
+
+ public:
+
+  gdb_function1 () : m_func (NULL)
+  {
+  }
+
+  gdb_function1 (self_func_type *func) : m_func (func)
+  {
+  }
+
+  R operator() (A a) const
+  {
+    return m_func (a);
+  }
+
+  bool operator== (const gdb_function1 &other)
+  {
+    return m_func == other.m_func;
+  }
+
+ private:
+
+  self_func_type *m_func;
+};
+
+template<typename R, typename A1, typename A2>
+class gdb_function2
+{
+  typedef R self_func_type (A1, A2);
+
+ public:
+
+  gdb_function2 () : m_func (NULL)
+  {
+  }
+
+  gdb_function2 (self_func_type *func) : m_func (func)
+  {
+  }
+
+  R operator() (A1 a1, A2 a2) const
+  {
+    return m_func (a1, a2);
+  }
+
+  bool operator== (const gdb_function2 &other)
+  {
+    return m_func == other.m_func;
+  }
+
+ private:
+
+  self_func_type *m_func;
+};
+
+template<typename R, typename A1, typename A2, typename A3, typename A4>
+class gdb_function4
+{
+  typedef R self_func_type (A1, A2, A3, A4);
+
+ public:
+
+  gdb_function4 () : m_func (NULL)
+  {
+  }
+
+  gdb_function4 (self_func_type *func) : m_func (func)
+  {
+  }
+
+  R operator() (A1 a1, A2 a2, A3 a3, A4 a4) const
+  {
+    return m_func (a1, a2, a3, a4);
+  }
+
+  bool operator== (const gdb_function4 &other)
+  {
+    return m_func == other.m_func;
+  }
+
+ private:
+
+  self_func_type *m_func;
+};
+
+#endif /* GDB_FUNCTION_H */
diff --git a/gdb/observer.c b/gdb/observer.c
index d678eea..ae7b725 100644
--- a/gdb/observer.c
+++ b/gdb/observer.c
@@ -17,159 +17,109 @@ 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-/* An observer is an entity who is interested in being notified when GDB
-   reaches certain states, or certain events occur in GDB.  The entity being
-   observed is called the Subject.  To receive notifications, the observer
-   attaches a callback to the subject.  One subject can have several
-   observers.
-
-   This file implements an internal generic low-level event notification
-   mechanism based on the Observer paradigm described in the book "Design
-   Patterns".  This generic event notification mechansim is then re-used
-   to implement the exported high-level notification management routines
-   for all possible notifications.
-
-   The current implementation of the generic observer provides support
-   for contextual data.  This contextual data is given to the subject
-   when attaching the callback.  In return, the subject will provide
-   this contextual data back to the observer as a parameter of the
-   callback.
-
-   FIXME: The current support for the contextual data is only partial,
-   as it lacks a mechanism that would deallocate this data when the
-   callback is detached.  This is not a problem so far, as this contextual
-   data is only used internally to hold a function pointer.  Later on,
-   if a certain observer needs to provide support for user-level
-   contextual data, then the generic notification mechanism will need
-   need to be enhanced to allow the observer to provide a routine to
-   deallocate the data when attaching the callback.
-
-   This file is currently maintained by hand, but the long term plan
-   if the number of different notifications starts growing is to create
-   a new script (observer.sh) that would generate this file, and the
-   associated documentation.  */
-
 #include "defs.h"
 #include "observer.h"
 #include "command.h"
 #include "gdbcmd.h"
 
-static unsigned int observer_debug;
-static void
-show_observer_debug (struct ui_file *file, int from_tty,
-		     struct cmd_list_element *c, const char *value)
-{
-  fprintf_filtered (file, _("Observer debugging is %s.\n"), value);
-}
-
-/* The internal generic observer.  */
-
-typedef void (generic_observer_notification_ftype) (const void *data,
-						    const void *args);
-
-struct observer
-{
-  generic_observer_notification_ftype *notify;
-  /* No memory management needed for the following field for now.  */
-  void *data;
-};
-
-/* A list of observers, maintained by the subject.  A subject is
-   actually represented by its list of observers.  */
-
-struct observer_list
-{
-  struct observer_list *next;
-  struct observer *observer;
-};
-
-/* Allocate a struct observer_list, intended to be used as a node
-   in the list of observers maintained by a subject.  */
-
-static struct observer_list *
-xalloc_observer_list_node (void)
-{
-  struct observer_list *node = XNEW (struct observer_list);
-
-  node->observer = XNEW (struct observer);
-  return node;
-}
-
-/* The opposite of xalloc_observer_list_node, frees the memory for
-   the given node.  */
-
-static void
-xfree_observer_list_node (struct observer_list *node)
-{
-  xfree (node->observer);
-  xfree (node);
-}
-
-/* Attach the callback NOTIFY to a SUBJECT.  The DATA is also stored,
-   in order for the subject to provide it back to the observer during
-   a notification.  */
-
-static struct observer *
-generic_observer_attach (struct observer_list **subject,
-			 generic_observer_notification_ftype * notify,
-			 void *data)
-{
-  struct observer_list *observer_list = xalloc_observer_list_node ();
-
-  observer_list->next = *subject;
-  observer_list->observer->notify = notify;
-  observer_list->observer->data = data;
-  *subject = observer_list;
-
-  return observer_list->observer;
-}
-
-/* Remove the given OBSERVER from the SUBJECT.  Once detached, OBSERVER
-   should no longer be used, as it is no longer valid.  */
-
-static void
-generic_observer_detach (struct observer_list **subject,
-			 const struct observer *observer)
+namespace gdb_observers
 {
-  struct observer_list *previous_node = NULL;
-  struct observer_list *current_node = *subject;
-
-  while (current_node != NULL)
-    {
-      if (current_node->observer == observer)
-	{
-	  if (previous_node != NULL)
-	    previous_node->next = current_node->next;
-	  else
-	    *subject = current_node->next;
-	  xfree_observer_list_node (current_node);
-	  return;
-	}
-      previous_node = current_node;
-      current_node = current_node->next;
-    }
-
-  /* We should never reach this point.  However, this should not be
-     a very serious error, so simply report a warning to the user.  */
-  warning (_("Failed to detach observer"));
+  unsigned int observer_debug;
+
+  observer2<struct bpstats *, int>
+    normal_stop ("normal_stop");
+  observer1<enum gdb_signal>
+    signal_received ("signal_received");
+  observer0
+    end_stepping_range ("end_stepping_range");
+  observer1<enum gdb_signal>
+    signal_exited ("signal_exited");
+  observer1<int>
+    exited ("exited");
+  observer0
+    no_history ("no_history");
+  observer0
+    sync_execution_done ("sync_execution_done");
+  observer0
+    command_error ("command_error");
+  observer1<struct target_ops *>
+    target_changed ("target_changed");
+  observer0
+    executable_changed ("executable_changed");
+  observer2<struct target_ops *, int>
+    inferior_created ("inferior_created");
+  observer4<struct inferior *, int, const char *, const char *>
+    record_changed ("record_changed");
+  observer1<struct so_list *>
+    solib_loaded ("solib_loaded");
+  observer1<struct so_list *>
+    solib_unloaded ("solib_unloaded");
+  observer1<struct objfile *>
+    new_objfile ("new_objfile");
+  observer1<struct objfile *>
+    free_objfile ("free_objfile");
+  observer1<struct thread_info *>
+    new_thread ("new_thread");
+  observer2<struct thread_info *, int>
+    thread_exit ("thread_exit");
+  observer1<ptid_t>
+    thread_stop_requested ("thread_stop_requested");
+  observer1<ptid_t>
+    target_resumed ("target_resumed");
+  observer0
+    about_to_proceed ("about_to_proceed");
+  observer1<struct breakpoint *>
+    breakpoint_created ("breakpoint_created");
+  observer1<struct breakpoint *>
+    breakpoint_deleted ("breakpoint_deleted");
+  observer1<struct breakpoint *>
+    breakpoint_modified ("breakpoint_modified");
+  observer2<int, int>
+    traceframe_changed ("traceframe_changed");
+  observer1<struct gdbarch *>
+    architecture_changed ("architecture_changed");
+  observer2<ptid_t, ptid_t>
+    thread_ptid_changed ("thread_ptid_changed");
+  observer1<struct inferior *>
+    inferior_added ("inferior_added");
+  observer1<struct inferior *>
+    inferior_appeared ("inferior_appeared");
+  observer1<struct inferior *>
+    inferior_exit ("inferior_exit");
+  observer1<struct inferior *>
+    inferior_removed ("inferior_removed");
+  observer4<struct inferior *, CORE_ADDR,
+    ssize_t, const bfd_byte *>
+    memory_changed ("memory_changed");
+  observer1<const char *>
+    before_prompt ("before_prompt");
+  observer0
+    gdb_datadir_changed ("gdb_datadir_changed");
+  observer2<const char *, const char *>
+    command_param_changed ("command_param_changed");
+  observer1<const struct trace_state_variable *>
+    tsv_created ("tsv_created");
+  observer1<const struct trace_state_variable *>
+    tsv_deleted ("tsv_deleted");
+  observer1<const struct trace_state_variable *>
+    tsv_modified ("tsv_modified");
+  observer2<ptid_t, CORE_ADDR>
+    inferior_call_pre ("inferior_call_pre");
+  observer2<ptid_t, CORE_ADDR>
+    inferior_call_post ("inferior_call_post");
+  observer2<struct frame_info *, int>
+    register_changed ("register_changed");
+  observer1<int>
+    test_notification ("test_notification");
 }
 
-/* Send a notification to all the observers of SUBJECT.  ARGS is passed to
-   all observers as an argument to the notification callback.  */
-
 static void
-generic_observer_notify (struct observer_list *subject, const void *args)
+show_observer_debug (struct ui_file *file, int from_tty,
+		     struct cmd_list_element *c, const char *value)
 {
-  struct observer_list *current_node = subject;
-
-  while (current_node != NULL)
-    {
-      (*current_node->observer->notify) (current_node->observer->data, args);
-      current_node = current_node->next;
-    }
+  fprintf_filtered (file, _("Observer debugging is %s.\n"), value);
 }
 
-
 /* The following code is only used to unit-test the observers from our
    testsuite.  DO NOT USE IT within observer.c (or anywhere else for
    that matter)!  */
@@ -210,7 +160,7 @@  void
 _initialize_observer (void)
 {
   add_setshow_zuinteger_cmd ("observer", class_maintenance,
-			     &observer_debug, _("\
+			     &gdb_observers::observer_debug, _("\
 Set observer debugging."), _("\
 Show observer debugging."), _("\
 When non-zero, observer debugging is enabled."),
@@ -218,5 +168,3 @@  When non-zero, observer debugging is enabled."),
 			     show_observer_debug,
 			     &setdebuglist, &showdebuglist);
 }
-
-#include "observer.inc"
diff --git a/gdb/observer.h b/gdb/observer.h
new file mode 100644
index 0000000..0fa5ab6
--- /dev/null
+++ b/gdb/observer.h
@@ -0,0 +1,394 @@ 
+/* Observers
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#if !defined (GDB_OBSERVER_H)
+#define GDB_OBSERVER_H
+
+#include "gdb_function.h"
+#include <forward_list>
+
+struct bpstats;
+struct so_list;
+struct objfile;
+struct thread_info;
+struct inferior;
+struct trace_state_variable;
+
+namespace gdb_observers
+{
+  extern unsigned int observer_debug;
+
+  // An observer is an entity which is interested in being notified when
+  // GDB reaches certain states, or certain events occur in GDB.  The
+  // entity being observed is called the subject.  To receive
+  // notifications, the observer attaches a callback to the subject.
+  // One subject can have several observers.
+  //
+  // The observer implementation is also currently not reentrant.
+  // In particular, it is therefore not possible to call the attach
+  // or detach routines during a notification.
+  //
+  // Because we don't have varied templates, we have one template for
+  // each needed number of arguments.
+  class observer0
+  {
+  public:
+
+    typedef gdb_function0<void> func_type;
+
+    observer0 (const char *name) : m_name (name)
+    {
+    }
+
+    func_type attach (const func_type &f)
+    {
+      m_observers.push_front (f);
+      return f;
+    }
+
+    void detach (const func_type &f)
+    {
+      m_observers.remove (f);
+    }
+
+    void notify () const
+    {
+      if (observer_debug)
+	fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n",
+			    m_name);
+      for (iter_type iter = m_observers.begin ();
+	   iter != m_observers.end ();
+	   ++iter)
+	(*iter) ();
+    }
+
+  private:
+
+    typedef std::forward_list<func_type>::const_iterator iter_type;
+
+    std::forward_list<func_type> m_observers;
+    const char *m_name;
+  };
+
+  template<typename A>
+    class observer1
+    {
+    public:
+
+      typedef gdb_function1<void, A> func_type;
+
+      observer1 (const char *name) : m_name (name)
+      {
+      }
+
+      func_type attach (const func_type &f)
+      {
+	m_observers.push_front (f);
+	return f;
+      }
+
+      void detach (const func_type &f)
+      {
+	m_observers.remove (f);
+      }
+
+      void notify (A arg) const
+      {
+	if (observer_debug)
+	  fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n",
+			      m_name);
+	for (iter_type iter = m_observers.begin ();
+	     iter != m_observers.end ();
+	     ++iter)
+	  (*iter) (arg);
+      }
+
+    private:
+
+      typedef typename std::forward_list<func_type>::const_iterator iter_type;
+
+      std::forward_list<func_type> m_observers;
+      const char *m_name;
+    };
+
+
+  template<typename A1, typename A2>
+    class observer2
+  {
+  public:
+
+    typedef gdb_function2<void, A1, A2> func_type;
+
+    observer2 (const char *name) : m_name (name)
+    {
+    }
+
+    func_type attach (const func_type &f)
+    {
+      m_observers.push_front (f);
+      return f;
+    }
+
+    void detach (const func_type &f)
+    {
+      m_observers.remove (f);
+    }
+
+    void notify (A1 arg1, A2 arg2) const
+    {
+      if (observer_debug)
+	fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n",
+			    m_name);
+      for (iter_type iter = m_observers.begin ();
+	   iter != m_observers.end ();
+	   ++iter)
+	(*iter) (arg1, arg2);
+    }
+
+  private:
+
+    typedef typename std::forward_list<func_type>::const_iterator iter_type;
+
+    std::forward_list<func_type> m_observers;
+    const char *m_name;
+  };
+
+  template<typename A1, typename A2, typename A3, typename A4>
+    class observer4
+  {
+  public:
+
+    typedef gdb_function4<void, A1, A2, A3, A4> func_type;
+
+    observer4 (const char *name) : m_name (name)
+    {
+    }
+
+    func_type attach (const func_type &f)
+    {
+      m_observers.push_front (f);
+      return f;
+    }
+
+    void detach (const func_type &f)
+    {
+      m_observers.remove (f);
+    }
+
+    void notify (A1 arg1, A2 arg2, A3 arg3, A4 arg4) const
+    {
+      if (observer_debug)
+	fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n",
+			    m_name);
+      for (iter_type iter = m_observers.begin ();
+	   iter != m_observers.end ();
+	   ++iter)
+	(*iter) (arg1, arg2, arg3, arg4);
+    }
+
+  private:
+
+    typedef typename std::forward_list<func_type>::const_iterator iter_type;
+
+    std::forward_list<func_type> m_observers;
+    const char *m_name;
+  };
+
+  // The inferior has stopped for real.  The bs argument
+  // describes the breakpoints were are stopped at, if any.  Second
+  // argument print_frame non-zero means display the location
+  // where the inferior has stopped.
+  extern observer2<struct bpstats *, int> normal_stop;
+
+  // The inferior was stopped by a signal.
+  extern observer1<enum gdb_signal> signal_received;
+
+  // We are done with a step/next/si/ni command.
+  extern observer0 end_stepping_range;
+
+  // The inferior was terminated by a signal.
+  extern observer1<enum gdb_signal> signal_exited;
+
+  // The inferior program is finished.
+  extern observer1<int> exited;
+
+  // Reverse execution: target ran out of history info.
+  extern observer0 no_history;
+
+  // A synchronous command finished.
+  extern observer0 sync_execution_done;
+
+  // An error was caught while executing a command.
+  extern observer0 command_error;
+
+  // The target's register contents have changed.
+  extern observer1<struct target_ops *> target_changed;
+
+  // The executable being debugged by GDB has changed: The user decided
+  // to debug a different program, or the program he was debugging has
+  // been modified since being loaded by the debugger (by being recompiled,
+  // for instance).
+  extern observer0 executable_changed;
+
+  // gdb has just connected to an inferior.  For 'run',
+  // gdb calls this observer while the inferior is still stopped
+  // at the entry-point instruction.  For 'attach' and 'core',
+  // gdb calls this observer immediately after connecting to the
+  // inferior, and before any information on the inferior has been printed.
+  extern observer2<struct target_ops *, int> inferior_created;
+
+  // The status of process record for inferior inferior in
+  // gdb has changed.  The process record is started if
+  // started is true, and the process record is stopped if
+  // started is false.
+  //
+  // When started is true, method indicates the short name
+  // of the method used for recording.  If the method supports multiple
+  // formats, format indicates which one is being used, otherwise
+  // it is NULL.  When started is false, they are both NULL.
+  extern observer4<struct inferior *, int, const char *, const char *>
+    record_changed;
+
+  // The shared library specified by solib has been loaded.  Note that
+  // when gdb calls this observer, the library's symbols probably
+  // haven't been loaded yet.
+  extern observer1<struct so_list *> solib_loaded;
+
+  // The shared library specified by solib has been unloaded.
+  // Note that when gdb calls this observer, the library's
+  // symbols have not been unloaded yet, and thus are still available.
+  extern observer1<struct so_list *> solib_unloaded;
+
+  // The symbol file specified by objfile has been loaded.
+  // Called with objfile equal to NULL to indicate
+  // previously loaded symbol table data has now been invalidated.
+  extern observer1<struct objfile *> new_objfile;
+
+  // The object file specified by objfile is about to be freed.
+  extern observer1<struct objfile *> free_objfile;
+
+  // The thread specified by t has been created.
+  extern observer1<struct thread_info *> new_thread;
+
+  // The thread specified by t has exited.  The silent argument
+  // indicates that gdb is removing the thread from its tables
+  // without wanting to notify the user about it.
+  extern observer2<struct thread_info *, int> thread_exit;
+
+  // An explicit stop request was issued to ptid.  If ptid
+  // equals minus_one_ptid, the request applied to all threads.  If
+  // ptid_is_pid(ptid) returns true, the request applied to all
+  // threads of the process pointed at by ptid.  Otherwise, the
+  // request applied to the single thread pointed at by ptid.
+  extern observer1<ptid_t> thread_stop_requested;
+
+  // The target was resumed.  The ptid parameter specifies which
+  // thread was resume, and may be RESUME_ALL if all threads are resumed.
+  extern observer1<ptid_t> target_resumed;
+
+  // The target is about to be proceeded.
+  extern observer0 about_to_proceed;
+
+  // A new breakpoint b has been created.
+  extern observer1<struct breakpoint *> breakpoint_created;
+
+  // A breakpoint has been destroyed.  The argument b is the
+  // pointer to the destroyed breakpoint.
+  extern observer1<struct breakpoint *> breakpoint_deleted;
+
+  // A breakpoint has been modified in some way.  The argument b
+  // is the modified breakpoint.
+  extern observer1<struct breakpoint *> breakpoint_modified;
+
+  // The trace frame is changed to tfnum (e.g., by using the
+  // 'tfind' command).  If tfnum is negative, it means
+  // gdb resumes live debugging.  The number of the tracepoint
+  // associated with this traceframe is tpnum.
+  extern observer2<int, int> traceframe_changed;
+
+  // The current architecture has changed.  The argument newarch is
+  // a pointer to the new architecture.
+  extern observer1<struct gdbarch *> architecture_changed;
+
+  // The thread's ptid has changed.  The old_ptid parameter specifies
+  // the old value, and new_ptid specifies the new value.
+  extern observer2<ptid_t, ptid_t> thread_ptid_changed;
+
+  // The inferior inf has been added to the list of inferiors.  At
+  // this point, it might not be associated with any process.
+  extern observer1<struct inferior *> inferior_added;
+
+  // The inferior identified by inf has been attached to a process.
+  extern observer1<struct inferior *> inferior_appeared;
+
+  // Either the inferior associated with inf has been detached from the
+  // process, or the process has exited.
+  extern observer1<struct inferior *> inferior_exit;
+
+  // The inferior inf has been removed from the list of inferiors.
+  // This method is called immediately before freeing inf.
+  extern observer1<struct inferior *> inferior_removed;
+
+  // Bytes from data to data + len have been written
+  // to the inferior at addr.
+  extern observer4<struct inferior *, CORE_ADDR, ssize_t, const bfd_byte *>
+    memory_changed;
+
+  // Called before a top-level prompt is displayed.  current_prompt is
+  // the current top-level prompt.
+  extern observer1<const char *> before_prompt;
+
+  // Variable gdb_datadir has been set.  The value may not necessarily change.
+  extern observer0 gdb_datadir_changed;
+
+  // The parameter of some 'set' commands in console are changed.  This
+  // method is called after a command 'set param value'.
+  // param is the parameter of 'set' command, and value
+  // is the value of changed parameter.
+  extern observer2<const char *, const char *> command_param_changed;
+
+  // The new trace state variable tsv is created.
+  extern observer1<const struct trace_state_variable *> tsv_created;
+
+  // The trace state variable tsv is deleted.  If tsv is
+  // NULL, all trace state variables are deleted.
+  extern observer1<const struct trace_state_variable *> tsv_deleted;
+
+  // The trace state value tsv is modified.
+  extern observer1<const struct trace_state_variable *> tsv_modified;
+
+  // An inferior function at address is about to be called in thread
+  // thread.
+  extern observer2<ptid_t, CORE_ADDR> inferior_call_pre;
+
+  // The inferior function at address has just been called.  This observer
+  // is called even if the inferior exits during the call.  thread is the
+  // thread in which the function was called, which may be different from the
+  // current thread.
+  extern observer2<ptid_t, CORE_ADDR> inferior_call_post;
+
+  // A register in the inferior has been modified by the gdb user.
+  extern observer2<struct frame_info *, int> register_changed;
+
+  // This observer is used for internal testing.  Do not use.
+  // See testsuite/gdb.gdb/observer.exp.
+  extern observer1<int> test_notification;
+}
+
+#endif /* GDB_OBSERVER_H */
diff --git a/gdb/observer.sh b/gdb/observer.sh
deleted file mode 100755
index cc613a7..0000000
--- a/gdb/observer.sh
+++ /dev/null
@@ -1,200 +0,0 @@ 
-#!/bin/sh -e
-
-# Make certain that the script is not running in an internationalized
-# environment.
-LANG=C ; export LANG
-LC_ALL=C ; export LC_ALL
-
-if test $# -ne 3
-then
-    echo "Usage: $0 <h|inc> <observer.texi> <observer.out>" 1>&2
-    exit 0
-fi
-
-lang=$1 ; shift
-texi=$1 ; shift
-o=$1
-case $lang in
-  h) tmp=htmp ;;
-  inc) tmp=itmp ;;
-esac
-otmp="`echo $1 | sed -e 's,\.[^.]*$,,'`.$tmp"; shift
-echo "Creating ${otmp}" 1>&2
-rm -f ${otmp}
-
-# Can use any of the following: cat cmp cp diff echo egrep expr false
-# grep install-info ln ls mkdir mv pwd rm rmdir sed sleep sort tar
-# test touch true
-
-cat <<EOF >>${otmp}
-/* GDB Notifications to Observers.
-
-   Copyright (C) 2004-2016 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-  
-   This program 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 General Public License for more details.
-  
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-   --
-
-   This file was generated using observer.sh and observer.texi.  */
-
-EOF
-
-
-case $lang in
-    h) cat <<EOF >>${otmp}
-#ifndef OBSERVER_H
-#define OBSERVER_H
-
-struct observer;
-struct bpstats;
-struct so_list;
-struct objfile;
-struct thread_info;
-struct inferior;
-struct trace_state_variable;
-EOF
-        ;;
-esac
-
-# We are about to set IFS=:, so DOS-style file names with a drive
-# letter and a colon will be in trouble.
-
-if test -n "$DJGPP"
-then
-     texi=`echo $texi | sed -e 's,^\([a-zA-Z]\):/,/dev/\1/,'`
-fi
-
-# generate a list of events that can be observed
-
-IFS=:
-sed -n '
-/@deftypefun void/{
-# Save original line for later processing into the actual parameter
-    h
-# Convert from: @deftypefun void EVENT (TYPE @var{PARAM},...)
-# to event and formals: EVENT:TYPE PARAM, ...:
-    s/^.* void \([a-z_][a-z_]*\) (\(.*\))$/\1:\2/
-    s/@var{//g
-    s/}//g
-# Switch to held
-    x
-# Convert from: @deftypefun void FUNC (TYPE @var{PARAM},...)
-# to actuals: PARAM, ...
-    s/^[^{]*[{]*//
-    s/[}]*[^}]*$//
-    s/}[^{]*{/, /g
-# Combine held (EVENT:TYPE PARAM, ...:) and pattern (PARAM, ...) into
-# FUNC:TYPE PARAM, ...:PARAM, ...
-    H
-    x
-    s/\n/:/g
-    p
-}
-' $texi | while read event formal actual
-do
-  case $lang in
-      h) cat <<EOF >>${otmp}
-
-/* ${event} notifications.  */
-
-typedef void (observer_${event}_ftype) (${formal});
-
-extern struct observer *observer_attach_${event} (observer_${event}_ftype *f);
-extern void observer_detach_${event} (struct observer *observer);
-extern void observer_notify_${event} (${formal});
-EOF
-	;;
-
-      inc)
-      	cat <<EOF >>${otmp}
-
-/* ${event} notifications.  */
-
-static struct observer_list *${event}_subject = NULL;
-
-EOF
-	if test "$formal" != "void"; then
-	    cat<<EOF >>${otmp}
-struct ${event}_args { `echo "${formal}" | sed -e 's/,/;/g'`; };
-
-EOF
-	fi
-	cat <<EOF >>${otmp}
-static void
-observer_${event}_notification_stub (const void *data, const void *args_data)
-{
-  observer_${event}_ftype *notify = (observer_${event}_ftype *) data;
-EOF
-
-	notify_args=`echo ${actual} | sed -e 's/\([a-z0-9_][a-z0-9_]*\)/args->\1/g'`
-
-	if test ! -z "${notify_args}"; then
-	    cat<<EOF >>${otmp}
-  const struct ${event}_args *args = (const struct ${event}_args *) args_data;
-EOF
-	fi
-	cat <<EOF >>${otmp}
-  notify (${notify_args});
-}
-
-struct observer *
-observer_attach_${event} (observer_${event}_ftype *f)
-{
-  return generic_observer_attach (&${event}_subject,
-				  &observer_${event}_notification_stub,
-				  (void *) f);
-}
-
-void
-observer_detach_${event} (struct observer *observer)
-{
-  generic_observer_detach (&${event}_subject, observer);
-}
-
-void
-observer_notify_${event} (${formal})
-{
-EOF
-	if test "$formal" != "void"; then
-	    cat<<EOF >>${otmp}
-  struct ${event}_args args;
-  `echo ${actual} | sed -e 's/\([a-z0-9_][a-z0-9_]*\)/args.\1 = \1/g'`;
-
-EOF
-	else
-	    echo "char *args = NULL;" >> ${otmp}
-	fi
-	cat<<EOF >>${otmp}
-  if (observer_debug)
-    fprintf_unfiltered (gdb_stdlog, "observer_notify_${event}() called\n");
-  generic_observer_notify (${event}_subject, &args);
-}
-EOF
-	;;
-    esac
-done
-
-
-case $lang in
-    h) cat <<EOF >>${otmp}
-
-#endif /* OBSERVER_H */
-EOF
-esac
-
-
-echo Moving ${otmp} to ${o}
-mv ${otmp} ${o}