#!@PERL@ use strict; use lib "../../infrastructure"; use BoxPlatform; # Make protocol C++ classes from a protocol description file # built in type info (values are is basic type, C++ typename) # may get stuff added to it later if protocol uses extra types my %translate_type_info = ( 'int64' => [1, 'int64_t'], 'int32' => [1, 'int32_t'], 'int16' => [1, 'int16_t'], 'int8' => [1, 'int8_t'], 'bool' => [1, 'bool'], 'string' => [0, 'std::string'] ); # built in instructions for logging various types # may be added to my %log_display_types = ( 'int64' => ['0x%llx', 'VAR'], 'int32' => ['0x%x', 'VAR'], 'int16' => ['0x%x', 'VAR'], 'int8' => ['0x%x', 'VAR'], 'bool' => ['%s', '((VAR)?"true":"false")'], 'string' => ['%s', 'VAR.c_str()'] ); <<<<<<< HEAD my ($type, $file) = @ARGV; if($type ne 'Server' && $type ne 'Client') { die "Neither Server or Client is specified on command line\n"; } open IN, $file or die "Can't open input file $file\n"; print "Making $type protocol classes from $file...\n"; my @extra_header_files; my $implement_syslog = 0; my $implement_filelog = 0; ======= if (@ARGV != 1) { die "Usage: $0 \n"; } my ($file) = @ARGV; open IN, $file or die "Can't open input file $file\n"; print "Making protocol classes from $file...\n"; my @extra_header_files; >>>>>>> 0.12 # read attributes my %attr; while() { # get and clean line my $l = $_; $l =~ s/#.*\Z//; $l =~ s/\A\s+//; $l =~ s/\s+\Z//; next unless $l =~ m/\S/; last if $l eq 'BEGIN_OBJECTS'; my ($k,$v) = split /\s+/,$l,2; <<<<<<< HEAD if($k eq 'ClientType') { add_type($v) if $type eq 'Client'; } elsif($k eq 'ServerType') { add_type($v) if $type eq 'Server'; } elsif($k eq 'ImplementLog') { my ($log_if_type,$log_type) = split /\s+/,$v; if($type eq $log_if_type) { if($log_type eq 'syslog') { $implement_syslog = 1; } elsif($log_type eq 'file') { $implement_filelog = 1; } else { printf("ERROR: Unknown log type for implementation: $log_type\n"); exit(1); } } } elsif($k eq 'LogTypeToText') { my ($log_if_type,$type_name,$printf_format,$arg_template) = split /\s+/,$v; if($type eq $log_if_type) { $log_display_types{$type_name} = [$printf_format,$arg_template] } ======= if($k eq 'AddType') { add_type($v); } elsif($k eq 'ImplementLog') { # Always implement logging } elsif($k eq 'LogTypeToText') { my ($type_name,$printf_format,$arg_template) = split /\s+/,$v; $log_display_types{$type_name} = [$printf_format,$arg_template] >>>>>>> 0.12 } else { $attr{$k} = $v; } } sub add_type { my ($protocol_name, $cpp_name, $header_file) = split /\s+/,$_[0]; $translate_type_info{$protocol_name} = [0, $cpp_name]; push @extra_header_files, $header_file; } # check attributes for(qw/Name ServerContextClass IdentString/) { if(!exists $attr{$_}) { die "Attribute $_ is required, but not specified\n"; } } my $protocol_name = $attr{'Name'}; my ($context_class, $context_class_inc) = split /\s+/,$attr{'ServerContextClass'}; my $ident_string = $attr{'IdentString'}; my $current_cmd = ''; my %cmd_contents; my %cmd_attributes; my %cmd_constants; my %cmd_id; my @cmd_list; # read in the command definitions while() { # get and clean line my $l = $_; $l =~ s/#.*\Z//; $l =~ s/\s+\Z//; next unless $l =~ m/\S/; # definitions or new command thing? if($l =~ m/\A\s+/) { die "No command defined yet" if $current_cmd eq ''; # definition of component $l =~ s/\A\s+//; my ($type,$name,$value) = split /\s+/,$l; if($type eq 'CONSTANT') { push @{$cmd_constants{$current_cmd}},"$name = $value" } else { push @{$cmd_contents{$current_cmd}},$type,$name; } } else { # new command my ($name,$id,@attributes) = split /\s+/,$l; $cmd_attributes{$name} = [@attributes]; $cmd_id{$name} = int($id); $current_cmd = $name; push @cmd_list,$name; } } close IN; # open files <<<<<<< HEAD my $h_filename = 'autogen_'.$protocol_name.'Protocol'.$type.'.h'; open CPP,'>autogen_'.$protocol_name.'Protocol'.$type.'.cpp'; open H,">$h_filename"; ======= my $filename_base = 'autogen_'.$protocol_name.'Protocol'; print "Writing $filename_base.cpp\n"; print "Writing $filename_base.h\n"; open CPP, "> $filename_base.cpp"; open H, "> $filename_base.h"; my $guardname = uc 'AUTOGEN_'.$protocol_name.'Protocol_H'; >>>>>>> 0.12 print CPP <<__E; // Auto-generated file -- do not edit #include "Box.h" #include <<<<<<< HEAD #include "$h_filename" #include "IOStream.h" __E if($implement_syslog) { print H < #endif EOF } my $guardname = uc 'AUTOGEN_'.$protocol_name.'Protocol'.$type.'_H'; print H <<__E; ======= #include "$filename_base.h" #include "IOStream.h" __E print H <<__E; >>>>>>> 0.12 // Auto-generated file -- do not edit #ifndef $guardname #define $guardname <<<<<<< HEAD #include "Protocol.h" #include "ProtocolObject.h" ======= #include #include #ifndef WIN32 #include #endif #include "Protocol.h" #include "Message.h" >>>>>>> 0.12 #include "ServerException.h" class IOStream; <<<<<<< HEAD __E if($implement_filelog) { print H qq~#include \n~; } ======= __E >>>>>>> 0.12 # extra headers for(@extra_header_files) { <<<<<<< HEAD print H qq~#include "$_"\n~ } print H "\n"; if($type eq 'Server') { # need utils file for the server print H '#include "Utils.h"',"\n\n" } my $derive_objects_from = 'ProtocolObject'; my $objects_extra_h = ''; my $objects_extra_cpp = ''; if($type eq 'Server') { # define the context print H "class $context_class;\n\n"; print CPP "#include \"$context_class_inc\"\n\n"; # change class we derive the objects from $derive_objects_from = $protocol_name.'ProtocolObject'; $objects_extra_h = <<__E; virtual std::auto_ptr DoCommand(${protocol_name}ProtocolServer &rProtocol, $context_class &rContext); __E $objects_extra_cpp = <<__E; std::auto_ptr ${derive_objects_from}::DoCommand(${protocol_name}ProtocolServer &rProtocol, $context_class &rContext) { THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand) } __E } print CPP qq~#include "MemLeakFindOn.h"\n~; if($type eq 'Client' && ($implement_syslog || $implement_filelog)) { # change class we derive the objects from $derive_objects_from = $protocol_name.'ProtocolObjectCl'; } if($implement_syslog) { $objects_extra_h .= <<__E; virtual void LogSysLog(const char *Action) const = 0; __E } if($implement_filelog) { $objects_extra_h .= <<__E; virtual void LogFile(const char *Action, FILE *file) const = 0; __E } if($derive_objects_from ne 'ProtocolObject') { # output a definition for the protocol object derived class print H <<__E; class ${protocol_name}ProtocolServer; class $derive_objects_from : public ProtocolObject { public: $derive_objects_from(); virtual ~$derive_objects_from(); $derive_objects_from(const $derive_objects_from &rToCopy); $objects_extra_h }; __E # and some cpp definitions print CPP <<__E; ${derive_objects_from}::${derive_objects_from}() { } ${derive_objects_from}::~${derive_objects_from}() { } ${derive_objects_from}::${derive_objects_from}(const $derive_objects_from &rToCopy) { } $objects_extra_cpp __E } my $classname_base = $protocol_name.'Protocol'.$type; # output the classes for my $cmd (@cmd_list) { print H <<__E; class $classname_base$cmd : public $derive_objects_from { public: $classname_base$cmd(); $classname_base$cmd(const $classname_base$cmd &rToCopy); ~$classname_base$cmd(); ======= print H qq@#include "$_"\n@; } print H <<__E; // need utils file for the server #include "Utils.h" __E my $message_base_class = "${protocol_name}ProtocolMessage"; my $objects_extra_h = ''; my $objects_extra_cpp = ''; # define the context print H "class $context_class;\n\n"; print CPP <<__E; #include "$context_class_inc" #include "MemLeakFindOn.h" __E my $request_base_class = "${protocol_name}ProtocolRequest"; my $reply_base_class = "${protocol_name}ProtocolReply"; # the abstract protocol interface my $protocol_base_class = $protocol_name."ProtocolBase"; my $replyable_base_class = $protocol_name."ProtocolReplyable"; print H <<__E; class $protocol_base_class; class $replyable_base_class; class $reply_base_class; class $message_base_class : public Message { public: virtual std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const; }; class $reply_base_class { }; class $request_base_class { }; __E print CPP <<__E; std::auto_ptr<$message_base_class> $message_base_class\::DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const { THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand) } __E my %cmd_class; # output the classes foreach my $cmd (@cmd_list) { my @cmd_base_classes = ($message_base_class); if(obj_is_type($cmd, 'Command')) { push @cmd_base_classes, $request_base_class; } if(obj_is_type($cmd, 'Reply')) { push @cmd_base_classes, $reply_base_class; } my $cmd_base_class = join(", ", map {"public $_"} @cmd_base_classes); my $cmd_class = $protocol_name."Protocol".$cmd; $cmd_class{$cmd} = $cmd_class; print H <<__E; class $cmd_class : $cmd_base_class { public: $cmd_class(); $cmd_class(const $cmd_class &rToCopy); ~$cmd_class(); >>>>>>> 0.12 int GetType() const; enum { TypeID = $cmd_id{$cmd} }; __E <<<<<<< HEAD ======= >>>>>>> 0.12 # constants if(exists $cmd_constants{$cmd}) { print H "\tenum\n\t{\n\t\t"; print H join(",\n\t\t",@{$cmd_constants{$cmd}}); print H "\n\t};\n"; } <<<<<<< HEAD ======= >>>>>>> 0.12 # flags if(obj_is_type($cmd,'EndsConversation')) { print H "\tbool IsConversationEnd() const;\n"; } <<<<<<< HEAD ======= >>>>>>> 0.12 if(obj_is_type($cmd,'IsError')) { print H "\tbool IsError(int &rTypeOut, int &rSubTypeOut) const;\n"; print H "\tstd::string GetMessage() const;\n"; } <<<<<<< HEAD if($type eq 'Server' && obj_is_type($cmd, 'Command')) { print H "\tstd::auto_ptr DoCommand(${protocol_name}ProtocolServer &rProtocol, $context_class &rContext); // IMPLEMENT THIS\n" } # want to be able to read from streams? my $read_from_streams = (obj_is_type($cmd,'Command') && $type eq 'Server') || (obj_is_type($cmd,'Reply') && $type eq 'Client'); my $write_to_streams = (obj_is_type($cmd,'Command') && $type eq 'Client') || (obj_is_type($cmd,'Reply') && $type eq 'Server'); if($read_from_streams) { print H "\tvoid SetPropertiesFromStreamData(Protocol &rProtocol);\n"; # write Get functions for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); print H "\t".translate_type_to_arg_type($ty)." Get$nm() {return m$nm;}\n"; } } my $param_con_args = ''; if($write_to_streams) { # extra constructor? if($#{$cmd_contents{$cmd}} >= 0) { my @a; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); push @a,translate_type_to_arg_type($ty)." $nm"; } $param_con_args = join(', ',@a); print H "\t$classname_base$cmd(".$param_con_args.");\n"; } print H "\tvoid WritePropertiesToStreamData(Protocol &rProtocol) const;\n"; # set functions for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); print H "\tvoid Set$nm(".translate_type_to_arg_type($ty)." $nm) {m$nm = $nm;}\n"; } } if($implement_syslog) { print H "\tvirtual void LogSysLog(const char *Action) const;\n"; } if($implement_filelog) { print H "\tvirtual void LogFile(const char *Action, FILE *file) const;\n"; } ======= if(obj_is_type($cmd, 'Command')) { print H <<__E; std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const; // IMPLEMENT THIS\n __E } # want to be able to read from streams? print H "\tvoid SetPropertiesFromStreamData(Protocol &rProtocol);\n"; # write Get functions for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); print H "\t".translate_type_to_arg_type($ty)." Get$nm() {return m$nm;}\n"; } my $param_con_args = ''; # extra constructor? if($#{$cmd_contents{$cmd}} >= 0) { my @a; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); push @a,translate_type_to_arg_type($ty)." $nm"; } $param_con_args = join(', ',@a); print H "\t$cmd_class(".$param_con_args.");\n"; } print H "\tvoid WritePropertiesToStreamData(Protocol &rProtocol) const;\n"; # set functions for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); print H "\tvoid Set$nm(".translate_type_to_arg_type($ty)." $nm) {m$nm = $nm;}\n"; } print H "\tvirtual void LogSysLog(const char *Action) const;\n"; print H "\tvirtual void LogFile(const char *Action, FILE *file) const;\n"; print H "\tvirtual std::string ToString() const;\n"; >>>>>>> 0.12 # write member variables and setup for cpp file my @def_constructor_list; my @copy_constructor_list; my @param_constructor_list; print H "private:\n"; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); print H "\t".translate_type_to_member_type($ty)." m$nm;\n"; my ($basic,$typename) = translate_type($ty); if($basic) { push @def_constructor_list, "m$nm(0)"; } push @copy_constructor_list, "m$nm(rToCopy.m$nm)"; push @param_constructor_list, "m$nm($nm)"; } # finish off print H "};\n\n"; # now the cpp file... my $def_con_vars = join(",\n\t ",@def_constructor_list); $def_con_vars = "\n\t: ".$def_con_vars if $def_con_vars ne ''; my $copy_con_vars = join(",\n\t ",@copy_constructor_list); $copy_con_vars = "\n\t: ".$copy_con_vars if $copy_con_vars ne ''; my $param_con_vars = join(",\n\t ",@param_constructor_list); $param_con_vars = "\n\t: ".$param_con_vars if $param_con_vars ne ''; <<<<<<< HEAD my $class = "$classname_base$cmd".'::'; print CPP <<__E; $class$classname_base$cmd()$def_con_vars { } $class$classname_base$cmd(const $classname_base$cmd &rToCopy)$copy_con_vars { } $class~$classname_base$cmd() { } int ${class}GetType() const ======= print CPP <<__E; $cmd_class\::$cmd_class()$def_con_vars { } $cmd_class\::$cmd_class(const $cmd_class &rToCopy)$copy_con_vars { } $cmd_class\::~$cmd_class() { } int $cmd_class\::GetType() const >>>>>>> 0.12 { return $cmd_id{$cmd}; } __E <<<<<<< HEAD if($read_from_streams) { print CPP "void ${class}SetPropertiesFromStreamData(Protocol &rProtocol)\n{\n"; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); if($ty =~ m/\Avector/) { print CPP "\trProtocol.ReadVector(m$nm);\n"; } else { print CPP "\trProtocol.Read(m$nm);\n"; } } print CPP "}\n"; } if($write_to_streams) { # implement extra constructor? if($param_con_vars ne '') { print CPP "$class$classname_base$cmd($param_con_args)$param_con_vars\n{\n}\n"; } print CPP "void ${class}WritePropertiesToStreamData(Protocol &rProtocol) const\n{\n"; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); if($ty =~ m/\Avector/) { print CPP "\trProtocol.WriteVector(m$nm);\n"; } else { print CPP "\trProtocol.Write(m$nm);\n"; } } print CPP "}\n"; } if(obj_is_type($cmd,'EndsConversation')) { print CPP "bool ${class}IsConversationEnd() const\n{\n\treturn true;\n}\n"; } ======= print CPP "void $cmd_class\::SetPropertiesFromStreamData(Protocol &rProtocol)\n{\n"; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); if($ty =~ m/\Avector/) { print CPP "\trProtocol.ReadVector(m$nm);\n"; } else { print CPP "\trProtocol.Read(m$nm);\n"; } } print CPP "}\n"; # implement extra constructor? if($param_con_vars ne '') { print CPP "$cmd_class\::$cmd_class($param_con_args)$param_con_vars\n{\n}\n"; } print CPP "void $cmd_class\::WritePropertiesToStreamData(Protocol &rProtocol) const\n{\n"; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); if($ty =~ m/\Avector/) { print CPP "\trProtocol.WriteVector(m$nm);\n"; } else { print CPP "\trProtocol.Write(m$nm);\n"; } } print CPP "}\n"; if(obj_is_type($cmd,'EndsConversation')) { print CPP "bool $cmd_class\::IsConversationEnd() const\n{\n\treturn true;\n}\n"; } >>>>>>> 0.12 if(obj_is_type($cmd,'IsError')) { # get parameters my ($mem_type,$mem_subtype) = split /,/,obj_get_type_params($cmd,'IsError'); print CPP <<__E; <<<<<<< HEAD bool ${class}IsError(int &rTypeOut, int &rSubTypeOut) const ======= bool $cmd_class\::IsError(int &rTypeOut, int &rSubTypeOut) const >>>>>>> 0.12 { rTypeOut = m$mem_type; rSubTypeOut = m$mem_subtype; return true; } <<<<<<< HEAD std::string ${class}GetMessage() const ======= std::string $cmd_class\::GetMessage() const >>>>>>> 0.12 { switch(m$mem_subtype) { __E foreach my $const (@{$cmd_constants{$cmd}}) { next unless $const =~ /^Err_(.*)/; my $shortname = $1; $const =~ s/ = .*//; print CPP <<__E; case $const: return "$shortname"; __E } print CPP <<__E; default: std::ostringstream out; out << "Unknown subtype " << m$mem_subtype; return out.str(); } } __E } <<<<<<< HEAD if($implement_syslog) { my ($log) = make_log_strings_framework($cmd); print CPP <<__E; void ${class}LogSysLog(const char *Action) const { BOX_TRACE($log); } __E } if($implement_filelog) { my ($log) = make_log_strings_framework($cmd); print CPP <<__E; void ${class}LogFile(const char *Action, FILE *File) const { std::ostringstream oss; oss << $log; ::fprintf(File, "%s\\n", oss.str().c_str()); ::fflush(File); } __E } } # finally, the protocol object itself print H <<__E; class $classname_base : public Protocol { public: $classname_base(IOStream &rStream); virtual ~$classname_base(); std::auto_ptr<$derive_objects_from> Receive(); void Send(const ${derive_objects_from} &rObject); __E if($implement_syslog) { print H "\tvoid SetLogToSysLog(bool Log = false) {mLogToSysLog = Log;}\n"; } if($implement_filelog) { print H "\tvoid SetLogToFile(FILE *File = 0) {mLogToFile = File;}\n"; } if($type eq 'Server') { # need to put in the conversation function print H "\tvoid DoServer($context_class &rContext);\n\n"; # and the send vector thing print H "\tvoid SendStreamAfterCommand(IOStream *pStream);\n\n"; } if($type eq 'Client') { # add plain object taking query functions my $with_params; for my $cmd (@cmd_list) { if(obj_is_type($cmd,'Command')) { my $has_stream = obj_is_type($cmd,'StreamWithCommand'); my $argextra = $has_stream?', IOStream &rStream':''; my $queryextra = $has_stream?', rStream':''; my $reply = obj_get_type_params($cmd,'Command'); print H "\tstd::auto_ptr<$classname_base$reply> Query(const $classname_base$cmd &rQuery$argextra);\n"; my @a; my @na; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); push @a,translate_type_to_arg_type($ty)." $nm"; push @na,"$nm"; } my $ar = join(', ',@a); my $nar = join(', ',@na); $nar = "($nar)" if $nar ne ''; $with_params .= "\tinline std::auto_ptr<$classname_base$reply> Query$cmd($ar$argextra)\n\t{\n"; $with_params .= "\t\t$classname_base$cmd send$nar;\n"; $with_params .= "\t\treturn Query(send$queryextra);\n"; $with_params .= "\t}\n"; } } # quick hack to correct bad argument lists for commands with zero paramters but with streams $with_params =~ s/\(, /(/g; print H "\n",$with_params,"\n"; } print H <<__E; private: $classname_base(const $classname_base &rToCopy); __E if($type eq 'Server') { # need to put the streams to send vector print H "\tstd::vector mStreamsToSend;\n\tvoid DeleteStreamsToSend();\n"; } if($implement_filelog || $implement_syslog) { print H <<__E; virtual void InformStreamReceiving(u_int32_t Size); virtual void InformStreamSending(u_int32_t Size); __E } if($implement_syslog) { print H "private:\n\tbool mLogToSysLog;\n"; } if($implement_filelog) { print H "private:\n\tFILE *mLogToFile;\n"; } print H <<__E; protected: virtual std::auto_ptr MakeProtocolObject(int ObjType); virtual const char *GetIdentString(); }; __E my $constructor_extra = ''; $constructor_extra .= ', mLogToSysLog(false)' if $implement_syslog; $constructor_extra .= ', mLogToFile(0)' if $implement_filelog; my $destructor_extra = ($type eq 'Server')?"\n\tDeleteStreamsToSend();":''; my $prefix = $classname_base.'::'; print CPP <<__E; $prefix$classname_base(IOStream &rStream) : Protocol(rStream)$constructor_extra { } $prefix~$classname_base() {$destructor_extra } const char *${prefix}GetIdentString() { return "$ident_string"; } std::auto_ptr ${prefix}MakeProtocolObject(int ObjType) ======= my ($log) = make_log_strings_framework($cmd); print CPP <<__E; std::string $cmd_class\::ToString() const { std::ostringstream oss; try { oss << $log; } catch(std::exception &e) { oss << "Failed to log command: " << e.what(); } return oss.str(); } void $cmd_class\::LogSysLog(const char *Action) const { try { BOX_TRACE(Action << " " << $log); } catch(std::exception &e) { BOX_WARNING("Failed to log command: " << Action << ": " << e.what()); } } void $cmd_class\::LogFile(const char *Action, FILE *File) const { ::fprintf(File, "%s %s\\n", Action, ToString().c_str()); ::fflush(File); } __E } my $error_class = $protocol_name."ProtocolError"; # the abstract protocol interface print H <<__E; class $protocol_base_class { public: $protocol_base_class(); virtual ~$protocol_base_class(); virtual const char *GetIdentString(); bool GetLastError(int &rTypeOut, int &rSubTypeOut); protected: void CheckReply(const std::string& requestCommand, const $message_base_class &rReply, int expectedType); void SetLastError(int Type, int SubType) { mLastErrorType = Type; mLastErrorSubType = SubType; } private: $protocol_base_class(const $protocol_base_class &rToCopy); /* do not call */ int mLastErrorType; int mLastErrorSubType; }; class $replyable_base_class : public virtual $protocol_base_class { public: $replyable_base_class(); virtual ~$replyable_base_class(); /* virtual std::auto_ptr<$message_base_class> Receive() = 0; virtual void Send(const ${message_base_class} &rObject) = 0; */ virtual std::auto_ptr ReceiveStream() = 0; virtual int GetTimeout() = 0; void SendStreamAfterCommand(std::auto_ptr apStream); protected: std::list mStreamsToSend; void DeleteStreamsToSend(); private: $replyable_base_class(const $replyable_base_class &rToCopy); /* do not call */ }; __E print CPP <<__E; $protocol_base_class\::$protocol_base_class() : mLastErrorType(Protocol::NoError), mLastErrorSubType(Protocol::NoError) { } $protocol_base_class\::~$protocol_base_class() { } const char *$protocol_base_class\::GetIdentString() { return "$ident_string"; } $replyable_base_class\::$replyable_base_class() { } $replyable_base_class\::~$replyable_base_class() { } void $replyable_base_class\::SendStreamAfterCommand(std::auto_ptr apStream) { ASSERT(apStream.get() != NULL); mStreamsToSend.push_back(apStream.release()); } void $replyable_base_class\::DeleteStreamsToSend() { for(std::list::iterator i(mStreamsToSend.begin()); i != mStreamsToSend.end(); ++i) { delete (*i); } mStreamsToSend.clear(); } void $protocol_base_class\::CheckReply(const std::string& requestCommand, const $message_base_class &rReply, int expectedType) { if(rReply.GetType() == expectedType) { // Correct response, do nothing } else { // Set protocol error int type, subType; if(rReply.IsError(type, subType)) { SetLastError(type, subType); THROW_EXCEPTION_MESSAGE(ConnectionException, Conn_Protocol_UnexpectedReply, requestCommand << " command failed: " "received error " << (($error_class&)rReply).GetMessage()); } else { SetLastError(Protocol::UnknownError, Protocol::UnknownError); THROW_EXCEPTION_MESSAGE(ConnectionException, Conn_Protocol_UnexpectedReply, requestCommand << " command failed: " "received unexpected response type " << rReply.GetType()); } } } // -------------------------------------------------------------------------- // // Function // Name: Protocol::GetLastError(int &, int &) // Purpose: Returns true if there was an error, and type and subtype if there was. // Created: 2003/08/19 // // -------------------------------------------------------------------------- bool $protocol_base_class\::GetLastError(int &rTypeOut, int &rSubTypeOut) { if(mLastErrorType == Protocol::NoError) { // no error. return false; } // Return type and subtype in args rTypeOut = mLastErrorType; rSubTypeOut = mLastErrorSubType; // and unset them mLastErrorType = Protocol::NoError; mLastErrorSubType = Protocol::NoError; return true; } __E # the callable protocol interface (implemented by Client and Local classes) # with Query methods that don't take a context parameter my $callable_base_class = $protocol_name."ProtocolCallable"; print H <<__E; class $callable_base_class : public virtual $protocol_base_class { public: virtual std::auto_ptr ReceiveStream() = 0; virtual int GetTimeout() = 0; __E # add plain object taking query functions my $with_params; for my $cmd (@cmd_list) { if(obj_is_type($cmd,'Command')) { my $has_stream = obj_is_type($cmd,'StreamWithCommand'); my $argextra = $has_stream?', std::auto_ptr apStream':''; my $queryextra = $has_stream?', apStream':''; my $request_class = $cmd_class{$cmd}; my $reply_class = $cmd_class{obj_get_type_params($cmd,'Command')}; print H "\tvirtual std::auto_ptr<$reply_class> Query(const $request_class &rQuery$argextra) = 0;\n"; my @a; my @na; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); push @a,translate_type_to_arg_type($ty)." $nm"; push @na,"$nm"; } my $ar = join(', ',@a); my $nar = join(', ',@na); $nar = "($nar)" if $nar ne ''; $with_params .= <<__E; inline std::auto_ptr<$reply_class> Query$cmd($ar$argextra) { $request_class send$nar; return Query(send$queryextra); } __E } } # quick hack to correct bad argument lists for commands with zero parameters but with streams $with_params =~ s/\(, /(/g; print H <<__E; $with_params }; __E # standard remote protocol objects foreach my $type ('Client', 'Server', 'Local') { my $writing_client = ($type eq 'Client'); my $writing_server = ($type eq 'Server'); my $writing_local = ($type eq 'Local'); my $server_or_client_class = $protocol_name."Protocol".$type; my @base_classes; if (not $writing_client) { push @base_classes, $replyable_base_class; } if (not $writing_server) { push @base_classes, $callable_base_class; } if (not $writing_local) { push @base_classes, "Protocol"; } my $base_classes_str = join(", ", map {"public $_"} @base_classes); print H <<__E; class $server_or_client_class : $base_classes_str { public: __E if($writing_local) { print H <<__E; $server_or_client_class($context_class &rContext); __E } else { print H <<__E; $server_or_client_class(IOStream &rStream); std::auto_ptr<$message_base_class> Receive(); void Send(const $message_base_class &rObject); __E } print H <<__E; virtual ~$server_or_client_class(); __E if($writing_server) { # need to put in the conversation function print H <<__E; void DoServer($context_class &rContext); __E } if($writing_client or $writing_local) { # add plain object taking query functions for my $cmd (@cmd_list) { if(obj_is_type($cmd,'Command')) { my $has_stream = obj_is_type($cmd,'StreamWithCommand'); my $argextra = $has_stream?', std::auto_ptr apStream':''; my $queryextra = $has_stream?', apStream':''; my $request_class = $cmd_class{$cmd}; my $reply_class = $cmd_class{obj_get_type_params($cmd,'Command')}; print H "\tstd::auto_ptr<$reply_class> Query(const $request_class &rQuery$argextra);\n"; } } } if($writing_local) { print H <<__E; private: $context_class &mrContext; __E } print H <<__E; protected: virtual std::auto_ptr MakeMessage(int ObjType); __E if($writing_local) { print H <<__E; virtual void InformStreamReceiving(u_int32_t Size) { } virtual void InformStreamSending(u_int32_t Size) { } public: virtual std::auto_ptr ReceiveStream() { std::auto_ptr apStream(mStreamsToSend.front()); mStreamsToSend.pop_front(); return apStream; } __E } else { print H <<__E; virtual void InformStreamReceiving(u_int32_t Size) { this->Protocol::InformStreamReceiving(Size); } virtual void InformStreamSending(u_int32_t Size) { this->Protocol::InformStreamSending(Size); } public: virtual std::auto_ptr ReceiveStream() { return this->Protocol::ReceiveStream(); } __E } print H <<__E; virtual const char *GetProtocolIdentString() { return GetIdentString(); } __E if($writing_local) { print H <<__E; virtual int GetTimeout() { return IOStream::TimeOutInfinite; } __E } else { print H <<__E; virtual int GetTimeout() { return this->Protocol::GetTimeout(); } __E } print H <<__E; /* virtual void Handshake() { this->Protocol::Handshake(); } virtual bool GetLastError(int &rTypeOut, int &rSubTypeOut) { return this->Protocol::GetLastError(rTypeOut, rSubTypeOut); } */ private: $server_or_client_class(const $server_or_client_class &rToCopy); /* no copies */ }; __E my $destructor_extra = ($writing_server) ? "\n\tDeleteStreamsToSend();" : ''; if($writing_local) { print CPP <<__E; $server_or_client_class\::$server_or_client_class($context_class &rContext) : mrContext(rContext) { } __E } else { print CPP <<__E; $server_or_client_class\::$server_or_client_class(IOStream &rStream) : Protocol(rStream) { } __E } print CPP <<__E; $server_or_client_class\::~$server_or_client_class() {$destructor_extra } __E # write receive and send functions print CPP <<__E; std::auto_ptr $server_or_client_class\::MakeMessage(int ObjType) >>>>>>> 0.12 { switch(ObjType) { __E <<<<<<< HEAD # do objects within this for my $cmd (@cmd_list) { print CPP <<__E; case $cmd_id{$cmd}: return std::auto_ptr(new $classname_base$cmd); break; __E } print CPP <<__E; ======= # do objects within this for my $cmd (@cmd_list) { print CPP <<__E; case $cmd_id{$cmd}: return std::auto_ptr(new $cmd_class{$cmd}()); break; __E } print CPP <<__E; >>>>>>> 0.12 default: THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnknownCommandRecieved) } } __E <<<<<<< HEAD # write receive and send functions print CPP <<__E; std::auto_ptr<$derive_objects_from> ${prefix}Receive() { std::auto_ptr<${derive_objects_from}> preply((${derive_objects_from}*)(Protocol::Receive().release())); __E if($implement_syslog) { print CPP <<__E; if(mLogToSysLog) { preply->LogSysLog("Receive"); } __E } if($implement_filelog) { print CPP <<__E; if(mLogToFile != 0) { preply->LogFile("Receive", mLogToFile); } __E } print CPP <<__E; ======= if(not $writing_local) { print CPP <<__E; std::auto_ptr<$message_base_class> $server_or_client_class\::Receive() { std::auto_ptr<$message_base_class> preply(($message_base_class *) Protocol::ReceiveInternal().release()); if(GetLogToSysLog()) { preply->LogSysLog("Receive"); } if(GetLogToFile() != 0) { preply->LogFile("Receive", GetLogToFile()); } >>>>>>> 0.12 return preply; } <<<<<<< HEAD void ${prefix}Send(const ${derive_objects_from} &rObject) { __E if($implement_syslog) { print CPP <<__E; if(mLogToSysLog) { rObject.LogSysLog("Send"); } __E } if($implement_filelog) { print CPP <<__E; if(mLogToFile != 0) { rObject.LogFile("Send", mLogToFile); } __E } print CPP <<__E; Protocol::Send(rObject); } __E # write server function? if($type eq 'Server') { print CPP <<__E; void ${prefix}DoServer($context_class &rContext) ======= void $server_or_client_class\::Send(const $message_base_class &rObject) { if(GetLogToSysLog()) { rObject.LogSysLog("Send"); } if(GetLogToFile() != 0) { rObject.LogFile("Send", GetLogToFile()); } Protocol::SendInternal(rObject); } __E } # write server function? if($writing_server) { print CPP <<__E; void $server_or_client_class\::DoServer($context_class &rContext) >>>>>>> 0.12 { // Handshake with client Handshake(); // Command processing loop bool inProgress = true; while(inProgress) { // Get an object from the conversation <<<<<<< HEAD std::auto_ptr<${derive_objects_from}> pobj(Receive()); // Run the command std::auto_ptr<${derive_objects_from}> preply((${derive_objects_from}*)(pobj->DoCommand(*this, rContext).release())); // Send the reply Send(*(preply.get())); // Send any streams for(unsigned int s = 0; s < mStreamsToSend.size(); s++) { // Send the streams SendStream(*mStreamsToSend[s]); } ======= std::auto_ptr<$message_base_class> pobj = Receive(); // Run the command std::auto_ptr<$message_base_class> preply = pobj->DoCommand(*this, rContext); // Send the reply Send(*preply); // Send any streams for(std::list::iterator i = mStreamsToSend.begin(); i != mStreamsToSend.end(); ++i) { SendStream(**i); } >>>>>>> 0.12 // Delete these streams DeleteStreamsToSend(); // Does this end the conversation? if(pobj->IsConversationEnd()) { inProgress = false; } } } <<<<<<< HEAD void ${prefix}SendStreamAfterCommand(IOStream *pStream) { ASSERT(pStream != NULL); mStreamsToSend.push_back(pStream); } void ${prefix}DeleteStreamsToSend() { for(std::vector::iterator i(mStreamsToSend.begin()); i != mStreamsToSend.end(); ++i) { delete (*i); } mStreamsToSend.clear(); } __E } # write logging functions? if($implement_filelog || $implement_syslog) { my ($fR,$fS); if($implement_syslog) { $fR .= <<__E; if(mLogToSysLog) { if(Size==Protocol::ProtocolStream_SizeUncertain) { BOX_TRACE("Receiving stream, size uncertain"); } else { BOX_TRACE("Receiving stream, size " << Size); } } __E $fS .= <<__E; if(mLogToSysLog) { if(Size==Protocol::ProtocolStream_SizeUncertain) { BOX_TRACE("Sending stream, size uncertain"); } else { BOX_TRACE("Sending stream, size " << Size); } } __E } if($implement_filelog) { $fR .= <<__E; if(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain) ?"Receiving stream, size uncertain\\n" :"Receiving stream, size %d\\n", Size); ::fflush(mLogToFile); } __E $fS .= <<__E; if(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain) ?"Sending stream, size uncertain\\n" :"Sending stream, size %d\\n", Size); ::fflush(mLogToFile); } __E } print CPP <<__E; void ${prefix}InformStreamReceiving(u_int32_t Size) { $fR} void ${prefix}InformStreamSending(u_int32_t Size) { $fS} __E } # write client Query functions? if($type eq 'Client') { for my $cmd (@cmd_list) { if(obj_is_type($cmd,'Command')) { my $reply = obj_get_type_params($cmd,'Command'); my $reply_id = $cmd_id{$reply}; my $has_stream = obj_is_type($cmd,'StreamWithCommand'); my $argextra = $has_stream?', IOStream &rStream':''; my $send_stream_extra = ''; if($has_stream) { $send_stream_extra = <<__E; // Send stream after the command SendStream(rStream); __E } print CPP <<__E; std::auto_ptr<$classname_base$reply> ${classname_base}::Query(const $classname_base$cmd &rQuery$argextra) ======= __E } # write client Query functions? if($writing_client or $writing_local) { for my $cmd (@cmd_list) { if(obj_is_type($cmd,'Command')) { my $request_class = $cmd_class{$cmd}; my $reply_msg = obj_get_type_params($cmd,'Command'); my $reply_class = $cmd_class{$reply_msg}; my $reply_id = $cmd_id{$reply_msg}; my $has_stream = obj_is_type($cmd,'StreamWithCommand'); my $argextra = $has_stream?', std::auto_ptr apStream':''; my $send_stream_extra = ''; my $send_stream_method = $writing_client ? "SendStream" : "SendStreamAfterCommand"; if($writing_client) { if($has_stream) { $send_stream_extra = <<__E; // Send stream after the command SendStream(*apStream); __E } print CPP <<__E; std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra) >>>>>>> 0.12 { // Send query Send(rQuery); $send_stream_extra <<<<<<< HEAD // Wait for the reply std::auto_ptr<${derive_objects_from}> preply(Receive().release()); if(preply->GetType() == $reply_id) { // Correct response return std::auto_ptr<$classname_base$reply>(($classname_base$reply*)preply.release()); } else { // Set protocol error int type, subType; if(preply->IsError(type, subType)) { SetError(type, subType); BOX_WARNING("$cmd command failed: received error " << ((${classname_base}Error&)*preply).GetMessage()); } else { SetError(Protocol::UnknownError, Protocol::UnknownError); BOX_WARNING("$cmd command failed: received " "unexpected response type " << preply->GetType()); } // Throw an exception THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnexpectedReply) } } __E ======= // Wait for the reply std::auto_ptr<$message_base_class> preply = Receive(); CheckReply("$cmd", *preply, $reply_id); // Correct response, if no exception thrown by CheckReply return std::auto_ptr<$reply_class>(($reply_class *)preply.release()); } __E } elsif($writing_local) { if($has_stream) { $send_stream_extra = <<__E; // Send stream after the command SendStreamAfterCommand(apStream); __E } print CPP <<__E; std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra) { // Send query $send_stream_extra std::auto_ptr<$message_base_class> preply = rQuery.DoCommand(*this, mrContext); CheckReply("$cmd", *preply, $reply_id); // Correct response, if no exception thrown by CheckReply return std::auto_ptr<$reply_class>(($reply_class *)preply.release()); } __E } } >>>>>>> 0.12 } } } <<<<<<< HEAD ======= >>>>>>> 0.12 print H <<__E; #endif // $guardname __E # close files close H; close CPP; <<<<<<< HEAD sub obj_is_type ======= sub obj_is_type ($$) >>>>>>> 0.12 { my ($c,$ty) = @_; for(@{$cmd_attributes{$c}}) { return 1 if $_ =~ m/\A$ty/; } return 0; } sub obj_get_type_params { my ($c,$ty) = @_; for(@{$cmd_attributes{$c}}) { return $1 if $_ =~ m/\A$ty\((.+?)\)\Z/; } die "Can't find attribute $ty\n" } # returns (is basic type, typename) sub translate_type { my $ty = $_[0]; if($ty =~ m/\Avector\<(.+?)\>\Z/) { my $v_type = $1; my (undef,$v_ty) = translate_type($v_type); return (0, 'std::vector<'.$v_ty.'>') } else { if(!exists $translate_type_info{$ty}) { die "Don't know about type name $ty\n"; } return @{$translate_type_info{$ty}} } } sub translate_type_to_arg_type { my ($basic,$typename) = translate_type(@_); return $basic?$typename:'const '.$typename.'&' } sub translate_type_to_member_type { my ($basic,$typename) = translate_type(@_); return $typename } <<<<<<< HEAD sub make_log_strings { my ($cmd) = @_; my @str; my @arg; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); if(exists $log_display_types{$ty}) { # need to translate it my ($format,$arg) = @{$log_display_types{$ty}}; $arg =~ s/VAR/m$nm/g; if ($format eq "0x%llx" and $target_windows) { $format = "0x%I64x"; $arg = "(uint64_t)$arg"; } push @str,$format; push @arg,$arg; } else { # is opaque push @str,'OPAQUE'; } } return ($cmd.'('.join(',',@str).')', join(',','',@arg)); } ======= >>>>>>> 0.12 sub make_log_strings_framework { my ($cmd) = @_; my @args; for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2) { my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]); if(exists $log_display_types{$ty}) { # need to translate it my ($format,$arg) = @{$log_display_types{$ty}}; $arg =~ s/VAR/m$nm/g; <<<<<<< HEAD if ($format eq '\\"%s\\"') ======= if ($format eq '"%s"') >>>>>>> 0.12 { $arg = "\"\\\"\" << $arg << \"\\\"\""; } elsif ($format =~ m'x$') { # my $width = 0; # $ty =~ /^int(\d+)$/ and $width = $1 / 4; $arg = "($arg == 0 ? \"0x\" : \"\") " . "<< std::hex " . "<< std::showbase " . # "<< std::setw($width) " . # "<< std::setfill('0') " . # "<< std::internal " . "<< $arg " . "<< std::dec"; } push @args, $arg; } else { # is opaque push @args, '"OPAQUE"'; } } <<<<<<< HEAD my $log_cmd = "Action << \" $cmd(\" "; ======= my $log_cmd = '"'.$cmd.'(" '; >>>>>>> 0.12 foreach my $arg (@args) { $arg = "<< $arg "; } $log_cmd .= join('<< "," ',@args); $log_cmd .= '<< ")"'; return $log_cmd; } <<<<<<< HEAD ======= >>>>>>> 0.12