Class: Oinky::Model::Cpp

Inherits:
Object
  • Object
show all
Defined in:
lib/oinky/cpp_emitter.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema) ⇒ Cpp

Returns a new instance of Cpp.



11
12
13
# File 'lib/oinky/cpp_emitter.rb', line 11

def initialize(schema)
  @schema = Oinky::Model.normalize_schema(schema)
end

Class Method Details

.instance_type_expression(type) ⇒ Object



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
# File 'lib/oinky/cpp_emitter.rb', line 56

def self.instance_type_expression(type)
  case type
  when :variant
    return 'Oinky::variant_cv_t'
  when :bit
    return 'bool'
  when :datetime
    return 'Oinky::datetime_t'
  when :string 
    return 'Oinky::db_string'
    #int
  when :int8 
    return 'int8_t'
  when :int16
    return 'int16_t'
  when :int32
    return 'int32_t'
  when :int64
    return 'int64_t'
    #uint
  when :uint8 
    return 'uint8_t'
  when :uint16
    return 'uint16_t'
  when :uint32
    return 'uint32_t'
  when :uint64
    return 'uint64_t'
    #float
  when :float32
    return 'float32_t'
  when :float64
    return 'float64_t'
  else
    # This must be a derived type.  It should support this method, to 
    # give us an expression we can use in the target language.
    return type.instance_type(self)
  end
end

.type_code(h) ⇒ Object



143
144
145
146
147
# File 'lib/oinky/cpp_emitter.rb', line 143

def self.type_code(h)
  h = h[:type].to_s
  h[0] = h[0].upcase
  return 'column_types::' + h
end

.value_from_variant(src, type) ⇒ Object



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
# File 'lib/oinky/cpp_emitter.rb', line 99

def self.value_from_variant(src,type)
  case type
  when :variant
    return src
  when :bit
    return "#{src}.bit_value()"
  when :datetime
    return "#{src}.dt_value()"
  when :string 
    return "#{src}.string_value()"
    #int
  when :int8 
    return "(int8_t) #{src}.int_value()"
  when :int16
    return "(int16_t) #{src}.int_value()"
  when :int32
    return "(int32_t) #{src}.int_value()"
  when :int64
    return "#{src}.int_value()"
    #uint
  when :uint8 
    return "(uint8_t) #{src}.uint_value()"
  when :uint16
    return "(uint16_t) #{src}.uint_value()"
  when :uint32
    return "(uint32_t) #{src}.uint_value()"
  when :uint64
    return "#{src}.uint_value()"
    #float
  when :float32
    return "#{src}.f32_value()"
  when :float64
    return "#{src}.f64_value()"

  else
    # This must be a derived type.  It should support this method, to 
    # give us an expression we can use in the target language.
    return type.value_from_variant(src, self)
  end
end

Instance Method Details

#accessor_from_tablename(str) ⇒ Object



22
# File 'lib/oinky/cpp_emitter.rb', line 22

def accessor_from_tablename(str) ; cppify(str) ; end

#accessor_name(h) ⇒ Object



38
39
40
# File 'lib/oinky/cpp_emitter.rb', line 38

def accessor_name(h)
  return cppify(h[:accessor])
end

#cppify(str) ⇒ Object



15
16
17
# File 'lib/oinky/cpp_emitter.rb', line 15

def cppify(str)
  str.gsub(/($[0-9])|[^a-z0-9A-Z_]/,'_')
end

#default_value_expression(coldef) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/oinky/cpp_emitter.rb', line 29

def default_value_expression(coldef)
  d = coldef[:default]
  return "#{instance_type_expression(coldef[:type])}()" unless d
  return d.inspect if d.is_a? String
  return d.inspect if d.is_a? Fixnum
  return make_datetime_expression(d) if d.is_a? DateTime
  return d[:cpp]
end

#emitObject

Generate a string, containing the C++ code for the model classes.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/oinky/cpp_emitter.rb', line 164

