--- /dev/null
+#!/bin/sh
+# Minimal Object-Oriented style PreProcessor.
+
+# Copyright (C) 2006 Free Software Foundation, Inc.
+# Written by Bruno Haible <bruno@clisp.org>, 2006.
+#
+# 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 2, 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, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Usage: moopp source.oo.c source.oo.h superclass.oo.h ...
+# Arguments:
+# - the source file of the class,
+# - the header file declaring the class,
+# - the header file declaring its superclass,
+# - etc. up to the root class.
+# Creates four files in the current directory:
+# - source.c, the preprocessing result of source.oo.c,
+# - source.h, the preprocessing result of source.oo.h,
+# - class.priv.h, a file declaring the private fields of the class,
+# - class.vt.h, a file declaring the virtual function table of the class.
+
+# This implementation of the preprocessor is a quick hack. It makes assumptions
+# about the source code:
+# - GNU indentation style,
+# - the struct declaration must be in a single line,
+# - no comments on the struct declaration line,
+# - no #ifs in relevant position,
+# - ...
+# Someday this should be rewritten to use a proper tokenizer and parser.
+
+# func_fatal_error message
+# outputs to stderr a fatal error message, and terminates the program.
+func_fatal_error ()
+{
+ echo "moopp: *** $1" 1>&2
+ echo "moopp: *** Stop." 1>&2
+ exit 1
+}
+
+if test $# -lt 2; then
+ func_fatal_error "Usage: $0 source.oo.c source.oo.h superclass.oo.h ..."
+fi
+
+# Check that all files exist.
+for file
+do
+ test -r "$file" || {
+ func_fatal_error "file $file does not exist"
+ }
+done
+
+source_impl_file="$1"
+source_header_file="$2"
+shift
+shift
+
+case "$source_impl_file" in
+ *.oo.c) ;;
+ *) func_fatal_error "invalid class source file name: $source_impl_file" ;;
+esac
+case "$source_header_file" in
+ *.oo.h) ;;
+ *) func_fatal_error "invalid class header file name: $source_header_file" ;;
+esac
+
+# A sed expression that removes empty lines.
+sed_remove_empty_lines='/^$/d'
+
+# A sed expression that removes ANSI C and ISO C99 comments.
+sed_remove_comments='
+/[/][/*]/{
+ ta
+ :a
+ s,^\(\([^"/]\|"\([^\"]\|[\].\)*"\|[/][^"/*]\|[/]"\([^\"]\|[\].\)*"\)*\)//.*,\1,
+ te
+ s,^\(\([^"/]\|"\([^\"]\|[\].\)*"\|[/][^"/*]\|[/]"\([^\"]\|[\].\)*"\)*\)/[*]\([^*]\|[*][^/*]\)*[*][*]*/,\1 ,
+ ta
+ /^\([^"/]\|"\([^\"]\|[\].\)*"\|[/][^"/*]\|[/]"\([^\"]\|[\].\)*"\)*[/][*]/{
+ s,^\(\([^"/]\|"\([^\"]\|[\].\)*"\|[/][^"/*]\|[/]"\([^\"]\|[\].\)*"\)*\)/[*].*,\1 ,
+ tu
+ :u
+ n
+ s,^\([^*]\|[*][^/*]\)*[*][*]*/,,
+ tv
+ s,^.*$,,
+ bu
+ :v
+ }
+ :e
+}'
+
+# func_check_impl_syntax file
+# Check the syntax of the source implementation file.
+# Output:
+# - classname name of the class being defined (without 'struct')
+# - superclassname name of the superclass, or empty for a root class
+# - impl_decl_lineno line number of the class name declaration ('struct')
+# - impl_beg_lineno line number of the start of the class declaration ('{')
+# - impl_end_lineno line number of the end of the class declaration ('}')
+# - fields field declarations, including preprocessor directives
+func_check_impl_syntax ()
+{
+ file="$1"
+ sed -e "$sed_remove_comments" < "$file" | grep '^fields:' > /dev/null || {
+ func_fatal_error "$file does not contain 'fields:'"
+ }
+ test `sed -e "$sed_remove_comments" < "$file" | grep -c '^fields:'` = 1 || {
+ func_fatal_error "$file contains more than one 'fields:'"
+ }
+ fields_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^fields:' | sed -e 's,:.*,,'`
+ sed_before_fields="$fields_lineno"',$d'
+ impl_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^struct[ ]' | tail -n 1 | sed -e 's,:.*,,'`
+ test -n "$impl_decl_lineno" || {
+ func_fatal_error "$file: class declaration not found"
+ }
+ class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$impl_decl_lineno"'p'`
+ sed_extract_classname='s,^struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
+ classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
+ test -n "$classname" || {
+ func_fatal_error "$0: $file: class name not recognized at line $impl_decl_lineno"
+ }
+ superclassname=
+ if echo "$class_line" | grep ':' > /dev/null; then
+ sed_extract_superclassname='s,^.*:[ ]*struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
+ superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
+ test -n "$superclassname" || {
+ func_fatal_error "$file: superclass name not recognized at line $impl_decl_lineno"
+ }
+ fi
+ impl_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
+ { test -n "$impl_beg_lineno" && test "$impl_decl_lineno" -lt "$impl_beg_lineno"; } || {
+ func_fatal_error "$file: opening brace of class declaration not found after line $impl_decl_lineno"
+ }
+ sed_after_fields='1,'"$fields_lineno"'d'
+ impl_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_fields" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
+ test -n "$impl_end_lineno" || {
+ func_fatal_error "$file: closing brace of class declaration not found after line $fields_lineno"
+ }
+ impl_end_lineno=`expr $fields_lineno + $impl_end_lineno`
+ sed_extract_fields="$impl_end_lineno"',$d;1,'"$fields_lineno"'d'
+ fields=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_fields"`
+}
+
+# func_check_header_syntax file
+# Check the syntax of a header file.
+# Output:
+# - classname name of the class being defined (without 'struct')
+# - superclassname name of the superclass, or empty for a root class
+# - class_decl_lineno line number of the class name declaration ('struct')
+# - class_beg_lineno line number of the start of the class declaration ('{')
+# - class_end_lineno line number of the end of the class declaration ('}')
+# - methods newline-separated list of method declarations
+func_check_header_syntax ()
+{
+ file="$1"
+ sed -e "$sed_remove_comments" < "$file" | grep '^methods:' > /dev/null || {
+ func_fatal_error "$file does not contain 'methods:'"
+ }
+ test `sed -e "$sed_remove_comments" < "$file" | grep -c '^methods:'` = 1 || {
+ func_fatal_error "$file contains more than one 'methods:'"
+ }
+ methods_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^methods:' | sed -e 's,:.*,,'`
+ sed_before_methods="$methods_lineno"',$d'
+ class_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^struct[ ]' | tail -n 1 | sed -e 's,:.*,,'`
+ test -n "$class_decl_lineno" || {
+ func_fatal_error "$file: class declaration not found"
+ }
+ class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$class_decl_lineno"'p'`
+ sed_extract_classname='s,^struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
+ classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
+ test -n "$classname" || {
+ func_fatal_error "$0: $file: class name not recognized at line $class_decl_lineno"
+ }
+ superclassname=
+ if echo "$class_line" | grep ':' > /dev/null; then
+ sed_extract_superclassname='s,^.*:[ ]*struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
+ superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
+ test -n "$superclassname" || {
+ func_fatal_error "$file: superclass name not recognized at line $class_decl_lineno"
+ }
+ fi
+ class_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
+ { test -n "$class_beg_lineno" && test "$class_decl_lineno" -lt "$class_beg_lineno"; } || {
+ func_fatal_error "$file: opening brace of class declaration not found after line $class_decl_lineno"
+ }
+ sed_after_methods='1,'"$methods_lineno"'d'
+ class_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_methods" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
+ test -n "$class_end_lineno" || {
+ func_fatal_error "$file: closing brace of class declaration not found after line $methods_lineno"
+ }
+ class_end_lineno=`expr $methods_lineno + $class_end_lineno`
+ sed_extract_methods="$class_end_lineno"',$d;1,'"$methods_lineno"'d'
+ methods=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_methods" | tr '\n' ' ' | tr ';' '\n' | sed -e 's,[ ]*$,,'`
+ sed_remove_valid_arg1_lines='/([ ]*'"$classname"'_t[ ]*[A-Za-z_0-9]*[ ]*[,)]/d'
+ sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*(.*$,\1,'
+ methods_with_bad_arg1=`echo "$methods" | sed -e "$sed_remove_empty_lines" -e "$sed_remove_valid_arg1_lines" -e "$sed_extract_method_name"`
+ if test -n "$methods_with_bad_arg1"; then
+ methods_with_bad_arg1=`{ echo "$methods_with_bad_arg1" | sed -e 's/$/, /' | tr -d '\n'; echo; } | sed -e 's/\(, \)*$//'`
+ func_fatal_error "$file: some methods don't have a first argument of type ${classname}_t: $methods_with_bad_arg1"
+ fi
+}
+
+func_check_impl_syntax "$source_impl_file"
+impl_classname="$classname"
+impl_superclassname="$superclassname"
+
+func_check_header_syntax "$source_header_file"
+main_classname="$classname"
+main_superclassname="$superclassname"
+main_class_decl_lineno="$class_decl_lineno"
+main_class_beg_lineno="$class_beg_lineno"
+main_class_end_lineno="$class_end_lineno"
+main_methods="$methods"
+all_superclasses=
+all_methods="$methods"
+inherited_methods=
+last_header_file="$source_header_file"
+expected_superclassname="$superclassname"
+
+for file
+do
+ if test -z "$expected_superclassname"; then
+ func_fatal_error "file $last_header_file does not specify a superclass; superfluous command line argument $file"
+ fi
+ func_check_header_syntax "$file"
+ all_superclasses="$classname $all_superclasses"
+ all_methods="$methods
+$all_methods"
+ inherited_methods="$methods
+$inherited_methods"
+ if test "$classname" != "$expected_superclassname"; then
+ func_fatal_error "file $last_header_file specifies superclass '$expected_superclassname', but file $file defines class '$classname'"
+ fi
+ last_header_file="$file"
+ expected_superclassname="$superclassname"
+done
+
+if test -n "$expected_superclassname"; then
+ func_fatal_error "$0: file $last_header_file specifies superclass '$expected_superclassname', please specify the header file that defines it as additional command line argument"
+fi
+
+if test "$impl_classname" != "$main_classname"; then
+ func_fatal_error "file $source_header_file specifies class '$main_classname', but file $source_impl_file specifies class '$impl_classname'"
+fi
+if test "$impl_superclassname" != "$main_superclassname"; then
+ if test -z "$main_superclassname"; then
+ func_fatal_error "file $source_header_file specifies no superclass, but file $source_impl_file specifies a superclass '$impl_superclassname'"
+ fi
+ if test -z "$impl_superclassname"; then
+ func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies no superclass"
+ fi
+ func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies a superclass '$impl_superclassname'"
+fi
+
+# func_start_creation file
+# starts the creation of the named file.
+func_start_creation ()
+{
+ file="$1"
+ if test -f "$file"; then
+ echo "Updating $file (backup in ${file}~)"
+ mv -f "$file" "${file}~" || func_fatal_error "failed"
+ else
+ echo "Creating $file"
+ fi
+}
+
+func_start_creation "${main_classname}.priv.h"
+{
+ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
+ echo
+ if test -n "${main_superclassname}"; then
+ echo "/* Field layout of superclass. */"
+ echo "#include \"${main_superclassname}.priv.h\""
+ echo
+ fi
+ echo "/* Field layout of ${main_classname} class. */"
+ echo "struct ${main_classname}_representation"
+ echo "{"
+ if test -n "${main_superclassname}"; then
+ echo " struct ${main_superclassname}_representation base;"
+ else
+ echo " const void *vtable;"
+ fi
+ echo "$fields" | sed -e "$sed_remove_empty_lines"
+ echo "};"
+} > "${main_classname}.priv.h" || func_fatal_error "failed"
+
+func_start_creation "${main_classname}.vt.h"
+{
+ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
+ echo
+ if test -n "${main_superclassname}"; then
+ echo "/* Virtual function table layout of superclass. */"
+ echo "#include \"${main_superclassname}.vt.h\""
+ echo
+ fi
+ echo "/* Virtual function table layout of ${main_classname} class. */"
+ echo "$main_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*([^,)]*/\1(*\2) (THIS_ARG/' -e 's,$,;,'
+} > "${main_classname}.vt.h" || func_fatal_error "failed"
+
+# In C++ mode, we have a precise type checking. But in C mode, we have only
+# loose type checking: So that rootclass_t and subclass_t are assignment
+# compatible, we have to define subclass_t as identical to rootclass_t.
+# Therefore we need an alias name for the representation of any type in the
+# hierarchy.
+if test -z "$main_superclassname"; then
+ main_repclassalias="any_${main_classname}_representation"
+else
+ main_repclassalias="${main_classname}_representation"
+fi
+
+sed_extract_method_rettype='s,^\(.*[^A-Za-z_0-9]\)[A-Za-z_0-9][A-Za-z_0-9]*[ ]*(.*$,\1,
+s,^[ ]*,,
+s,[ ]*$,,'
+sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*(.*$,\1,'
+sed_extract_method_arglist='s,^.*[^A-Za-z_0-9][A-Za-z_0-9][A-Za-z_0-9]*[ ]*([^,)]*\(.*\)).*$,'"${main_classname}_t"' first_arg\1,'
+sed_extract_method_args='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)$,\1,'
+
+source_header_file_base=`echo "$source_header_file" | sed -e 's,^.*/,,'`
+newfile=`echo "$source_header_file_base" | sed -e 's,\.oo\.h$,.h,'`
+func_start_creation "$newfile"
+{
+ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
+ echo
+ echo "#line 1 \"${source_header_file_base}\""
+ cat "$source_header_file" | sed -e "${main_class_decl_lineno}"',$d'
+ echo "#line "`expr 3 + ${main_class_decl_lineno} + 1`" \"$newfile\""
+ echo "struct ${main_repclassalias};"
+ echo "/* ${main_classname}_t is defined as a pointer to struct ${main_repclassalias}."
+ echo " In C++ mode, we use a smart pointer class."
+ echo " In C mode, we have no other choice than a typedef to the root class type. */"
+ echo "#ifdef __cplusplus"
+ echo "struct ${main_classname}_t"
+ echo "{"
+ echo "private:"
+ echo " struct ${main_repclassalias} *_pointer;"
+ echo "public:"
+ echo " ${main_classname}_t () : _pointer (NULL) {}"
+ echo " ${main_classname}_t (struct ${main_repclassalias} *pointer) : _pointer (pointer) {}"
+ echo " struct ${main_repclassalias} * operator -> () { return _pointer; }"
+ echo " operator struct ${main_repclassalias} * () { return _pointer; }"
+ atroot=yes
+ for classname in $all_superclasses; do
+ if test -n "$atroot"; then
+ repclassalias="any_${classname}_representation"
+ else
+ repclassalias="${classname}_representation"
+ fi
+ echo " operator struct ${repclassalias} * () { return (struct ${repclassalias} *) _pointer; }"
+ atroot=
+ done
+ # The 'operator void *' is needed to avoid ambiguous conversion chains.
+ echo " operator void * () { return _pointer; }"
+ atroot=yes
+ for classname in $all_superclasses; do
+ if test -n "$atroot"; then
+ repclassalias="any_${classname}_representation"
+ else
+ repclassalias="${classname}_representation"
+ fi
+ echo " operator ${classname}_t () { return (${classname}_t) ($struct ${repclassalias} *) _pointer; }"
+ atroot=
+ done
+ echo "};"
+ echo "#else"
+ if test -n "${main_superclassname}"; then
+ echo "typedef ${main_superclassname}_t ${main_classname}_t;"
+ else
+ echo "typedef struct ${main_repclassalias} * ${main_classname}_t;"
+ fi
+ echo "#endif"
+ echo
+ echo "/* Functions that invoke the methods. */"
+ echo "$all_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*([^,)]*/\1'"${main_classname}_"'\2 ('"${main_classname}_t first_arg"'/' -e 's,^,extern ,' -e 's,$,;,'
+ echo
+ # Now come the implementation details.
+ echo "/* Type representing an implementation of ${main_classname}_t. */"
+ echo "struct ${main_classname}_implementation"
+ echo "{"
+ echo " const typeinfo_t * const *superclasses;"
+ echo " size_t superclasses_length;"
+ echo " size_t instance_size;"
+ echo "#define THIS_ARG ${main_classname}_t first_arg"
+ echo "#include \"${main_classname}.vt.h\""
+ echo "#undef THIS_ARG"
+ echo "};"
+ echo
+ echo "/* Public portion of the object pointed to by a ${main_classname}_t. */"
+ echo "struct ${main_classname}_representation_header"
+ echo "{"
+ echo " const struct ${main_classname}_implementation *vtable;"
+ echo "};"
+ echo
+ echo "#if HAVE_INLINE"
+ echo
+ echo "/* Define the functions that invoke the methods as inline accesses to"
+ echo " the ${main_classname}_implementation."
+ echo " Use #define to avoid a warning because of extern vs. static. */"
+ echo
+ echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
+ while read method; do
+ rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
+ name=`echo "$method" | sed -e "$sed_extract_method_name"`
+ arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
+ if test "$arglist" = "void"; then
+ args=
+ else
+ args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
+ fi
+ if test "$rettype" = "void"; then
+ return=
+ else
+ return="return "
+ fi
+ echo "# define ${main_classname}_${name} ${main_classname}_${name}_inline"
+ echo "static inline $rettype"
+ echo "${main_classname}_${name} ($arglist)"
+ echo "{"
+ echo " const struct ${main_classname}_implementation *vtable ="
+ echo " ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
+ echo " ${return}vtable->${name} ($args);"
+ echo "}"
+ echo
+ done
+ echo "#endif"
+ echo
+ echo "extern const typeinfo_t ${main_classname}_typeinfo;"
+ if test -n "${main_superclassname}"; then
+ superclasses_array_initializer="${main_superclassname}_SUPERCLASSES"
+ else
+ superclasses_array_initializer="NULL"
+ fi
+ echo "#define ${main_classname}_SUPERCLASSES &${main_classname}_typeinfo, ${superclasses_array_initializer}"
+ echo
+ echo "extern const struct ${main_classname}_implementation ${main_classname}_vtable;"
+ echo
+ echo "#line "`expr $main_class_end_lineno + 1`" \"${source_header_file_base}\""
+ cat "$source_header_file" | sed -e "1,${main_class_end_lineno}d"
+} > "$newfile" || func_fatal_error "failed"
+
+source_impl_file_base=`echo "$source_impl_file" | sed -e 's,^.*/,,'`
+newfile=`echo "$source_impl_file_base" | sed -e 's,\.oo\.c$,.c,'`
+func_start_creation "$newfile"
+{
+ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
+ echo
+ echo "#line 1 \"${source_impl_file_base}\""
+ cat "$source_impl_file" | sed -e "${impl_decl_lineno}"',$d'
+ echo "#line "`expr 3 + ${impl_decl_lineno} + 1`" \"$newfile\""
+ # In C mode, where subclass_t is identical to rootclass_t, we define the
+ # any_rootclass_representation type to the right one for subclass.
+ if test -n "$all_superclasses"; then
+ for classname in $all_superclasses; do
+ rootclassname="$classname"
+ break
+ done
+ else
+ rootclassname="$main_classname"
+ fi
+ echo "#ifndef __cplusplus"
+ echo "#define ${main_classname}_representation any_${rootclassname}_representation"
+ echo "#endif"
+ echo "#include \"${main_classname}.priv.h\""
+ echo
+ echo "const typeinfo_t ${main_classname}_typeinfo = { \"${main_classname}\" };"
+ echo
+ echo "static const typeinfo_t * const ${main_classname}_superclasses[] ="
+ echo " { ${main_classname}_SUPERCLASSES };"
+ echo
+ if test -n "${main_superclassname}"; then
+ echo "#define super ${main_superclassname}_vtable"
+ echo
+ fi
+ echo "#line "`expr $impl_end_lineno + 1`" \"${source_impl_file_base}\""
+ cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "s,${main_classname}::,${main_classname}__,g"
+ echo
+ lineno=`wc -l < "$newfile"`
+ echo "#line "`expr $lineno + 2`" \"$newfile\""
+ # Define trivial stubs for methods that are not defined or overridden.
+ inherited_method_names=`echo "$inherited_methods" | sed -e "$sed_remove_empty_lines" | sed -e "$sed_extract_method_name"`
+ echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
+ while read method; do
+ rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
+ name=`echo "$method" | sed -e "$sed_extract_method_name"`
+ arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
+ if test "$arglist" = "void"; then
+ args=
+ else
+ args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
+ fi
+ if test "$rettype" = "void"; then
+ return=
+ else
+ return="return "
+ fi
+ if cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "$sed_remove_comments" | grep "${main_classname}::${name} *(" > /dev/null; then
+ # The class defines or overrides the method.
+ :
+ else
+ # Add a stub for the method.
+ inherited=
+ for i in $inherited_method_names; do
+ if test "$i" = "$name"; then
+ inherited=yes
+ fi
+ done
+ echo "$rettype"
+ echo "${main_classname}__${name} ($arglist)"
+ echo "{"
+ if test -n "$inherited"; then
+ echo " ${return}super.${name} ($args);"
+ else
+ echo " /* Abstract (unimplemented) method called. */"
+ echo " abort ();"
+ # Avoid C++ compiler warning about missing return value.
+ echo " #ifndef __GNUC__"
+ echo " ${return}${main_classname}__${name} ($args);"
+ echo " #endif"
+ fi
+ echo "}"
+ echo
+ fi
+ done
+ echo
+ echo "const struct ${main_classname}_implementation ${main_classname}_vtable ="
+ echo "{"
+ echo " ${main_classname}_superclasses,"
+ echo " sizeof (${main_classname}_superclasses) / sizeof (${main_classname}_superclasses[0]),"
+ echo " sizeof (struct ${main_classname}_representation),"
+ echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
+ while read method; do
+ name=`echo "$method" | sed -e "$sed_extract_method_name"`
+ echo " ${main_classname}__${name},"
+ done
+ echo "};"
+ echo
+ echo "#if !HAVE_INLINE"
+ echo
+ echo "/* Define the functions that invoke the methods. */"
+ echo
+ echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
+ while read method; do
+ rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
+ name=`echo "$method" | sed -e "$sed_extract_method_name"`
+ arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
+ if test "$arglist" = "void"; then
+ args=
+ else
+ args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
+ fi
+ if test "$rettype" = "void"; then
+ return=
+ else
+ return="return "
+ fi
+ echo "$rettype"
+ echo "${main_classname}_${name} ($arglist)"
+ echo "{"
+ echo " const struct ${main_classname}_implementation *vtable ="
+ echo " ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
+ echo " ${return}vtable->${name} ($args);"
+ echo "}"
+ echo
+ done
+ echo "#endif"
+} > "$newfile" || func_fatal_error "failed"
--- /dev/null
+/* Minimal object-oriented facilities for C.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ USA. */
+
+/* This file defines minimal facilities for object-oriented programming
+ style in ANSI C.
+
+ The facilities allow to define classes with single inheritance and
+ "virtual" methods.
+
+ Strict type checking is provided in combination with a C++ compiler:
+ The code compiles in ANSI C with less strict type checking; when
+ compiled with a C++ compiler, strict type checking is done.
+
+ In contrast to [OOC] and [OOPC], this implementation concentrates on the
+ bare essentials of an object-oriented programming style. It does not
+ provide features that are "sometimes useful", but not essential.
+
+ Features:
+ - Combination of fields and methods into a single object. YES
+ - Description of objects of same shape and same behaviour
+ by a class. YES
+ - Single inheritance. YES
+ - Multiple inheritance. NO
+ - Operator overloading (compile-time polymorphism). NO
+ - Virtual methods (run-time polymorphism). YES
+ - Information hiding: private/protected/public. private fields
+ - Static fields and methods. NO
+ - Constructors, destructors. NO
+ - 'new', 'delete'. NO
+ - Exception handling. NO
+ - Garbage collection. NO
+ - Templates / Generic classes with parameters. NO
+ - Namespaces. NO
+ - Hidden 'this' pointer in methods. NO
+ - Declaring or implementing several classes in the same file. NO
+
+ Rationale for NO:
+ - Multiple inheritance is not supported because programming languages
+ like Java and C# prove that they are not necessary. Modern design
+ patterns use delegation more often than composition; this reduces
+ the pressure to use multiple inheritance.
+ Multiple inheritance of "interfaces" (classes without fields) might
+ be considered, though.
+ - Operator overloading is not essential: The programmer can rename
+ methods so that they carry unambiguous method names. This also makes
+ the code more readable.
+ - Virtual methods are supported. Non-virtual methods are not: they
+ constitute an assumption about the possible subclasses which is more
+ often wrong than right. In other words, non-virtual methods are a
+ premature optimization - "the root of all evil", according to
+ Donald E. Knuth.
+ - Information hiding: 'protected' is not supported because it is always
+ inappropriate: it prohibits the use of the delegation design pattern.
+ 'private' is implemented on fields. There are no 'public' fields,
+ since the use of getters/setters allows for overriding in subclasses
+ and is more maintainable (ability to set breakpoints). On the other
+ hand, all methods are 'public'. 'private` methods are not supported
+ because methods with static linkage can be used instead.
+ - Static fields and methods are not supported because normal variables
+ and functions with static or extern linkage can be used instead.
+ - Constructors and destructors are not supported. The programmer can
+ define 'init' and 'do_free' methods himself.
+ - 'new', 'delete' are not supported because they only provide the
+ grouping of two lines of code into a single line of code.
+ - Exception handling is not supported because conventions with a return
+ code can be used instead.
+ - Garbage collection is not supported. Without it the programmer's life
+ is harder, but not impossible. The programmer has to think about
+ ownership of objects.
+ - Templates / Generic classes with parameters are not supported because
+ they are mostly used for container classes, and container classes can
+ be implemented in a simpler object-oriented way that requires only a
+ very limited form of class inheritance.
+ - Namespaces are not implemented, because they can be simulated by a
+ consistent naming convention.
+ - A hidden 'this' pointer in methods is not implemented. It reduces the
+ transparency of the code (because what looks like a variable access can
+ be an access through 'this') and is simply not needed.
+ - Declaring or implementing several classes in the same file is not
+ supported, because it is anyway good practice to define each class in
+ its own .oo.h / .oo.c file.
+
+ Syntax:
+
+ The syntax resembles C++, but deviates from C++ where the C++ syntax is
+ just too braindead.
+
+ A root class is declared in a .oo.h file:
+
+ struct rootfoo
+ {
+ methods:
+ int method1 (rootfoo_t x, ...); ...
+ };
+
+ and in the corresponding .oo.c file:
+
+ struct rootfoo
+ {
+ fields:
+ int field1; ...
+ };
+
+ A subclass is declared in a .oo.h file as well:
+
+ struct subclass : struct rootfoo
+ {
+ methods:
+ int method2 (subclass_t x, ...); ...
+ };
+
+ and in the corresponding .oo.c file:
+
+ struct subclass : struct rootfoo
+ {
+ fields:
+ int field2; ...
+ };
+
+ This defines:
+ - An incomplete type 'struct any_rootfoo_representation' or
+ 'struct subclass_representation', respectively. It denotes the memory
+ occupied by an object of the respective class. The prefix 'any_' is
+ present only for a root class.
+ - A type 'rootfoo_t' or 'subclass_t' that is equivalent to a pointer
+ 'struct any_rootfoo_representation *' or
+ 'struct subclass_representation *', respectively.
+ - A type 'struct rootfoo_implementation' or
+ 'struct subclass_implementation', respectively. It contains a virtual
+ function table for the corresponding type.
+ - A type 'struct rootfoo_representation_header' or
+ 'struct subclass_representation_header', respectively, that defines
+ the part of the memory representation containing the virtual function
+ table pointer.
+ - Functions 'rootfoo_method1 (rootfoo_t x, ...);' ...
+ 'subclass_method1 (subclass_t x, ...);' ...
+ 'subclass_method2 (subclass_t x, ...);' ...
+ that invoke the corresponding methods. They are realized as inline
+ functions if possible.
+ - A declaration of 'rootfoo_typeinfo' or 'subclass_typeinfo', respectively,
+ each being a typeinfo_t instance.
+ - A declaration of 'ROOTFOO_SUPERCLASSES' or 'SUBCLASS_SUPERCLASSES',
+ respectively, each being an initializer for an array of typeinfo_t.
+ - A declaration of 'rootfoo_vtable' or 'subclass_vtable', respectively,
+ being an instance of 'struct rootfoo_implementation' or
+ 'struct subclass_implementation', respectively.
+ - A header file "rootfoo.priv.h" or "subclass.priv.h" that defines the
+ private fields of 'struct rootfoo_representation' or
+ 'struct subclass_representation', respectively.
+
+ A class implementation looks like this, in a .oo.c file:
+
+ struct subclass : struct rootfoo
+ {
+ fields:
+ int field2; ...
+ };
+
+ int subclass::method1 (subclass_t x, ...) { ... } [optional]
+ int subclass::method2 (subclass_t x, ...) { ... }
+ ...
+
+ At the place of the second "struct subclass" definition, the type
+ 'struct subclass_representation' is expanded, and the macro 'super' is
+ defined, referring to the vtable of the superclass. For root classes,
+ 'super' is not defined. Also, 'subclass_typeinfo' is defined.
+
+ Each method subclass::method_i defines the implementation of a method
+ for the particular class. Its C name is subclass__method_i (not to be
+ confused with subclass_method_i, which is the externally visible function
+ that invokes this method).
+
+ Methods that are not defined implicitly inherited from the superclass.
+
+ At the end of the file, 'subclass_vtable' is defined, as well as
+ 'subclass_method1 (subclass_t x, ...);' ...
+ 'subclass_method2 (subclass_t x, ...);' ...
+ if they were not already defined as inline functions in the header file.
+
+ Object representation in memory:
+ - Objects have as their first field, called 'vtable', a pointer to a table
+ to data and function pointers that depend only on the class, not on the
+ object instance.
+ - One of the first fields of the vtable is a pointer to the
+ 'superclasses'; this is a NULL-terminated array of pointers to
+ typeinfo_t objects, starting with the class itself, then its
+ superclass etc.
+
+
+ [OOC] Axel-Tobias Schreiner: Object-oriented programming with ANSI-C. 1993.
+
+ [OOPC] Laurent Deniau: Object Oriented Programming in C. 2001.
+
+ */
+
+#ifndef _MOO_H
+#define _MOO_H
+
+/* Get size_t, abort(). */
+#include <stdlib.h>
+
+/* An object of this type is defined for each class. */
+typedef struct
+{
+ const char *classname;
+} typeinfo_t;
+
+/* IS_INSTANCE (OBJ, ROOTCLASSNAME, CLASSNAME)
+ IS_INSTANCE_PRIVATE (OBJ, ROOTCLASSNAME, CLASSNAME)
+ test whether an object is instance of a given class, given as lower case
+ class name.
+ IS_INSTANCE can be used anywhere; IS_INSTANCE_PRIVATE can only be used in
+ the file that implements CLASSNAME but is better optimized. */
+#define IS_INSTANCE(obj,rootclassname,classname) \
+ (((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses_length \
+ >= classname##_implementation.superclasses_length \
+ && ((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses \
+ [((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses_length \
+ - classname##_implementation.superclasses_length] \
+ == & classname##_typeinfo)
+#define IS_INSTANCE_PRIVATE(obj,rootclassname,classname) \
+ (((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses_length \
+ >= sizeof (classname##_superclasses) / sizeof (classname##_superclasses[0]) \
+ && ((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses \
+ [((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses_length \
+ - sizeof (classname##_superclasses) / sizeof (classname##_superclasses[0])] \
+ == & classname##_typeinfo)
+/* This instance test consists of two comparisons. One could even optimize
+ this to a single comparison, by limiting the inheritance depth to a fixed
+ limit, for example, say, depth <= 10. The superclasses list would then
+ need to be stored in reverse order, from the root down to the class itself,
+ and be filled up with NULLs so that the array has length 10. The instance
+ test would look like this:
+ #define IS_INSTANCE_PRIVATE(obj,rootclassname,classname) \
+ (((const struct rootclassname##_representation_header *)(const struct any_##rootclassname##_representation *)(obj))->vtable->superclasses \
+ [classname##_superclasses_length - 1] \
+ == & classname##_typeinfo)
+ but the classname##_superclasses_length would no longer be available as a
+ simple sizeof expression. */
+
+#endif /* _MOO_H */