summaryrefslogtreecommitdiff
path: root/qpid/cpp/rubygen/0-10/specification.rb
blob: d4ecfe98eddab3a5c66d432a15a2307764f083fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/env ruby
$: << ".."                      # Include .. in load path
require 'cppgen'

class Specification < CppGen
  def initialize(outdir, amqp)
    super(outdir, amqp)
    @ns="qpid::amqp_#{@amqp.version.bars}"
    @dir="qpid/amqp_#{@amqp.version.bars}"
  end

  # domains

  def domain_h(d)
    genl
    typename=d.name.typename
    if d.enum
      scope("enum #{typename} {", "};") { 
        genl d.enum.choices.map { |c|
          "#{c.name.constname} = #{c.value}" }.join(",\n")
      }
    elsif (d.type_ == "array") 
      genl "typedef Array<#{ArrayTypes[d.name].amqp2cpp}> #{typename};"
    else
      genl "typedef #{d.type_.amqp2cpp} #{typename};"
    end
  end

  # class constants
  
  def class_h(c)
    genl "const uint8_t CODE=#{c.code};"
    genl "extern const char* NAME;"
  end
  
  def class_cpp(c)
    genl "const char* NAME=\"#{c.fqname}\";"
  end

  # Used by structs, commands and controls.
  def action_struct_h(x, base, consts, &block)
    genl
    struct(x.classname, "public #{base}") {
      x.fields.each { |f| genl "#{f.type_.amqp2cpp} #{f.cppname};" }
      genl
      genl "static const char* NAME;"
      consts.each { |c| genl "static const uint8_t #{c.upcase}=#{x.send c or 0};"}
      genl "static const uint8_t CLASS_CODE=#{x.containing_class.nsname}::CODE;"
      ctor_decl(x.classname,[])
      ctor_decl(x.classname, x.parameters) unless x.fields.empty?
      function_decl("void accept", ["Visitor&"], "const")
      genl
      yield if block
    }
  end

  def action_struct_cpp(x)
    genl
    genl "const char* #{x.classname}::NAME=\"#{x.fqname}\";"
    genl
    ctor_defn(x.classname) {}
    ctor_defn(x.classname, x.parameters, x.initializers) {} if not x.fields.empty?
    function_defn("void #{x.classname}::accept", ["Visitor&"], "const") { 
      genl "// FIXME aconway 2008-02-27: todo"
    }
  end

  # structs

  def struct_h(s) action_struct_h(s, "Struct", ["size","pack","code"]); end
  def struct_cpp(s) action_struct_cpp(s) end

  # command and control
  
  def action_h(a)
    action_struct_h(a, a.base, ["code"]) {
      function_defn("template <class T> void invoke", ["T& target"]) {
        genl "target.#{a.funcname}(#{a.values.join(', ')});"
      }
      function_defn("template <class S> void serialize", ["S& s"]) { 
        gen "s"
        a.fields.each { |f| gen "(#{f.cppname})"}
        genl ";"
      } unless a.fields.empty?
    }
  end
  
  def action_cpp(a) action_struct_cpp(a); end

  # Types that must be generated early because they are used by other types.
  def pregenerate?(x) not @amqp.used_by[x.fqname].empty?;  end

  # Generate the log
  def gen_specification()
    h_file("#{@dir}/specification") {
      include "#{@dir}/built_in_types"
      include "#{@dir}/helpers"
      include "<boost/call_traits.hpp>"
      genl "using boost::call_traits;"
      namespace(@ns) {
        # Top level 
        @amqp.domains.each { |d|
          # segment-type and track are are built in
          domain_h d unless ["track","segment-type"].include?(d.name)
        }
        # Domains and structs that must be generated early because
        # they are used by other definitions:
        each_class_ns { |c|
          class_h c
          c.domains.each { |d| domain_h d if pregenerate? d }
          c.structs.each { |s| struct_h s if pregenerate? s }
        }
        # Now dependent domains/structs and actions
        each_class_ns { |c|
          c.domains.each { |d| domain_h d if not pregenerate? d }
          c.structs.each { |s| struct_h s if not pregenerate? s }
          c.actions.each { |a| action_h a }
        }
      }
    }

    cpp_file("#{@dir}/specification") { 
      include "#{@dir}/specification"
      namespace(@ns) { 
        each_class_ns { |c|
          class_cpp c
          c.actions.each { |a| action_cpp a}
          c.structs.each { |s| struct_cpp s }
        }
      }
    }
  end
  
  def gen_proxy()
    h_file("#{@dir}/ProxyTemplate.h") { 
      include "#{@dir}/specification"
      namespace(@ns) { 
        genl "template <class F, class R=typename F::result_type>"
        cpp_class("ProxyTemplate") {
          public
          genl "ProxyTemplate(F f=F()) : functor(f) {}"
          @amqp.classes.each { |c|
            c.actions.each { |a|
              genl
              function_defn("R #{a.funcname}", a.parameters) { 
                var=a.name.funcname
                args = a.arguments.empty? ? "" : "("+a.arguments.join(", ")+")"
                genl("#{a.fqclassname} #{var}#{args};")
                genl "return functor(#{var});"
              }
            }
          }
          private
          genl "F functor;"
        }
      }
    }
  end
  
  def generate
    gen_specification
    gen_proxy
  end
end

Specification.new($outdir, $amqp).generate();