def emit
  p = Oinky::Detail::Builder.new
  ns = "namespace #{schemaname_to_ns(@schema[:name])}"
  p.next("#{ns} {", "} //#{ns}") {
    p << "using namespace Oinky;"
    p << "using Oinky::Model::TableBase;"
    p << "using Oinky::Model::SchemaBase;"
    p << "using Oinky::db_t;"
    p << ''
    p << "class Schema;"
    tables = @schema[:tables]
    tables.each{|tn, t|
#            struct_hdr = "struct #{tablename_to_groupname(t[:name])}"
#            p << struct_hdr
#            p.next("{", "}; //#{struct_hdr}") {
      tbl_classname = make_table_classname(tn)
      p << ''
      p << "class #{tbl_classname} : public TableBase"
      p.next("{", "}; //class #{tbl_classname}") {
        p << "friend class Schema;"
        p.write("public:", -1)
        p.next("struct Row {", "}; //struct Row") {
          maxw = t[:columns].max_by{|k,col| instance_type_expression(col[:type]).length}
          maxwlen = maxw ? instance_type_expression(maxw[1][:type]).length : 0
          t[:columns].each{|cn,col|
            if col[:description]
              col[:description].split("\n").each{|l|
                p << ("//  " + l)
              }
            end
            # The column name is the default value of the accessor name,
            # but it can be overridden
            p << Kernel.sprintf("%-#{maxwlen}s %s;",
                                instance_type_expression(col[:type]),
                                accessor_name(col))
          }
        }
        p << ''
        p.write("private:", -1)                
        p << "typedef db_t::column_selector_t column_selector_t;"
        p << 'column_selector_t row_selector;'
        # index handles
        indices(t).each {|iname, idef|
          p << "const db_t::index_handle #{accessor_name(idef)};"
        }
        p << ''
        # update_schema
        p << 'static void up_schema(db_t::table_handle th)'
        p.next('{','}') {
          # Add columns
          t[:columns].each{|cn, col|
            p << "TableBase::create_column_if(th, #{cn.inspect}, #{type_code(col)}, #{default_value_expression(col)});"
          }
          # Add indices
          indices(t).each_with_index {|idef,idx|
            iname, idef = idef
            # Column definitions
            names = "idx_column_defs_#{idx}"
            columns = idef[:columns]
            p.next("static index_column_def #{names}[] = {", "};") {
              columns.each_with_index{|col, i|
                colname = index_column_name(col)
                ascending = index_column_ascending(col) ? 'true' : 'false'
                sep = (i == 0 ? '' : ',')
                p << "#{sep} { db_string(\"#{colname}\"), #{ascending} }"
              }
            }
            unique = idef[:unique] ? 'true' : 'false'
            p << "TableBase::create_index_if(th, #{iname.inspect}, #{unique}, #{names}, #{names}+#{columns.size} );"
          }
        }
        p << ''
        p << "template<typename CURSOR>"
        p.next("void priv_row_from_cursor(Row &row, const CURSOR &crs) {", "}") {
          # Don't presume that enumeration with index is a consistent
          # ordering
          indexed_cols = []
          p.next("static const char *column_names[] = {","};") {
            t[:columns].each{|cn, col|
              p << "#{cn.inspect},"
              indexed_cols << col
            }
            p << 'NULL'
          }
          p.next("TableBase::check_init_row_accessor(", "") {
            p << "row_selector, "
            p << "column_names,"
            p << "column_names + #{t[:columns].count}"
            p << ");"
          }
          p.next("variant_cv_t *vals = (variant_cv_t *) ","") {
            p << "alloca(sizeof(variant_cv_t) * #{t[:columns].count});"
          }
          p << "crs.select(row_selector).copy_to(vals, #{t[:columns].count});"
          p << "//  Select values into the target struct"
          indexed_cols.each_with_index{|col,i|
            val = value_from_variant("vals[#{i}]", col[:type])
            p << "row.#{accessor_name(col)} = #{val};"
          }
        } # priv_row_from_cursor              
        p.write("public:", -1)
        # Constructor - initialize index handles.
        p.next("#{tbl_classname}(db_t::table_handle _th, db_t &db) : TableBase(_th, db)","{}") {
          indices(t).each {|iname, idef|
            p << ",#{accessor_name(idef)}(TableBase::get_index_handle(_th, \"#{iname}\"))"
          }
        }
        # Row accessors
        p << ''
        p.next("void row_from_cursor(Row &row, const db_t::index_cursor_handle &crs) {", "}") {
          p << "priv_row_from_cursor(row, crs);"
        }
        p.next("void row_from_cursor(Row &row, const db_t::table_cursor_handle &crs) {", "}") {
          p << "priv_row_from_cursor(row, crs);"
        }
      } # class "#{tbl_classname}"
    } # tables.each {}

    p << ''
    p << "class Schema : public SchemaBase"
    p.next("{","}; //class Schema") {
      p.next("static void up_schema(db_t &db) {","}") {
        tables.each{|tn, t|
          tbl_classname = make_table_classname(tn)
          p << "#{tbl_classname}::up_schema(SchemaBase::get_table_handle(db, #{tn.inspect}));";
        }
      }
      p << ''
      p.write("public:", -1)
      # Table accessors
      tables.each {|tn, tdef|
        tbl_classname = make_table_classname(tn)
        p << "const #{tbl_classname} #{accessor_name(tdef)};"
      }
      p << ''
      p.next("Schema(db_t &db) : SchemaBase(db, &up_schema)","{}") {
        tables.each {|tn, tdef|
          p << ",#{accessor_name(tdef)}(SchemaBase::get_table_handle(db, #{tn.inspect}), db)"
        }              
      }
    }          
  } # namespace <schemaname>
  return p.format
