| #!/bin/sh |
| # Minimal Object-Oriented style PreProcessor. |
| |
| # Copyright (C) 2006-2008, 2016, 2018-2019 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 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 <https://www.gnu.org/licenses/>. |
| |
| # 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_usage |
| # outputs to stdout the --help usage message. |
| func_usage () |
| { |
| echo "\ |
| Usage: moopp [OPTION]... SOURCE.oo.c SOURCE.oo.h SUPERCLASS.oo.h ... |
| |
| Preprocesses SOURCE.oo.c into CLASS.c and SOURCE.oo.h into CLASS.h, |
| where CLASS is the name of the class defined in these files. |
| |
| See moo.h for the object-oriented features and the syntax of the input files. |
| |
| Options: |
| --help print this help and exit |
| --version print version information and exit |
| --dllexport=NAME Arrange so that the specified class name can be accessed |
| from outside the shared library it is compiled into. |
| This option can be repeated. |
| |
| Report bugs to <bruno@clisp.org>." |
| } |
| |
| # func_version |
| # outputs to stdout the --version message. |
| func_version () |
| { |
| echo "$progname (GNU $package) $version" |
| echo "Copyright (C) 2006-2019 Free Software Foundation, Inc. |
| License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html> |
| This is free software: you are free to change and redistribute it. |
| There is NO WARRANTY, to the extent permitted by law." |
| echo "Written by" "Bruno Haible" |
| } |
| |
| # 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 |
| } |
| |
| # Command-line option processing. |
| # Removes the OPTIONS from the arguments. Sets the variables: |
| # - dllexports list of class names to export from Woe32 DLLs |
| dllexports= |
| while test $# -gt 0; do |
| case "$1" in |
| --dllexport | --dllexpor | --dllexpo | --dllexp | --dllex | --dlle ) |
| shift |
| if test $# = 0; then |
| func_fatal_error "missing argument for --dllexport" |
| fi |
| case "$1" in |
| -*) func_fatal_error "missing argument for --dllexport" ;; |
| esac |
| dllexports="$dllexports $1" |
| shift ;; |
| --dllexport=* ) |
| arg=`echo "X$1" | sed -e 's/^X--dllexport=//'` |
| dllexports="$dllexports $arg" |
| shift ;; |
| --help | --hel | --he | --h ) |
| func_usage |
| exit 0 ;; |
| --version | --versio | --versi | --vers | --ver | --ve | --v ) |
| func_version |
| exit 0 ;; |
| -- ) # Stop option prcessing |
| shift; break ;; |
| -* ) |
| func_fatal_error "unrecognized option: $option" |
| ;; |
| * ) |
| break ;; |
| esac |
| done |
| |
| if test $# -lt 2; then |
| func_fatal_error "Usage: $0 [OPTION]... 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 |
| }" |
| # The same thing as an extended regular expression, for use with |
| # sed --regexp-extended. |
| sed_remove_comments_ERE=" |
| /[/][/*]/{ |
| ta |
| :a |
| s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)//.*,\\1, |
| te |
| s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*]([^*]|[*][^/*])*[*][*]*/,\\1 , |
| ta |
| /^([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*[/][*]/{ |
| s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*].*,\\1 , |
| tu |
| :u |
| n |
| s,^([^*]|[*][^/*])*[*][*]*/,, |
| tv |
| s,^.*\$,, |
| bu |
| :v |
| } |
| :e |
| }" |
| |
| # Check that 'sed' supports the kind of regular expressions used in |
| # sed_remove_comments. The use of \| meaning alternation of basic regular |
| # expressions is a GNU extension. |
| sed_test='s,^\(\(a\|X\)*\)//.*,\1,' |
| sed_result=`echo 'aaa//bcd' | sed -e "$sed_test"` |
| test "$sed_result" = 'aaa' \ |
| || func_fatal_error "The 'sed' program is not GNU sed. Try installing GNU sed." |
| |
| # 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 '\015\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 '\015\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_emit_priv_h newfile |
| # outputs to $newfile the contents of class.priv.h. |
| func_emit_priv_h () |
| { |
| newfile="$1" |
| { |
| 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 "};" |
| } > "$newfile" |
| } |
| |
| # func_emit_vt_h newfile |
| # outputs to $newfile the contents of class.vt.h. |
| func_emit_vt_h () |
| { |
| newfile="$1" |
| { |
| 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,$,;,' |
| } > "$newfile" |
| } |
| |
| # 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,' |
| |
| # func_emit_source_h newfile newfile_base |
| # outputs to $newfile the contents of source.h. |
| source_header_file_base=`echo "$source_header_file" | sed -e 's,^.*/,,'` |
| func_emit_source_h () |
| { |
| newfile="$1" |
| newfile_base="$2" |
| # Use DLL_VARIABLE if and only if the main classname is among the names |
| # specified with --dllexport options. |
| dllexport_for_variables= |
| for name in $dllexports; do |
| if test "${main_classname}" = "${name}"; then |
| dllexport_for_variables=" DLL_VARIABLE" |
| break |
| fi |
| done |
| { |
| 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_base\"" |
| 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 "#if IS_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; }" |
| # The 'operator ==' and 'operator !=' are needed to avoid ambiguous comparisons with NULL. |
| echo " bool operator == (const void *p) { return _pointer == p; }" |
| echo " bool operator != (const void *p) { return _pointer != p; }" |
| 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; }" |
| # The 'explicit' constructors allow to downcast. |
| echo " explicit ${main_classname}_t (${classname}_t x) : _pointer ((struct ${main_repclassalias} *) (void *) x) {}" |
| 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 "#ifdef __cplusplus" |
| echo "extern \"C\" {" |
| echo "#endif" |
| 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 "#ifdef __cplusplus" |
| echo "}" |
| echo "#endif" |
| 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${dllexport_for_variables} 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}" |
| if test -n "${main_superclassname}"; then |
| echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + ${main_superclassname}_SUPERCLASSES_LENGTH)" |
| else |
| echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + 1)" |
| fi |
| echo |
| echo "extern${dllexport_for_variables} 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_emit_source_c newfile newfile_base |
| # outputs to $newfile the contents of source.c. |
| source_impl_file_base=`echo "$source_impl_file" | sed -e 's,^.*/,,'` |
| func_emit_source_c () |
| { |
| newfile="$1" |
| newfile_base="$2" |
| { |
| echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' |
| echo |
| # 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 "#if !IS_CPLUSPLUS" |
| echo "#define ${main_classname}_representation any_${rootclassname}_representation" |
| echo "#endif" |
| echo "#line 1 \"${source_impl_file_base}\"" |
| cat "$source_impl_file" | sed -e "${impl_decl_lineno}"',$d' |
| echo "#line "`expr 6 + ${impl_decl_lineno} + 1`" \"$newfile_base\"" |
| 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_base\"" |
| # 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 |
| # First a prototype, to avoid gcc -Wmissing-prototypes warnings. |
| echo "$rettype ${main_classname}__${name} ($arglist);" |
| 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" |
| } |
| |
| # Generate the files in the source directory, not in the current directory. |
| # This is needed because they need to be distributed, since not all platforms |
| # have GNU 'sed' preinstalled. |
| |
| sed_butbase='s,[^/]*$,,' |
| destdir=`echo "$source_impl_file" | sed -e "$sed_butbase"` |
| |
| # Generate the source.h file first. The Makefile.am snippets rely on the |
| # fact that the other generated files have the same or a newer timestamp. |
| # |
| # Also, generate the source.c file last. The Makefile.am snippets don't know |
| # about the other generated files; they assume that when the source.c file |
| # is finished, this command is complete. |
| |
| new_source_header_file_base=`echo "$source_header_file_base" | sed -e 's,\.oo\.h$,.h,'` |
| new_source_header_file="${destdir}$new_source_header_file_base" |
| func_start_creation "$new_source_header_file" |
| func_emit_source_h "$new_source_header_file" "$new_source_header_file_base" \ |
| || func_fatal_error "failed" |
| |
| new_priv_header_file="${destdir}${main_classname}.priv.h" |
| func_start_creation "$new_priv_header_file" |
| func_emit_priv_h "$new_priv_header_file" \ |
| || func_fatal_error "failed" |
| |
| new_vt_header_file="${destdir}${main_classname}.vt.h" |
| func_start_creation "$new_vt_header_file" |
| func_emit_vt_h "$new_vt_header_file" \ |
| || func_fatal_error "failed" |
| |
| new_source_impl_file_base=`echo "$source_impl_file_base" | sed -e 's,\.oo\.c$,.c,'` |
| new_source_impl_file="${destdir}$new_source_impl_file_base" |
| func_start_creation "$new_source_impl_file" |
| func_emit_source_c "$new_source_impl_file" "$new_source_impl_file_base" \ |
| || func_fatal_error "failed" |