From: Bruno Haible Date: Tue, 7 Nov 2006 14:44:28 +0000 (+0000) Subject: Minimal object-oriented programming style. X-Git-Tag: v0.17~672 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a041771aff5dbe2fbb93504405e05da9f0d85465;p=thirdparty%2Fgettext.git Minimal object-oriented programming style. --- diff --git a/gnulib-local/build-aux/moopp b/gnulib-local/build-aux/moopp new file mode 100755 index 000000000..4093c57f9 --- /dev/null +++ b/gnulib-local/build-aux/moopp @@ -0,0 +1,583 @@ +#!/bin/sh +# Minimal Object-Oriented style PreProcessor. + +# Copyright (C) 2006 Free Software Foundation, Inc. +# Written by Bruno Haible , 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" diff --git a/gnulib-local/lib/moo.h b/gnulib-local/lib/moo.h new file mode 100644 index 000000000..e5f3736ec --- /dev/null +++ b/gnulib-local/lib/moo.h @@ -0,0 +1,257 @@ +/* Minimal object-oriented facilities for C. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 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 + +/* 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 */ diff --git a/gnulib-local/modules/moo b/gnulib-local/modules/moo new file mode 100644 index 000000000..5e2f197d7 --- /dev/null +++ b/gnulib-local/modules/moo @@ -0,0 +1,23 @@ +Description: +Minimal object-oriented programming style. + +Files: +build-aux/moopp +lib/moo.h + +Depends-on: + +configure.ac: +AC_REQUIRE([AC_C_INLINE]) + +Makefile.am: + +Include: +"moo.h" + +License: +LGPL + +Maintainer: +Bruno Haible +