end

#handle_from_tablename(str) ⇒ Object



21
# File 'lib/oinky/cpp_emitter.rb', line 21

def handle_from_tablename(str) ; "#{cppify(str)}_handle" ; end

#index_column_ascending(col) ⇒ Object



47
48
49
50
51
# File 'lib/oinky/cpp_emitter.rb', line 47

def index_column_ascending(col)
  return col[:ascending] if col.is_a? Hash
  # ascending is the default
  return true
end

#index_column_name(col) ⇒ Object



42
43
44
45
46
# File 'lib/oinky/cpp_emitter.rb', line 42

def index_column_name(col)
  return col if col.is_a? String
  return col.to_s if col.is_a? Symbol
  return index_column_name(col[:name])
end

#indices(t) ⇒ Object



159
160
161
# File 'lib/oinky/cpp_emitter.rb', line 159

def indices(t)
  t[:indices] || t[:indexes] || []
end

#instance_type_expression(type) ⇒ Object



53
54
55
# File 'lib/oinky/cpp_emitter.rb', line 53

def instance_type_expression(type)
  self.class.instance_type_expression(type)
end

#make_datetime_expression(d) ⇒ Object



24
25
26
27
# File 'lib/oinky/cpp_emitter.rb', line 24

def make_datetime_expression(d)
  "Oinky::datetime_t::compose(#{d.year-1900},#{d.month - 1},#{d.day - 1}," +
    "#{d.hour},#{d.minute},#{d.second},#{(d.second_fraction * 1000000).to_i})"
end

#make_index_handle_name(idef) ⇒ Object



152
153
154
# File 'lib/oinky/cpp_emitter.rb', line 152

def make_index_handle_name(idef)
  "_idx_handle__#{make_index_name(idef)}"
end

#make_index_name(idef) ⇒ Object



149
150
151
# File 'lib/oinky/cpp_emitter.rb', line 149

def make_index_name(idef)
  cppify(idef[:name])
end

#make_table_classname(tn) ⇒ Object



155
156
157
# File 'lib/oinky/cpp_emitter.rb', line 155

def make_table_classname(tn)
  "CTable_#{cppify(tn)}"
end

#schemaname_to_ns(str) ⇒ Object



19
# File 'lib/oinky/cpp_emitter.rb', line 19

def schemaname_to_ns(str) ; cppify(str) ; end

#tablename_to_groupname(str) ⇒ Object



20
# File 'lib/oinky/cpp_emitter.rb', line 20

def tablename_to_groupname(str) ; cppify(str) ; end

#type_code(h) ⇒ Object



140
141
142
# File 'lib/oinky/cpp_emitter.rb', line 140

def type_code(h)
  self.class.type_code(h)
end

#value_from_variant(src, type) ⇒ Object



96
97
98
# File 'lib/oinky/cpp_emitter.rb', line 96

def value_from_variant(src,type)
  self.class.value_from_variant(src,type)
end