#!@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()'] ); 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; # 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; 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] } 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 if $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 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'; print CPP <<__E; // Auto-generated file -- do not edit #include "Box.h" #include #include "$filename_base.h" #include "CollectInBufferStream.h" #include "MemBlockStream.h" #include "SelfFlushingStream.h" #include "SocketStream.h" __E print H <<__E; // Auto-generated file -- do not edit #ifndef $guardname #define $guardname #include #include #ifndef WIN32 #include #endif #include "Protocol.h" #include "Message.h" #include "ServerException.h" class IOStream; class SocketStream; __E # extra headers for(@extra_header_files) { 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 $custom_protocol_subclass = $protocol_name."Protocol"; my $client_server_base_class = $protocol_name."ProtocolClientServer"; my $replyable_base_class = $protocol_name."ProtocolReplyable"; my $callable_base_class = $protocol_name."ProtocolCallable"; print H <<__E; class $custom_protocol_subclass; class $client_server_base_class; class $callable_base_class; class $replyable_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; virtual std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext, IOStream& rDataStream) const; virtual bool HasStreamWithCommand() const = 0; }; class $reply_base_class { }; class $request_base_class { }; class $custom_protocol_subclass : public Protocol { public: $custom_protocol_subclass(std::auto_ptr apConn) : Protocol(apConn) { } virtual ~$custom_protocol_subclass() { } virtual std::auto_ptr MakeMessage(int ObjType); virtual const char *GetProtocolIdentString(); private: $custom_protocol_subclass(const $custom_protocol_subclass &rToCopy); }; __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) } std::auto_ptr<$message_base_class> $message_base_class\::DoCommand($replyable_base_class &rProtocol, $context_class &rContext, IOStream& rDataStream) const { THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand) } __E my %cmd_classes; my $error_message = undef; # 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_classes{$cmd} = $cmd_class; print H <<__E; class $cmd_class : $cmd_base_class { public: $cmd_class(); $cmd_class(const $cmd_class &rToCopy); ~$cmd_class(); int GetType() const; enum { TypeID = $cmd_id{$cmd} }; __E # 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"; } # flags if(obj_is_type($cmd,'EndsConversation')) { print H "\tbool IsConversationEnd() const;\n"; } if(obj_is_type($cmd,'IsError')) { $error_message = $cmd; my ($mem_type,$mem_subtype) = split /,/,obj_get_type_params($cmd,'IsError'); print H <<__E; bool IsError(int &rTypeOut, int &rSubTypeOut) const; std::string GetMessage() const { return GetMessage(m$mem_subtype); }; static std::string GetMessage(int subtype); __E } my $has_stream = obj_is_type($cmd, 'StreamWithCommand'); if(obj_is_type($cmd, 'Command') && $has_stream) { print H <<__E; std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext, IOStream& rDataStream) const; // IMPLEMENT THIS\n std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const { THROW_EXCEPTION_MESSAGE(CommonException, Internal, "This command requires a stream parameter"); } __E } elsif(obj_is_type($cmd, 'Command') && !$has_stream) { print H <<__E; std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const; // IMPLEMENT THIS\n std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext, IOStream& rDataStream) const { THROW_EXCEPTION_MESSAGE(CommonException, NotSupported, "This command requires no stream parameter"); } __E } print H <<__E; bool HasStreamWithCommand() const { return $has_stream; } __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"; # 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 ''; 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 { return $cmd_id{$cmd}; } __E 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"; } if(obj_is_type($cmd,'IsError')) { # get parameters my ($mem_type,$mem_subtype) = split /,/,obj_get_type_params($cmd,'IsError'); print CPP <<__E; bool $cmd_class\::IsError(int &rTypeOut, int &rSubTypeOut) const { rTypeOut = m$mem_type; rSubTypeOut = m$mem_subtype; return true; } std::string $cmd_class\::GetMessage(int subtype) { switch(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 " << subtype; return out.str(); } } __E } 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 $client_server_base_class { public: $client_server_base_class(); virtual ~$client_server_base_class(); virtual std::auto_ptr ReceiveStream() = 0; bool GetLastError(int &rTypeOut, int &rSubTypeOut); int GetLastErrorType() { return mLastErrorSubType; } protected: void SetLastError(int Type, int SubType) { mLastErrorType = Type; mLastErrorSubType = SubType; } std::string mPreviousCommand; std::string mPreviousReply; private: $client_server_base_class(const $client_server_base_class &rToCopy); /* do not call */ int mLastErrorType; int mLastErrorSubType; }; class $replyable_base_class : public virtual $client_server_base_class { public: $replyable_base_class() { } virtual ~$replyable_base_class(); 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; $client_server_base_class\::$client_server_base_class() : mLastErrorType(Protocol::NoError), mLastErrorSubType(Protocol::NoError) { } $client_server_base_class\::~$client_server_base_class() { } const char *$custom_protocol_subclass\::GetProtocolIdentString() { return "$ident_string"; } std::auto_ptr $custom_protocol_subclass\::MakeMessage(int ObjType) { switch(ObjType) { __E # do objects within this for my $cmd (@cmd_list) { print CPP <<__E; case $cmd_id{$cmd}: return std::auto_ptr(new $cmd_classes{$cmd}()); break; __E } print CPP <<__E; default: THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnknownCommandRecieved) } } $replyable_base_class\::~$replyable_base_class() { // If there were any streams left over, there's no longer any way to // access them, and we're responsible for them, so we'd better delete them. DeleteStreamsToSend(); } 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 $callable_base_class\::CheckReply(const std::string& requestCommandName, const $message_base_class &rCommand, const $message_base_class &rReply, int expectedType) { if(rReply.GetType() == expectedType) { // Correct response, do nothing SetLastError(Protocol::NoError, Protocol::NoError); } else { // Set protocol error int type, subType; if(rReply.IsError(type, subType)) { SetLastError(type, subType); THROW_EXCEPTION_MESSAGE(ConnectionException, Conn_Protocol_UnexpectedReply, requestCommandName << " command failed: " "received error " << (($error_class&)rReply).GetMessage()); } else { SetLastError(Protocol::UnknownError, Protocol::UnknownError); THROW_EXCEPTION_MESSAGE(ConnectionException, Conn_Protocol_UnexpectedReply, requestCommandName << " command failed: " "received unexpected response type " << rReply.GetType()); } } // As a client, if we get an unexpected reply later, we'll want to know // the last command that we executed, and the reply, to help debug the // server. mPreviousCommand = rCommand.ToString(); mPreviousReply = rReply.ToString(); } // -------------------------------------------------------------------------- // // 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 $client_server_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 print H <<__E; class $callable_base_class : public virtual $client_server_base_class { public: virtual int GetTimeout() = 0; protected: void CheckReply(const std::string& requestCommandName, const $message_base_class &rCommand, const $message_base_class &rReply, int expectedType); public: __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_classes{$cmd}; my $reply_class = $cmd_classes{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, $custom_protocol_subclass; } my $base_classes_str = join(", ", map {"public $_"} @base_classes); print H <<__E; class $server_or_client_class : $base_classes_str { public: virtual ~$server_or_client_class(); __E if($writing_local) { print H <<__E; $server_or_client_class($context_class &rContext); __E } else { print H <<__E; $server_or_client_class(std::auto_ptr apConn); std::auto_ptr<$message_base_class> Receive(); void Send(const $message_base_class &rObject); __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_classes{$cmd}; my $reply_class = $cmd_classes{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; public: virtual std::auto_ptr ReceiveStream() { std::auto_ptr apStream(mStreamsToSend.front()); mStreamsToSend.pop_front(); return apStream; } __E } else { print H <<__E; virtual std::auto_ptr ReceiveStream(); __E print CPP <<__E; std::auto_ptr $server_or_client_class\::ReceiveStream() { try { return $custom_protocol_subclass\::ReceiveStream(); } catch(ConnectionException &e) { if(e.GetSubType() == ConnectionException::Protocol_ObjWhenStreamExpected) { THROW_EXCEPTION_MESSAGE(ConnectionException, Protocol_ObjWhenStreamExpected, "Last exchange was " << mPreviousCommand << " => " << mPreviousReply); } else { throw; } } } __E } if($writing_local) { print H <<__E; virtual int GetTimeout() { return IOStream::TimeOutInfinite; } __E } else { print H <<__E; virtual int GetTimeout() { return $custom_protocol_subclass\::GetTimeout(); } __E } print H <<__E; 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(std::auto_ptr apConn) : $custom_protocol_subclass(apConn) { } __E } print CPP <<__E; $server_or_client_class\::~$server_or_client_class() {$destructor_extra } __E # write receive and send functions if(not $writing_local) { print CPP <<__E; std::auto_ptr<$message_base_class> $server_or_client_class\::Receive() { std::auto_ptr<$message_base_class> apReply; try { apReply = std::auto_ptr<$message_base_class>( static_cast<$message_base_class *> ($custom_protocol_subclass\::ReceiveInternal().release())); } catch(ConnectionException &e) { if(e.GetSubType() == ConnectionException::Protocol_StreamWhenObjExpected) { THROW_EXCEPTION_MESSAGE(ConnectionException, Protocol_StreamWhenObjExpected, "Last exchange was " << mPreviousCommand << " => " << mPreviousReply); } else { throw; } } if(GetLogToSysLog()) { apReply->LogSysLog("Receive"); } if(GetLogToFile() != 0) { apReply->LogFile("Receive", GetLogToFile()); } return apReply; } 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) { // Handshake with client Handshake(); // Command processing loop bool inProgress = true; while(inProgress) { // Get an object from the conversation std::auto_ptr<$message_base_class> pobj = Receive(); std::auto_ptr<$message_base_class> preply; // Run the command if(pobj->HasStreamWithCommand()) { std::auto_ptr apDataStream = ReceiveStream(); SelfFlushingStream autoflush(*apDataStream); preply = pobj->DoCommand(*this, rContext, *apDataStream); } else { 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); } // As a server, if we get an unexpected message later, we'll // want to know the last command that we received, and the // reply, to help debug our response to it. mPreviousCommand = pobj->ToString(); std::ostringstream reply; reply << preply->ToString() << " and " << mStreamsToSend.size() << " streams"; mPreviousReply = reply.str(); // Delete these streams DeleteStreamsToSend(); // Does this end the conversation? if(pobj->IsConversationEnd()) { inProgress = false; } } } __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_classes{$cmd}; my $reply_msg = obj_get_type_params($cmd,'Command'); my $reply_class = $cmd_classes{$reply_msg}; my $reply_id = $cmd_id{$reply_msg}; my $has_stream = obj_is_type($cmd,'StreamWithCommand'); my $argextra = $has_stream?', std::auto_ptr apDataStream':''; my $send_stream_extra = ''; my $send_stream_method = $writing_client ? "SendStream" : "SendStreamAfterCommand"; print CPP <<__E; std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra) { __E if($writing_client) { if($has_stream) { $send_stream_extra = <<__E; // Send stream after the command SendStream(*apDataStream); __E } print CPP <<__E; // Send query Send(rQuery); $send_stream_extra // Wait for the reply std::auto_ptr<$message_base_class> apReply = Receive(); __E } elsif($writing_local) { if($has_stream) { print CPP <<__E; std::auto_ptr<$message_base_class> apReply = rQuery.DoCommand(*this, mrContext, *apDataStream); __E } else { print CPP <<__E; std::auto_ptr<$message_base_class> apReply = rQuery.DoCommand(*this, mrContext); __E } } # Common to both client and local print CPP <<__E; CheckReply("$cmd", rQuery, *apReply, $reply_id); // Correct response, if no exception thrown by CheckReply return std::auto_ptr<$reply_class>( static_cast<$reply_class *>(apReply.release())); } __E } } } } print H <<__E; #endif // $guardname __E # close files close H; close CPP; sub obj_is_type ($$) { 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 } 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; if ($format eq '"%s"') { $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"'; } } my $log_cmd = '"'.$cmd.'(" '; foreach my $arg (@args) { $arg = "<< $arg "; } $log_cmd .= join('<< "," ',@args); $log_cmd .= '<< ")"'; return $log_cmd; }