# Gtkmmproc Output module # # Copyright 2001 Free Software Foundation # # 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 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # package Output; use strict; BEGIN { @Namespace::ISA=qw(main); } # $objOutputter new() sub new { my ($m4path, $macrodirs) = @_; my $self = {}; bless $self; $$self{out} = []; $$self{source} = ""; $$self{tmpdir} = "/tmp"; $$self{destdir} = ""; $$self{objDefsParser} = undef; # It will be set in set_defsparser() $$self{m4path} = $m4path; $$self{m4args} = "-I"; $$self{m4args} .= join(" -I", @$macrodirs); return $self; } sub set_defsparser($$) { my ($self, $objDefsParser) = @_; $$self{objDefsParser} = $objDefsParser; #Remember it so that we can use it in our output methods. } sub m4args_append($$) { my ($self, $str) = @_; $$self{m4args} .= $str; } sub append($$) { my ($self, $str) = @_; push(@{$$self{out}}, $str); } # void output_wrap_failed($cname, $error) # Puts a comment in the header about the error during code-generation. sub output_wrap_failed($$$) { my ($self, $cname, $error) = @_; my $str = sprintf("//gtkmmproc error: %s : %s", $cname, $error); print STDERR "Output.pm: $cname : $error\n"; $self->append($str); } sub error { my $format=shift @_; printf STDERR "Output.pm: $format",@_; } ### Convert _WRAP to a virtual # _VFUNC_H(signame,rettype,`') # _VFUNC_PH(gtkname,crettype,cargs and names) # void output_wrap_vfunc_h($filename, $line_num, $objCppfunc, $objCDefsFunc) sub output_wrap_vfunc_h($$$$$) { my ($self, $filename, $line_num, $objCppfunc, $objCDefsFunc) = @_; my $str = sprintf("_VFUNC_H(%s,%s,\`%s\')dnl\n", $$objCppfunc{name}, $$objCppfunc{rettype}, $objCppfunc->args_types_and_names() ); $self->append($str); #The default callback, which will call *_impl, which will then call the base default callback. #Declares the callback in the private *Class class and sets it in the class_init function. my $str = sprintf("_VFUNC_PH(%s,%s,\`%s\')dnl\n", $$objCDefsFunc{name}, $$objCDefsFunc{rettype}, $objCDefsFunc->args_types_and_names() ); $self->append($str); } # _VFUNC_CC(signame,gtkname,rettype,crettype,`',`') sub output_wrap_vfunc_cc($$$$$$) { my ($self, $filename, $line_num, $objCppfunc, $objDefsSignal) = @_; my $cname = $$objDefsSignal{name}; # e.g. Gtk::Button::draw_indicator: #Use a different macro for Interfaces, to generate an extra convenience method. my $refreturn = ""; $refreturn = "refreturn" if($$objCppfunc{rettype_needs_ref}); my $str = sprintf("_VFUNC_CC(%s,%s,%s,%s,\`%s\',\`%s\',%s)dnl\n", $$objCppfunc{name}, $cname, $$objCppfunc{rettype}, $$objDefsSignal{rettype}, $objCppfunc->args_types_and_names(), convert_args_cpp_to_c($objCppfunc, $objDefsSignal, 0, $line_num), #$objCppfunc->args_names_only(), $refreturn); $self->append($str); # e.g. Gtk::ButtonClass::draw_indicator(): my $refreturn_ctype = ""; $refreturn_ctype = "refreturn_ctype" if($$objDefsSignal{rettype_needs_ref}); my $str = sprintf("_VFUNC_PCC(%s,%s,%s,%s,\`%s\',\`%s\',\`%s\',%s,%s)dnl\n", $$objCppfunc{name}, $cname, $$objCppfunc{rettype}, $$objDefsSignal{rettype}, $objDefsSignal->args_types_and_names(), $objDefsSignal->args_names_only(), convert_args_c_to_cpp($objDefsSignal, $objCppfunc, $line_num), ${$objDefsSignal->get_param_names()}[0], $refreturn_ctype); $self->append($str); } ### Convert _WRAP to a virtual # _SIGNAL_H(signame,rettype,`') # _SIGNAL_PH(gtkname,crettype,cargs and names) # void output_wrap_default_signal_handler_h($filename, $line_num, $objCppfunc, $objCDefsFunc, @args) sub output_wrap_default_signal_handler_h($$$$$$) { my ($self, $filename, $line_num, $objCppfunc, $objCDefsFunc, $bImplement) = @_; my $str = sprintf("_SIGNAL_H(%s,%s,\`%s\')dnl\n", $$objCppfunc{name}, $$objCppfunc{rettype}, $objCppfunc->args_types_and_names() ); $self->append($str); #The default callback, which will call *_impl, which will then call the base default callback. #Declares the callback in the private *Class class and sets it in the class_init function. $str = sprintf("_SIGNAL_PH(%s,%s,\`%s\')dnl\n", $$objCDefsFunc{name}, $$objCDefsFunc{rettype}, $objCDefsFunc->args_types_and_names() ); $self->append($str); } # _SIGNAL_CC(signame,gtkname,rettype,crettype,`',`') sub output_wrap_default_signal_handler_cc($$$$$) { my ($self, $filename, $line_num, $objCppfunc, $objDefsSignal, $bImplement) = @_; my $cname = $$objDefsSignal{name}; # $cname = $1 if ($args[3] =~ /"(.*)"/); #TODO: What's this about? # e.g. Gtk::Button::on_clicked: if($bImplement eq 1) { my $str = sprintf("_SIGNAL_CC(%s,%s,%s,%s,\`%s\',\`%s\',%s)dnl\n", $$objCppfunc{name}, $cname, $$objCppfunc{rettype}, $$objDefsSignal{rettype}, $objCppfunc->args_types_and_names(), convert_args_cpp_to_c($objCppfunc, $objDefsSignal, 0, $line_num), #$objCppfunc->args_names_only(), $$objCppfunc{const}); $self->append($str); } # e.g. Gtk::ButtonClass::on_clicked(): #Callbacks always take the object instance as the first argument: # my $arglist_names = "object"; # my $arglist_names_extra = $objDefsSignal->args_names_only(); # if ($arglist_names_extra) # { # $arglist_names .= ", "; # $arglist_names .= $arglist_names_extra; # } my $str = sprintf("_SIGNAL_PCC(%s,%s,%s,%s,\`%s\',\`%s\',\`%s\',%s)dnl\n", $$objCppfunc{name}, $cname, $$objCppfunc{rettype}, $$objDefsSignal{rettype}, $objDefsSignal->args_types_and_names(), $objDefsSignal->args_names_only(), convert_args_c_to_cpp($objDefsSignal, $objCppfunc, $line_num), ${$objDefsSignal->get_param_names()}[0]); $self->append($str); } ### Convert _WRAP to a method # _METHOD(cppname,cname,cpprettype,crettype,arglist,cargs,const) # void output_wrap_meth($filename, $line_num, $objCppFunc, $objCDefsFunc, $cppMethodDecl, $documentation) sub output_wrap_meth($$$$$$) { my ($self, $filename, $line_num, $objCppfunc, $objCDefsFunc, $cppMethodDecl, $documentation) = @_; my $objDefsParser = $$self{objDefsParser}; # Doxygen documentation before the method declaration: $self->append("\n${documentation}"); #Declaration: $self->append(" ${cppMethodDecl};"); my $refneeded = ""; if($$objCDefsFunc{rettype_needs_ref}) { $refneeded = "refreturn" } my $errthrow = ""; if($$objCDefsFunc{throw_any_errors}) { $errthrow = "errthrow" } #Implementation: my $str; if ($$objCppfunc{static}) { $str = sprintf("_STATIC_METHOD(%s,%s,%s,%s,\`%s\',\`%s\',%s,%s)dnl\n", $$objCppfunc{name}, $$objCDefsFunc{c_name}, $$objCppfunc{rettype}, $objCDefsFunc->get_return_type_for_methods(), $objCppfunc->args_types_and_names(), convert_args_cpp_to_c($objCppfunc, $objCDefsFunc, 1, $line_num, $errthrow), #1 means it's static, so it has 'object'. $refneeded, $errthrow); } else { $str = sprintf("_METHOD(%s,%s,%s,%s,\`%s\',\`%s\',%s,%s,%s)dnl\n", $$objCppfunc{name}, $$objCDefsFunc{c_name}, $$objCppfunc{rettype}, $objCDefsFunc->get_return_type_for_methods(), $objCppfunc->args_types_and_names(), convert_args_cpp_to_c($objCppfunc, $objCDefsFunc, 0, $line_num, $errthrow), $$objCppfunc{const}, $refneeded, $errthrow); } $self->append($str); } ### Convert _WRAP_CTOR to a ctor # _METHOD(cppname,cname,cpprettype,crettype,arglist,cargs,const) # void output_wrap_ctor($filename, $line_num, $objCppFunc, $objCDefsFunc, $cppMethodDecl) sub output_wrap_ctor($$$$$) { my ($self, $filename, $line_num, $objCppfunc, $objCDefsFunc, $cppMethodDecl) = @_; my $objDefsParser = $$self{objDefsParser}; #Ctor Declaration: #TODO: Add explicit. $self->append("explicit " . $cppMethodDecl . ";"); #Implementation: my $str = sprintf("_CTOR_IMPL(%s,%s,\`%s\',\`%s\')dnl\n", $$objCppfunc{name}, $$objCDefsFunc{c_name}, $objCppfunc->args_types_and_names(), get_ctor_properties($objCppfunc, $objCDefsFunc, $line_num) ); $self->append($str); } sub output_wrap_create($$$) { my ($self, $args_type_and_name_with_default_values, $objWrapParser) = @_; #Re-use Function in a very hacky way, to separate the argument types_and_names. my $fake_decl = "void fake_func(" . $args_type_and_name_with_default_values . ")"; my $objFunction = &Function::new($fake_decl, $objWrapParser); my $args_names_only = $objFunction->args_names_only(); my $args_type_and_name = $objFunction->args_types_and_names(); my $str = sprintf("_CREATE_METHOD(\`%s\',\`%s\')dnl\n", $args_type_and_name, $args_names_only); $self->append($str) } # _SIGNAL_IMPL(return_type,func_name,`',cname) # sub output_wrap_sig_impl($$$$) # { # my ($self, $filename, $line_num, $objCppfunc) = @_; # # my $str; # $str = sprintf("_SIGNAL_IMPL(%s,%s,\`%s\')dnl\n", # $$objCppfunc{rettype}, # $$objCppfunc{name}, # $objCppfunc->args_types_only() # ); # # $self->append($str); # } # void output_wrap_sig_decl($filename, $line_num, $objCSignal, $objCppfunc, $signal_name) # custom_signalproxy_name is "" when no type conversion is required - a normal templates SignalProxy will be used instead. sub output_wrap_sig_decl($$$$$$) { my ($self, $filename, $line_num, $objCSignal, $objCppfunc, $signal_name) = @_; # _SIGNAL_PROXY(c_signal_name, c_return_type, `', # cpp_signal_name, cpp_return_type, `',`', # refdoc_comment) my $str = sprintf("_SIGNAL_PROXY(%s,%s,\`%s\',%s,%s,\`%s\',\`%s\',\`%s\')dnl\n", $signal_name, $$objCSignal{rettype}, $objCSignal->args_types_and_names_without_object(), $$objCppfunc{name}, $$objCppfunc{rettype}, $objCppfunc->args_types_only(), convert_args_c_to_cpp($objCSignal, $objCppfunc, $line_num), $objCppfunc->get_refdoc_comment() ); $self->append($str); } # void output_wrap_enum($filename, $line_num, $cpp_type, $c_type, $comment, @flags) sub output_wrap_enum($$$$$$$) { my ($self, $filename, $line_num, $cpp_type, $c_type, $comment, @flags) = @_; my $objEnum = GtkDefs::lookup_enum($c_type); if(!$objEnum) { $self->output_wrap_failed($c_type, "enum defs lookup failed."); return; } $objEnum->beautify_values(); my $no_gtype = ""; my $elements = $objEnum->build_element_list(\@flags, \$no_gtype, " "); if(!$elements) { $self->output_wrap_failed($c_type, "unknown _WRAP_ENUM() flag"); return; } my $value_suffix = "Enum"; $value_suffix = "Flags" if($$objEnum{flags}); my $str = sprintf("_ENUM(%s,%s,%s,\`%s\',\`%s\',\`%s\')dnl\n", $cpp_type, $c_type, $value_suffix, $elements, $no_gtype, $comment ); $self->append($str); } # void output_wrap_gerror($filename, $line_num, $cpp_type, $c_enum, $domain, @flags) sub output_wrap_gerror($$$$$$$) { my ($self, $filename, $line_num, $cpp_type, $c_enum, $domain, @flags) = @_; my $objDefsParser = $$self{objDefsParser}; my $objEnum = GtkDefs::lookup_enum($c_enum); if(!$objEnum) { $self->output_wrap_failed($c_enum, "enum defs lookup failed."); return; } # Shouldn't happen, and if it does, I'd like to know that. warn if($$objEnum{flags}); $objEnum->beautify_values(); # cut off the module prefix, e.g. GDK_ my $prefix = $domain; $prefix =~ s/^[^_]+_//; # Chop off the domain prefix, because we put the enum into the class. unshift(@flags, "s#^${prefix}_##"); my $no_gtype = ""; my $elements = $objEnum->build_element_list(\@flags, \$no_gtype, " "); my $str = sprintf("_GERROR(%s,%s,%s,\`%s\',%s)dnl\n", $cpp_type, $c_enum, $domain, $elements, $no_gtype ); $self->append($str); } # _PROPERTY_PROXY(name, cpp_type) # void output_wrap_property($filename, $line_num, $name, $cpp_type) sub output_wrap_property($$$$$$) { my ($self, $filename, $line_num, $name, $cpp_type, $c_class) = @_; my $objDefsParser = $$self{objDefsParser}; my $objProperty = GtkDefs::lookup_property($c_class, $name); if($objProperty eq 0) #If the lookup failed: { $self->output_wrap_failed($name, "property defs lookup failed."); } elsif($objProperty->get_construct_only() eq 1) { $self->output_wrap_failed($name, "Attempt to wrap a construct-only property."); } else { # We use a suffix to specify a particular Glib::PropertyProxy* class. my $proxy_suffix = ""; # Read/Write: if($objProperty->get_readable() ne 1) { $proxy_suffix = "_WriteOnly"; } elsif($objProperty->get_writable() ne 1) { $proxy_suffix = "_ReadOnly"; } $name =~ s/-/_/g; my $str = sprintf("_PROPERTY_PROXY(%s,%s,%s)dnl\n", $name, $cpp_type, $proxy_suffix ); $self->append($str); } } # vpod output_temp_g1($filename, $section) e.g. output_temp_g1(button, gtk) sub output_temp_g1($$) { my ($self, $section) = @_; # Write out *.g1 temporary file open(FILE, '>', "$$self{tmpdir}/gtkmmproc_$$.g1"); # $$ is the Process ID print FILE "include(base.m4)dnl\n"; my $module = $section; my $module_canonical = Util::string_canonical($module); #In case there is a / character in the module. print FILE "_START($$self{source},$module,$module_canonical)dnl\n"; print FILE join("", @{$$self{out}}); print FILE "_END()\n"; close(FILE); } sub make_g2_from_g1($) { my ($self) = @_; # Execute m4 to get *.g2 file: system("$$self{m4path} $$self{m4args} '$$self{tmpdir}/gtkmmproc_$$.g1' > '$$self{tmpdir}/gtkmmproc_$$.g2'"); return ($? >> 8); } # void write_sections_to_files() # This is where we snip the /tmp/gtkmmproc*.g2 file into sections (,h, .cc, _private.h) sub write_sections_to_files() { my ($self) = @_; my $fname_h = "$$self{destdir}/$$self{source}.h"; my $fname_ph = "$$self{destdir}/private/$$self{source}_p.h"; my $fname_cc = "$$self{destdir}/$$self{source}.cc"; open(INPUT, '<', "$$self{tmpdir}/gtkmmproc_$$.g2"); # $$ is the process ID. # open tempory file for each section open(OUTPUT_H, '>', "$fname_h.tmp"); open(OUTPUT_PH, '>', "$fname_ph.tmp"); open(OUTPUT_CC, '>', "$fname_cc.tmp"); my $oldfh = select(OUTPUT_H); my $blank = 0; while() { # section switching if(/^#S 0/) { select(OUTPUT_H); next; } if(/^#S 1/) { select(OUTPUT_PH); next; } if(/^#S 2/) { select(OUTPUT_CC); next; } # get rid of bogus blank lines if(/^\s*$/) { ++$blank; } else { $blank = 0; } next if($blank > 2); print $_; } select($oldfh); close(INPUT); close(OUTPUT_H); close(OUTPUT_PH); close(OUTPUT_CC); foreach($fname_h, $fname_ph, $fname_cc) { # overwrite the source file only if it has actually changed system("cmp -s '$_.tmp' '$_' || cp '$_.tmp' '$_' ; rm -f '$_.tmp'"); } } sub remove_temp_files($) { my ($self) = @_; system("rm -f \"$$self{tmpdir}/gtkmmproc_$$.g1\""); system("rm -f \"$$self{tmpdir}/gtkmmproc_$$.g2\""); } # procedure for generating CONVERT macros # $string convert_args_cpp_to_c($objCppfunc, $objCDefsFunc, $static, $wrap_line_number,$automatic_error) sub convert_args_cpp_to_c($$$$;$) { my ($objCppfunc, $objCDefsFunc, $static, $wrap_line_number, $automatic_error) = @_; $automatic_error = "" unless defined $automatic_error; my $cpp_param_names = $$objCppfunc{param_names}; my $cpp_param_types = $$objCppfunc{param_types}; my $c_param_types = $$objCDefsFunc{param_types}; my @result; my $num_c_args_expected = scalar(@{$c_param_types}); if( !($static) ) { $num_c_args_expected--; } #The cpp method will need an Object* paramater at the start. my $num_cpp_args = scalar(@{$cpp_param_types}); # add implicit last error parameter; if ( $automatic_error ne "" && $num_cpp_args == ($num_c_args_expected - 1) && ${$c_param_types}[-1] eq "GError**" ) { $num_cpp_args++; $cpp_param_names = [@{$cpp_param_names},"error"]; $cpp_param_types = [@{$cpp_param_types},"GError*&"]; } if ( $num_cpp_args != $num_c_args_expected ) { Output::error( "convert_args_cpp_to_c(): Incorrect number of arguments. (%d != %d)\n", $num_cpp_args, $num_c_args_expected ); $objCppfunc->dump(); $objCDefsFunc->dump(); return ""; } # Loop through the cpp parameters: my $i; my $cpp_param_max = $num_cpp_args; # if( !($static) ) { $cpp_param_max++; } for ($i = 0; $i < $cpp_param_max; $i++) { #index of C parameter: my $iCParam = $i; if( !($static) ) { $iCParam++; } my $cppParamType = $$cpp_param_types[$i]; $cppParamType =~ s/ &/&/g; #Remove space between type and & $cppParamType =~ s/ \*/*/g; #Remove space between type and * my $cppParamName = $$cpp_param_names[$i]; my $cParamType = $$c_param_types[$iCParam]; if ($cppParamType ne $cParamType) #If a type conversion is needed. { push(@result, sprintf("_CONVERT(%s,%s,%s,%s)", $cppParamType, $cParamType, $cppParamName, $wrap_line_number) ); } else { push(@result, $cppParamName); } } return join(", ", @result); } # procedure for generating CONVERT macros # Ignores the first C 'self' argument. # $string convert_args_c_to_cpp($objCDefsFunc, $objCppFunc, $wrap_line_number) sub convert_args_c_to_cpp($$$) { my ($objCDefsFunc, $objCppfunc, $wrap_line_number) = @_; my $cpp_param_types = $$objCppfunc{param_types}; my $c_param_types = $$objCDefsFunc{param_types}; my $c_param_names = $$objCDefsFunc{param_names}; my @result; my $num_c_args = scalar(@{$c_param_types}); my $num_cpp_args = scalar(@{$cpp_param_types}); if ( ($num_cpp_args + 1) != $num_c_args ) { Output::error( "convert_args_c_to_cpp(): Incorrect number of arguments. (%d != %d)\n", $num_cpp_args + 1, $num_c_args); $objCppfunc->dump(); $objCDefsFunc->dump(); return ""; } # Loop through the c parameters: my $i; my $c_param_max = $num_c_args; for ($i = 1; $i < $c_param_max; $i++) { #index of C parameter: my $iCppParam = $i - 1; my $cppParamType = $$cpp_param_types[$iCppParam]; $cppParamType =~ s/ &/&/g; #Remove space between type and &. $cppParamType =~ s/ \*/*/g; #Remove space between type and * my $cParamName = $$c_param_names[$i]; my $cParamType = $$c_param_types[$i]; if ($cParamType ne $cppParamType) #If a type conversion is needed. { push(@result, sprintf("_CONVERT(%s,%s,%s,%s)\n", $cParamType, $cppParamType, $cParamName, $wrap_line_number) ); } else { push(@result, $cParamName); } } return join(", ",@result); } # generates the XXX in g_object_new(get_type(), XXX): A list of property names and values. # Uses the cpp arg name as the property name. # $string get_ctor_properties($objCppfunc, $objCDefsFunc, $wrap_line_number) sub get_ctor_properties($$$$) { my ($objCppfunc, $objCDefsFunc, $wrap_line_number) = @_; my $cpp_param_names = $$objCppfunc{param_names}; my $cpp_param_types = $$objCppfunc{param_types}; my $c_param_types = $$objCDefsFunc{param_types}; my @result; my $num_args = scalar(@{$c_param_types}); my $num_cpp_args = scalar(@{$cpp_param_types}); if ( $num_cpp_args != $num_args ) { Output::error("get_ctor_properties(): Incorrect number of arguments. (%d != %d)\n", $num_cpp_args, $num_args ); return ""; } # Loop through the cpp parameters: my $i = 0; for ($i = 0; $i < $num_args; $i++) { my $cppParamType = $$cpp_param_types[$i]; $cppParamType =~ s/ &/&/g; #Remove space between type and & $cppParamType =~ s/ \*/*/g; #Remove space between type and * my $cppParamName = $$cpp_param_names[$i]; my $cParamType = $$c_param_types[$i]; # Property name: push(@result, "\"" . $cppParamName . "\""); # C property value: if ($cppParamType ne $cParamType) #If a type conversion is needed. { push(@result, sprintf("_CONVERT(%s,%s,%s,%s)", $cppParamType, $cParamType, $cppParamName, $wrap_line_number) ); } else { push(@result, $cppParamName); } } return join(", ", @result); } ### Convert _WRAP to a corba method # _CORBA_METHOD(retype, method_name,args, arg_names_only) - implemented in libbonobomm. # void output_wrap_corba_method($filename, $line_num, $objCppFunc) sub output_wrap_corba_method($$$$) { my ($self, $filename, $line_num, $objCppfunc) = @_; my $str = sprintf("_CORBA_METHOD(%s,%s,\`%s\',\`%s\')dnl\n", $$objCppfunc{rettype}, $$objCppfunc{name}, $objCppfunc->args_types_and_names(), $objCppfunc->args_names_only() ); $self->append($str); } 1; # indicate proper module load.