source: box/trunk/lib/server/makeprotocol.pl.in @ 3100

Revision 3100, 24.5 KB checked in by chris, 4 weeks ago (diff)

Defend against exceptions during logging, e.g. CipherException? if filename decrypt fails.

  • Property svn:eol-style set to native
Line 
1#!@PERL@
2use strict;
3
4use lib "../../infrastructure";
5use BoxPlatform;
6
7# Make protocol C++ classes from a protocol description file
8
9# built in type info (values are is basic type, C++ typename)
10# may get stuff added to it later if protocol uses extra types
11my %translate_type_info =
12(
13        'int64' => [1, 'int64_t'],
14        'int32' => [1, 'int32_t'],
15        'int16' => [1, 'int16_t'],
16        'int8' => [1, 'int8_t'],
17        'bool' => [1, 'bool'],
18        'string' => [0, 'std::string']
19);
20
21# built in instructions for logging various types
22# may be added to
23my %log_display_types =
24(
25        'int64' => ['0x%llx', 'VAR'],
26        'int32' => ['0x%x', 'VAR'],
27        'int16' => ['0x%x', 'VAR'],
28        'int8' => ['0x%x', 'VAR'],
29        'bool' => ['%s', '((VAR)?"true":"false")'],
30        'string' => ['%s', 'VAR.c_str()']
31);
32
33if (@ARGV != 1)
34{
35        die "Usage: $0 <protocol-txt-file>\n";
36}
37
38my ($file) = @ARGV;
39
40open IN, $file or die "Can't open input file $file\n";
41
42print "Making protocol classes from $file...\n";
43
44my @extra_header_files;
45
46# read attributes
47my %attr;
48while(<IN>)
49{
50        # get and clean line
51        my $l = $_; $l =~ s/#.*\Z//; $l =~ s/\A\s+//; $l =~ s/\s+\Z//; next unless $l =~ m/\S/;
52       
53        last if $l eq 'BEGIN_OBJECTS';
54       
55        my ($k,$v) = split /\s+/,$l,2;
56       
57        if($k eq 'AddType')
58        {
59                add_type($v);
60        }
61        elsif($k eq 'ImplementLog')
62        {
63                # Always implement logging
64        }
65        elsif($k eq 'LogTypeToText')
66        {
67                my ($type_name,$printf_format,$arg_template) = split /\s+/,$v;
68                $log_display_types{$type_name} = [$printf_format,$arg_template]
69        }
70        else
71        {
72                $attr{$k} = $v;
73        }
74}
75
76sub add_type
77{
78        my ($protocol_name, $cpp_name, $header_file) = split /\s+/,$_[0];
79       
80        $translate_type_info{$protocol_name} = [0, $cpp_name];
81        push @extra_header_files, $header_file;
82}
83
84# check attributes
85for(qw/Name ServerContextClass IdentString/)
86{
87        if(!exists $attr{$_})
88        {
89                die "Attribute $_ is required, but not specified\n";
90        }
91}
92
93my $protocol_name = $attr{'Name'};
94my ($context_class, $context_class_inc) = split /\s+/,$attr{'ServerContextClass'};
95my $ident_string = $attr{'IdentString'};
96
97my $current_cmd = '';
98my %cmd_contents;
99my %cmd_attributes;
100my %cmd_constants;
101my %cmd_id;
102my @cmd_list;
103
104# read in the command definitions
105while(<IN>)
106{
107        # get and clean line
108        my $l = $_; $l =~ s/#.*\Z//; $l =~ s/\s+\Z//; next unless $l =~ m/\S/;
109       
110        # definitions or new command thing?
111        if($l =~ m/\A\s+/)
112        {
113                die "No command defined yet" if $current_cmd eq '';
114       
115                # definition of component
116                $l =~ s/\A\s+//;
117               
118                my ($type,$name,$value) = split /\s+/,$l;
119                if($type eq 'CONSTANT')
120                {
121                        push @{$cmd_constants{$current_cmd}},"$name = $value"
122                }
123                else
124                {
125                        push @{$cmd_contents{$current_cmd}},$type,$name;
126                }
127        }
128        else
129        {
130                # new command
131                my ($name,$id,@attributes) = split /\s+/,$l;
132                $cmd_attributes{$name} = [@attributes];
133                $cmd_id{$name} = int($id);
134                $current_cmd = $name;
135                push @cmd_list,$name;
136        }
137}
138
139close IN;
140
141
142
143# open files
144my $filename_base = 'autogen_'.$protocol_name.'Protocol';
145print "Writing $filename_base.cpp\n";
146print "Writing $filename_base.h\n";
147open CPP, "> $filename_base.cpp";
148open H,   "> $filename_base.h";
149
150my $guardname = uc 'AUTOGEN_'.$protocol_name.'Protocol_H';
151
152print CPP <<__E;
153
154// Auto-generated file -- do not edit
155
156#include "Box.h"
157
158#include <sstream>
159
160#include "$filename_base.h"
161#include "IOStream.h"
162__E
163
164print H <<__E;
165// Auto-generated file -- do not edit
166
167#ifndef $guardname
168#define $guardname
169
170#include <cstdio>
171#include <list>
172
173#ifndef WIN32
174#include <syslog.h>
175#endif
176
177#include "Protocol.h"
178#include "Message.h"
179#include "ServerException.h"
180
181class IOStream;
182
183
184__E
185
186# extra headers
187for(@extra_header_files)
188{
189        print H qq@#include "$_"\n@;
190}
191
192print H <<__E;
193
194// need utils file for the server
195#include "Utils.h"
196
197__E
198
199my $message_base_class = "${protocol_name}ProtocolMessage";
200my $objects_extra_h = '';
201my $objects_extra_cpp = '';
202
203# define the context
204print H "class $context_class;\n\n";
205print CPP <<__E;
206#include "$context_class_inc"
207#include "MemLeakFindOn.h"
208__E
209
210my $request_base_class = "${protocol_name}ProtocolRequest";
211my $reply_base_class   = "${protocol_name}ProtocolReply";
212# the abstract protocol interface
213my $protocol_base_class = $protocol_name."ProtocolBase";
214my $replyable_base_class = $protocol_name."ProtocolReplyable";
215
216print H <<__E;
217class $protocol_base_class;
218class $replyable_base_class;
219class $reply_base_class;
220
221class $message_base_class : public Message
222{
223public:
224        virtual std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol,
225                $context_class &rContext) const;
226};
227
228class $reply_base_class
229{
230};
231
232class $request_base_class
233{
234};
235
236__E
237
238print CPP <<__E;
239std::auto_ptr<$message_base_class> $message_base_class\::DoCommand($replyable_base_class &rProtocol,
240        $context_class &rContext) const
241{
242        THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand)
243}
244__E
245
246my %cmd_class;
247
248# output the classes
249foreach my $cmd (@cmd_list)
250{
251        my @cmd_base_classes = ($message_base_class);
252       
253        if(obj_is_type($cmd, 'Command'))
254        {
255                push @cmd_base_classes, $request_base_class;
256        }
257       
258        if(obj_is_type($cmd, 'Reply'))
259        {
260                push @cmd_base_classes, $reply_base_class;
261        }
262       
263        my $cmd_base_class = join(", ", map {"public $_"} @cmd_base_classes);
264        my $cmd_class = $protocol_name."Protocol".$cmd;
265        $cmd_class{$cmd} = $cmd_class;
266       
267        print H <<__E;
268class $cmd_class : $cmd_base_class
269{
270public:
271        $cmd_class();
272        $cmd_class(const $cmd_class &rToCopy);
273        ~$cmd_class();
274        int GetType() const;
275        enum
276        {
277                TypeID = $cmd_id{$cmd}
278        };
279__E
280
281        # constants
282        if(exists $cmd_constants{$cmd})
283        {
284                print H "\tenum\n\t{\n\t\t";
285                print H join(",\n\t\t",@{$cmd_constants{$cmd}});
286                print H "\n\t};\n";
287        }
288       
289        # flags
290        if(obj_is_type($cmd,'EndsConversation'))
291        {
292                print H "\tbool IsConversationEnd() const;\n";
293        }
294       
295        if(obj_is_type($cmd,'IsError'))
296        {
297                print H "\tbool IsError(int &rTypeOut, int &rSubTypeOut) const;\n";
298                print H "\tstd::string GetMessage() const;\n";
299        }
300       
301        if(obj_is_type($cmd, 'Command'))
302        {
303                print H <<__E;
304        std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol,
305                $context_class &rContext) const; // IMPLEMENT THIS\n
306__E
307        }
308
309        # want to be able to read from streams?
310        print H "\tvoid SetPropertiesFromStreamData(Protocol &rProtocol);\n";
311       
312        # write Get functions
313        for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
314        {
315                my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
316               
317                print H "\t".translate_type_to_arg_type($ty)." Get$nm() {return m$nm;}\n";
318        }
319
320        my $param_con_args = '';
321        # extra constructor?
322        if($#{$cmd_contents{$cmd}} >= 0)
323        {
324                my @a;
325                for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
326                {
327                        my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
328
329                        push @a,translate_type_to_arg_type($ty)." $nm";
330                }               
331                $param_con_args = join(', ',@a);
332                print H "\t$cmd_class(".$param_con_args.");\n";
333        }
334        print H "\tvoid WritePropertiesToStreamData(Protocol &rProtocol) const;\n";
335        # set functions
336        for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
337        {
338                my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
339               
340                print H "\tvoid Set$nm(".translate_type_to_arg_type($ty)." $nm) {m$nm = $nm;}\n";
341        }
342       
343        print H "\tvirtual void LogSysLog(const char *Action) const;\n";
344        print H "\tvirtual void LogFile(const char *Action, FILE *file) const;\n";
345       
346        # write member variables and setup for cpp file
347        my @def_constructor_list;
348        my @copy_constructor_list;
349        my @param_constructor_list;
350       
351        print H "private:\n";
352        for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
353        {
354                my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
355
356                print H "\t".translate_type_to_member_type($ty)." m$nm;\n";
357               
358                my ($basic,$typename) = translate_type($ty);
359                if($basic)
360                {
361                        push @def_constructor_list, "m$nm(0)";
362                }
363                push @copy_constructor_list, "m$nm(rToCopy.m$nm)";
364                push @param_constructor_list, "m$nm($nm)";
365        }
366       
367        # finish off
368        print H "};\n\n";
369       
370        # now the cpp file...
371        my $def_con_vars = join(",\n\t  ",@def_constructor_list);
372        $def_con_vars = "\n\t: ".$def_con_vars if $def_con_vars ne '';
373        my $copy_con_vars = join(",\n\t  ",@copy_constructor_list);
374        $copy_con_vars = "\n\t: ".$copy_con_vars if $copy_con_vars ne '';
375        my $param_con_vars = join(",\n\t  ",@param_constructor_list);
376        $param_con_vars = "\n\t: ".$param_con_vars if $param_con_vars ne '';
377
378        print CPP <<__E;
379$cmd_class\::$cmd_class()$def_con_vars
380{
381}
382$cmd_class\::$cmd_class(const $cmd_class &rToCopy)$copy_con_vars
383{
384}
385$cmd_class\::~$cmd_class()
386{
387}
388int $cmd_class\::GetType() const
389{
390        return $cmd_id{$cmd};
391}
392__E
393        print CPP "void $cmd_class\::SetPropertiesFromStreamData(Protocol &rProtocol)\n{\n";
394        for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
395        {
396                my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
397                if($ty =~ m/\Avector/)
398                {
399                        print CPP "\trProtocol.ReadVector(m$nm);\n";
400                }
401                else
402                {
403                        print CPP "\trProtocol.Read(m$nm);\n";
404                }
405        }
406        print CPP "}\n";
407
408        # implement extra constructor?
409        if($param_con_vars ne '')
410        {
411                print CPP "$cmd_class\::$cmd_class($param_con_args)$param_con_vars\n{\n}\n";
412        }
413        print CPP "void $cmd_class\::WritePropertiesToStreamData(Protocol &rProtocol) const\n{\n";
414        for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
415        {
416                my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
417                if($ty =~ m/\Avector/)
418                {
419                        print CPP "\trProtocol.WriteVector(m$nm);\n";
420                }
421                else
422                {
423                        print CPP "\trProtocol.Write(m$nm);\n";
424                }
425        }
426        print CPP "}\n";
427
428        if(obj_is_type($cmd,'EndsConversation'))
429        {
430                print CPP "bool $cmd_class\::IsConversationEnd() const\n{\n\treturn true;\n}\n";
431        }
432       
433        if(obj_is_type($cmd,'IsError'))
434        {
435                # get parameters
436                my ($mem_type,$mem_subtype) = split /,/,obj_get_type_params($cmd,'IsError');
437                print CPP <<__E;
438bool $cmd_class\::IsError(int &rTypeOut, int &rSubTypeOut) const
439{
440        rTypeOut = m$mem_type;
441        rSubTypeOut = m$mem_subtype;
442        return true;
443}
444std::string $cmd_class\::GetMessage() const
445{
446        switch(m$mem_subtype)
447        {
448__E
449                foreach my $const (@{$cmd_constants{$cmd}})
450                {
451                        next unless $const =~ /^Err_(.*)/;
452                        my $shortname = $1;
453                        $const =~ s/ = .*//;
454                        print CPP <<__E;
455                case $const: return "$shortname";
456__E
457                }
458                print CPP <<__E;
459                default:
460                        std::ostringstream out;
461                        out << "Unknown subtype " << m$mem_subtype;
462                        return out.str();
463        }
464}
465__E
466        }
467
468        my ($log) = make_log_strings_framework($cmd);
469        print CPP <<__E;
470void $cmd_class\::LogSysLog(const char *Action) const
471{
472        try
473        {
474                BOX_TRACE($log);
475        }
476        catch(std::exception &e)
477        {
478                BOX_WARNING("Failed to log command: " << Action << ": " <<
479                        e.what());
480        }
481}
482void $cmd_class\::LogFile(const char *Action, FILE *File) const
483{
484        std::ostringstream oss;
485        try
486        {
487                oss << $log;
488        }
489        catch(std::exception &e)
490        {
491                oss << "Failed to log command: " << Action << ": " <<
492                        e.what();
493        }
494        ::fprintf(File, "%s\\n", oss.str().c_str());
495        ::fflush(File);
496}
497__E
498}
499
500my $error_class = $protocol_name."ProtocolError";
501
502# the abstract protocol interface
503print H <<__E;
504class $protocol_base_class
505{
506public:
507        $protocol_base_class();
508        virtual ~$protocol_base_class();
509        virtual const char *GetIdentString();
510        bool GetLastError(int &rTypeOut, int &rSubTypeOut);
511
512protected:
513        void CheckReply(const std::string& requestCommand,
514                const $message_base_class &rReply, int expectedType);
515        void SetLastError(int Type, int SubType)
516        {
517                mLastErrorType = Type;
518                mLastErrorSubType = SubType;
519        }
520
521private:
522        $protocol_base_class(const $protocol_base_class &rToCopy); /* do not call */
523        int mLastErrorType;
524        int mLastErrorSubType;
525};
526
527class $replyable_base_class : public virtual $protocol_base_class
528{
529public:
530        $replyable_base_class();
531        virtual ~$replyable_base_class();
532
533        /*
534        virtual std::auto_ptr<$message_base_class> Receive() = 0;
535        virtual void Send(const ${message_base_class} &rObject) = 0;
536        */
537
538        virtual std::auto_ptr<IOStream> ReceiveStream() = 0;
539        virtual int GetTimeout() = 0;
540        void SendStreamAfterCommand(IOStream *pStream);
541       
542protected:
543        std::list<IOStream*> mStreamsToSend;
544        void DeleteStreamsToSend();
545
546private:
547        $replyable_base_class(const $replyable_base_class &rToCopy); /* do not call */
548};
549
550__E
551
552print CPP <<__E;
553$protocol_base_class\::$protocol_base_class()
554: mLastErrorType(Protocol::NoError),
555  mLastErrorSubType(Protocol::NoError)
556{ }
557
558$protocol_base_class\::~$protocol_base_class()
559{ }
560
561const char *$protocol_base_class\::GetIdentString()
562{
563        return "$ident_string";
564}
565
566$replyable_base_class\::$replyable_base_class()
567{ }
568
569$replyable_base_class\::~$replyable_base_class()
570{ }
571
572void $replyable_base_class\::SendStreamAfterCommand(IOStream *pStream)
573{
574        ASSERT(pStream != NULL);
575        mStreamsToSend.push_back(pStream);
576}
577
578void $replyable_base_class\::DeleteStreamsToSend()
579{
580        for(std::list<IOStream*>::iterator i(mStreamsToSend.begin()); i != mStreamsToSend.end(); ++i)
581        {
582                delete (*i);
583        }
584        mStreamsToSend.clear();
585}
586
587void $protocol_base_class\::CheckReply(const std::string& requestCommand,
588        const $message_base_class &rReply, int expectedType)
589{
590        if(rReply.GetType() == expectedType)
591        {
592                // Correct response, do nothing
593        }
594        else
595        {
596                // Set protocol error
597                int type, subType;
598               
599                if(rReply.IsError(type, subType))
600                {
601                        SetLastError(type, subType);
602                        THROW_EXCEPTION_MESSAGE(ConnectionException,
603                                Conn_Protocol_UnexpectedReply,
604                                requestCommand << " command failed: "
605                                "received error " <<
606                                (($error_class&)rReply).GetMessage());
607                }
608                else
609                {
610                        SetLastError(Protocol::UnknownError, Protocol::UnknownError);
611                        THROW_EXCEPTION_MESSAGE(ConnectionException,
612                                Conn_Protocol_UnexpectedReply,
613                                requestCommand << " command failed: "
614                                "received unexpected response type " <<
615                                rReply.GetType());
616                }
617        }
618}
619
620// --------------------------------------------------------------------------
621//
622// Function
623//              Name:    Protocol::GetLastError(int &, int &)
624//              Purpose: Returns true if there was an error, and type and subtype if there was.
625//              Created: 2003/08/19
626//
627// --------------------------------------------------------------------------
628bool $protocol_base_class\::GetLastError(int &rTypeOut, int &rSubTypeOut)
629{
630        if(mLastErrorType == Protocol::NoError)
631        {
632                // no error.
633                return false;
634        }
635       
636        // Return type and subtype in args
637        rTypeOut = mLastErrorType;
638        rSubTypeOut = mLastErrorSubType;
639       
640        // and unset them
641        mLastErrorType = Protocol::NoError;
642        mLastErrorSubType = Protocol::NoError;
643       
644        return true;
645}
646
647__E
648
649# the callable protocol interface (implemented by Client and Local classes)
650# with Query methods that don't take a context parameter
651my $callable_base_class = $protocol_name."ProtocolCallable";
652print H <<__E;
653class $callable_base_class : public virtual $protocol_base_class
654{
655public:
656        virtual std::auto_ptr<IOStream> ReceiveStream() = 0;
657        virtual int GetTimeout() = 0;
658__E
659
660# add plain object taking query functions
661my $with_params;
662for my $cmd (@cmd_list)
663{
664        if(obj_is_type($cmd,'Command'))
665        {
666                my $has_stream = obj_is_type($cmd,'StreamWithCommand');
667                my $argextra = $has_stream?', IOStream &rStream':'';
668                my $queryextra = $has_stream?', rStream':'';
669                my $request_class = $cmd_class{$cmd};
670                my $reply_class = $cmd_class{obj_get_type_params($cmd,'Command')};
671               
672                print H "\tvirtual std::auto_ptr<$reply_class> Query(const $request_class &rQuery$argextra) = 0;\n";
673                my @a;
674                my @na;
675                for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
676                {
677                        my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
678                        push @a,translate_type_to_arg_type($ty)." $nm";
679                        push @na,"$nm";
680                }
681                my $ar = join(', ',@a);
682                my $nar = join(', ',@na);
683                $nar = "($nar)" if $nar ne '';
684
685                $with_params .= <<__E;
686        inline std::auto_ptr<$reply_class> Query$cmd($ar$argextra)
687        {
688                $request_class send$nar;
689                return Query(send$queryextra);
690        }
691__E
692        }
693}
694
695# quick hack to correct bad argument lists for commands with zero parameters but with streams
696$with_params =~ s/\(, /(/g;
697
698print H <<__E;
699
700$with_params
701};
702__E
703
704# standard remote protocol objects
705foreach my $type ('Client', 'Server', 'Local')
706{
707        my $writing_client = ($type eq 'Client');
708        my $writing_server = ($type eq 'Server');
709        my $writing_local  = ($type eq 'Local');
710                       
711        my $server_or_client_class = $protocol_name."Protocol".$type;
712        my @base_classes;
713
714        if (not $writing_client)
715        {
716                push @base_classes, $replyable_base_class;
717        }
718        if (not $writing_server)
719        {
720                push @base_classes, $callable_base_class;
721        }
722        if (not $writing_local)
723        {
724                push @base_classes, "Protocol";
725        }
726
727        my $base_classes_str = join(", ", map {"public $_"} @base_classes);
728
729        print H <<__E;
730class $server_or_client_class : $base_classes_str
731{
732public:
733__E
734
735        if($writing_local)
736        {
737                print H <<__E;
738        $server_or_client_class($context_class &rContext);
739__E
740        }
741        else
742        {
743                print H <<__E;
744        $server_or_client_class(IOStream &rStream);
745        std::auto_ptr<$message_base_class> Receive();
746        void Send(const $message_base_class &rObject);
747__E
748        }
749       
750        print H <<__E;
751        virtual ~$server_or_client_class();
752__E
753
754        if($writing_server)
755        {
756                # need to put in the conversation function
757                print H <<__E; 
758        void DoServer($context_class &rContext);
759
760__E
761        }
762
763        if($writing_client or $writing_local)
764        {
765                # add plain object taking query functions
766                for my $cmd (@cmd_list)
767                {
768                        if(obj_is_type($cmd,'Command'))
769                        {
770                                my $has_stream = obj_is_type($cmd,'StreamWithCommand');
771                                my $argextra = $has_stream?', IOStream &rStream':'';
772                                my $queryextra = $has_stream?', rStream':'';
773                                my $request_class = $cmd_class{$cmd};
774                                my $reply_class = $cmd_class{obj_get_type_params($cmd,'Command')};
775                                print H "\tstd::auto_ptr<$reply_class> Query(const $request_class &rQuery$argextra);\n";
776                        }
777                }
778        }
779       
780        if($writing_local)
781        {
782                print H <<__E;
783private:
784        $context_class &mrContext;
785__E
786        }
787       
788        print H <<__E;
789
790protected:
791        virtual std::auto_ptr<Message> MakeMessage(int ObjType);
792
793__E
794
795        if($writing_local)
796        {
797                print H <<__E;
798        virtual void InformStreamReceiving(u_int32_t Size) { }
799        virtual void InformStreamSending(u_int32_t Size) { }
800
801public:
802        virtual std::auto_ptr<IOStream> ReceiveStream()
803        {
804                std::auto_ptr<IOStream> apStream(mStreamsToSend.front());
805                mStreamsToSend.pop_front();
806                return apStream;
807        }
808__E
809        }
810        else
811        {
812                print H <<__E;
813        virtual void InformStreamReceiving(u_int32_t Size)
814        {
815                this->Protocol::InformStreamReceiving(Size);
816        }
817        virtual void InformStreamSending(u_int32_t Size)
818        {
819                this->Protocol::InformStreamSending(Size);
820        }
821
822public:
823        virtual std::auto_ptr<IOStream> ReceiveStream()
824        {
825                return this->Protocol::ReceiveStream();
826        }
827__E
828        }
829
830        print H <<__E;
831        virtual const char *GetProtocolIdentString()
832        {
833                return GetIdentString();
834        }
835__E
836
837        if($writing_local)
838        {
839                print H <<__E;
840        virtual int GetTimeout()
841        {
842                return IOStream::TimeOutInfinite;
843        }
844__E
845        }
846        else
847        {
848                print H <<__E;
849        virtual int GetTimeout()
850        {
851                return this->Protocol::GetTimeout();
852        }
853__E
854        }       
855       
856        print H <<__E;
857        /*
858        virtual void Handshake()
859        {
860                this->Protocol::Handshake();
861        }
862        virtual bool GetLastError(int &rTypeOut, int &rSubTypeOut)
863        {
864                return this->Protocol::GetLastError(rTypeOut, rSubTypeOut);
865        }
866        */
867       
868private:
869        $server_or_client_class(const $server_or_client_class &rToCopy); /* no copies */
870};
871
872__E
873
874        my $destructor_extra = ($writing_server) ? "\n\tDeleteStreamsToSend();"
875                : '';
876
877        if($writing_local)
878        {
879                print CPP <<__E;
880$server_or_client_class\::$server_or_client_class($context_class &rContext)
881: mrContext(rContext)
882{ }
883__E
884        }
885        else
886        {
887                print CPP <<__E;
888$server_or_client_class\::$server_or_client_class(IOStream &rStream)
889: Protocol(rStream)
890{ }
891__E
892        }
893       
894        print CPP <<__E;
895$server_or_client_class\::~$server_or_client_class()
896{$destructor_extra
897}
898__E
899
900        # write receive and send functions
901        print CPP <<__E;
902std::auto_ptr<Message> $server_or_client_class\::MakeMessage(int ObjType)
903{
904        switch(ObjType)
905        {
906__E
907
908        # do objects within this
909        for my $cmd (@cmd_list)
910        {
911                print CPP <<__E;
912        case $cmd_id{$cmd}:
913                return std::auto_ptr<Message>(new $cmd_class{$cmd}());
914                break;
915__E
916        }
917
918        print CPP <<__E;
919        default:
920                THROW_EXCEPTION(ConnectionException, Conn_Protocol_UnknownCommandRecieved)
921        }
922}
923__E
924
925        if(not $writing_local)
926        {
927                print CPP <<__E;
928std::auto_ptr<$message_base_class> $server_or_client_class\::Receive()
929{
930        std::auto_ptr<$message_base_class> preply(($message_base_class *)
931                Protocol::ReceiveInternal().release());
932
933        if(GetLogToSysLog())
934        {
935                preply->LogSysLog("Receive");
936        }
937
938        if(GetLogToFile() != 0)
939        {
940                preply->LogFile("Receive", GetLogToFile());
941        }
942
943        return preply;
944}
945
946void $server_or_client_class\::Send(const $message_base_class &rObject)
947{
948        if(GetLogToSysLog())
949        {
950                rObject.LogSysLog("Send");
951        }
952
953        if(GetLogToFile() != 0)
954        {
955                rObject.LogFile("Send", GetLogToFile());
956        }
957
958        Protocol::SendInternal(rObject);
959}
960
961__E
962        }
963       
964        # write server function?
965        if($writing_server)
966        {
967                print CPP <<__E;
968void $server_or_client_class\::DoServer($context_class &rContext)
969{
970        // Handshake with client
971        Handshake();
972
973        // Command processing loop
974        bool inProgress = true;
975        while(inProgress)
976        {
977                // Get an object from the conversation
978                std::auto_ptr<$message_base_class> pobj = Receive();
979
980                // Run the command
981                std::auto_ptr<$message_base_class> preply = pobj->DoCommand(*this, rContext);
982               
983                // Send the reply
984                Send(*preply);
985
986                // Send any streams
987                for(std::list<IOStream*>::iterator
988                        i =  mStreamsToSend.begin();
989                        i != mStreamsToSend.end(); ++i)
990                {
991                        SendStream(**i);
992                }
993               
994                // Delete these streams
995                DeleteStreamsToSend();
996
997                // Does this end the conversation?
998                if(pobj->IsConversationEnd())
999                {
1000                        inProgress = false;
1001                }
1002        }       
1003}
1004
1005__E
1006        }
1007
1008        # write client Query functions?
1009        if($writing_client or $writing_local)
1010        {
1011                for my $cmd (@cmd_list)
1012                {
1013                        if(obj_is_type($cmd,'Command'))
1014                        {
1015                                my $request_class = $cmd_class{$cmd};
1016                                my $reply_msg = obj_get_type_params($cmd,'Command');
1017                                my $reply_class = $cmd_class{$reply_msg};
1018                                my $reply_id = $cmd_id{$reply_msg};
1019                                my $has_stream = obj_is_type($cmd,'StreamWithCommand');
1020                                my $argextra = $has_stream?', IOStream &rStream':'';
1021                                my $send_stream_extra = '';
1022                                my $send_stream_method = $writing_client ? "SendStream"
1023                                        : "SendStreamAfterCommand";
1024                               
1025                                if($writing_client)
1026                                {
1027                                        if($has_stream)
1028                                        {
1029                                                $send_stream_extra = <<__E;
1030        // Send stream after the command
1031        SendStream(rStream);
1032__E
1033                                        }
1034                                       
1035                                        print CPP <<__E;
1036std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra)
1037{
1038        // Send query
1039        Send(rQuery);
1040        $send_stream_extra
1041       
1042        // Wait for the reply
1043        std::auto_ptr<$message_base_class> preply = Receive();
1044       
1045        CheckReply("$cmd", *preply, $reply_id);
1046
1047        // Correct response, if no exception thrown by CheckReply
1048        return std::auto_ptr<$reply_class>(($reply_class *)preply.release());
1049}
1050__E
1051                                }
1052                                elsif($writing_local)
1053                                {
1054                                        if($has_stream)
1055                                        {
1056                                                $send_stream_extra = <<__E;
1057        // Send stream after the command
1058        SendStreamAfterCommand(&rStream);
1059__E
1060                                        }
1061
1062                                        print CPP <<__E;
1063std::auto_ptr<$reply_class> $server_or_client_class\::Query(const $request_class &rQuery$argextra)
1064{
1065        // Send query
1066        $send_stream_extra
1067        std::auto_ptr<$message_base_class> preply = rQuery.DoCommand(*this, mrContext);
1068       
1069        CheckReply("$cmd", *preply, $reply_id);
1070
1071        // Correct response, if no exception thrown by CheckReply
1072        return std::auto_ptr<$reply_class>(($reply_class *)preply.release());
1073}
1074__E
1075                                }
1076                        }
1077                }
1078        }
1079}
1080
1081print H <<__E;
1082#endif // $guardname
1083
1084__E
1085
1086# close files
1087close H;
1088close CPP;
1089
1090sub obj_is_type ($$)
1091{
1092        my ($c,$ty) = @_;
1093        for(@{$cmd_attributes{$c}})
1094        {
1095                return 1 if $_ =~ m/\A$ty/;
1096        }
1097       
1098        return 0;
1099}
1100
1101sub obj_get_type_params
1102{
1103        my ($c,$ty) = @_;
1104        for(@{$cmd_attributes{$c}})
1105        {
1106                return $1 if $_ =~ m/\A$ty\((.+?)\)\Z/;
1107        }
1108        die "Can't find attribute $ty\n"
1109}
1110
1111# returns (is basic type, typename)
1112sub translate_type
1113{
1114        my $ty = $_[0];
1115       
1116        if($ty =~ m/\Avector\<(.+?)\>\Z/)
1117        {
1118                my $v_type = $1;
1119                my (undef,$v_ty) = translate_type($v_type);
1120                return (0, 'std::vector<'.$v_ty.'>')
1121        }
1122        else
1123        {
1124                if(!exists $translate_type_info{$ty})
1125                {
1126                        die "Don't know about type name $ty\n";
1127                }
1128                return @{$translate_type_info{$ty}}
1129        }
1130}
1131
1132sub translate_type_to_arg_type
1133{
1134        my ($basic,$typename) = translate_type(@_);
1135        return $basic?$typename:'const '.$typename.'&'
1136}
1137
1138sub translate_type_to_member_type
1139{
1140        my ($basic,$typename) = translate_type(@_);
1141        return $typename
1142}
1143
1144sub make_log_strings_framework
1145{
1146        my ($cmd) = @_;
1147
1148        my @args;
1149
1150        for(my $x = 0; $x < $#{$cmd_contents{$cmd}}; $x+=2)
1151        {
1152                my ($ty,$nm) = (${$cmd_contents{$cmd}}[$x], ${$cmd_contents{$cmd}}[$x+1]);
1153               
1154                if(exists $log_display_types{$ty})
1155                {
1156                        # need to translate it
1157                        my ($format,$arg) = @{$log_display_types{$ty}};
1158                        $arg =~ s/VAR/m$nm/g;
1159
1160                        if ($format eq '"%s"')
1161                        {
1162                                $arg = "\"\\\"\" << $arg << \"\\\"\"";
1163                        }
1164                        elsif ($format =~ m'x$')
1165                        {
1166                                # my $width = 0;
1167                                # $ty =~ /^int(\d+)$/ and $width = $1 / 4;
1168                                $arg =  "($arg == 0 ? \"0x\" : \"\") " .
1169                                        "<< std::hex " .
1170                                        "<< std::showbase " .
1171                                        # "<< std::setw($width) " .
1172                                        # "<< std::setfill('0') " .
1173                                        # "<< std::internal " .
1174                                        "<< $arg " .
1175                                        "<< std::dec";
1176                        }
1177
1178                        push @args, $arg;
1179                }
1180                else
1181                {
1182                        # is opaque
1183                        push @args, '"OPAQUE"';
1184                }
1185        }
1186
1187        my $log_cmd = "Action << \" $cmd(\" ";
1188        foreach my $arg (@args)
1189        {
1190                $arg = "<< $arg ";
1191        }
1192        $log_cmd .= join('<< "," ',@args);
1193        $log_cmd .= '<< ")"';
1194        return $log_cmd;
1195}
1196
Note: See TracBrowser for help on using the repository browser.