pyloxi: add convenience code to generate most OFTypeData objects
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index 9d249d4..2854e76 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -29,10 +29,10 @@
 
 OFTypeData = namedtuple("OFTypeData", ["init", "pack", "unpack"])
 
+# Map from LOXI type name to an object with templates for init, pack, and unpack
+# Most types are defined using the convenience code below. This dict should
+# only be used directly for special cases such as primitive types.
 type_data = {
-
-    ## Primitives
-
     'char': OFTypeData(
         init='0',
         pack='struct.pack("!B", %s)',
@@ -93,177 +93,108 @@
         pack='%s',
         unpack='str(%s.read_all())'),
 
-    ## Strings
-
-    'of_port_name_t': OFTypeData(
-        init="''",
-        pack='struct.pack("!16s", %s)',
-        unpack='%s.read("!16s")[0].rstrip("\\x00")'),
-
-    'of_table_name_t': OFTypeData(
-        init="''",
-        pack='struct.pack("!32s", %s)',
-        unpack='%s.read("!32s")[0].rstrip("\\x00")'),
-
-    'of_serial_num_t': OFTypeData(
-        init="''",
-        pack='struct.pack("!32s", %s)',
-        unpack='%s.read("!32s")[0].rstrip("\\x00")'),
-
-    'of_desc_str_t': OFTypeData(
-        init="''",
-        pack='struct.pack("!256s", %s)',
-        unpack='%s.read("!256s")[0].rstrip("\\x00")'),
-
-    ## Embedded structs
-    # TODO add helper to generate these
-
-    'of_match_t': OFTypeData(
-        init='common.match()',
-        pack='%s.pack()',
-        unpack='common.match.unpack(%s)'),
-
-    'of_port_desc_t': OFTypeData(
-        init='common.port_desc()',
-        pack='%s.pack()',
-        unpack='common.port_desc.unpack(%s)'),
-
-    'of_meter_features_t': OFTypeData(
-        init='common.meter_features()',
-        pack='%s.pack()',
-        unpack='common.meter_features.unpack(%s)'),
-
-    'of_bsn_vport_q_in_q_t': OFTypeData(
-        init='common.bsn_vport_q_in_q()',
-        pack='%s.pack()',
-        unpack='common.bsn_vport_q_in_q.unpack(%s)'),
-
-    ## TLV Lists
-
-    'list(of_action_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='action.unpack_list(%s)'),
-
-    'list(of_table_features_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack=None),
-
-    'list(of_flow_stats_entry_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_flow_stats_entry(%s)'),
-
-    'list(of_bucket_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_bucket(%s)'),
-
-    'list(of_packet_queue_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_packet_queue(%s)'),
-
-    'list(of_meter_stats_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_meter_stats(%s)'),
-
     # HACK need the match_v3 length field
     'list(of_oxm_t)': OFTypeData(
         init='[]',
         pack='util.pack_list(%s)',
         unpack='oxm.unpack_list(%s.slice(_length-4))'),
 
-    'list(of_group_stats_entry_t)': OFTypeData(
+    # TODO implement unpack
+    'list(of_table_features_t)': OFTypeData(
         init='[]',
         pack='util.pack_list(%s)',
-        unpack='common.unpack_list_group_stats_entry(%s)'),
+        unpack=None),
 
-    'list(of_hello_elem_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_hello_elem(%s)'),
-
+    # TODO implement unpack
     'list(of_action_id_t)': OFTypeData(
         init='[]',
         pack='util.pack_list(%s)',
         unpack=None),
 
-    'list(of_group_desc_stats_entry_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_group_desc_stats_entry(%s)'),
-
-    'list(of_queue_prop_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='common.unpack_list_queue_prop(%s)'),
-
-    'list(of_instruction_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='instruction.unpack_list(%s)'),
-
+    # TODO implement unpack
     'list(of_table_feature_prop_t)': OFTypeData(
         init='[]',
         pack='util.pack_list(%s)',
         unpack=None),
-
-    'list(of_meter_band_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='meter_band.unpack_list(%s)'),
-
-    ## Lists with fixed-length elements
-
-    'list(of_bucket_counter_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.bucket_counter.unpack)'),
-
-    'list(of_uint32_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.uint32.unpack)'),
-
-    'list(of_queue_stats_entry_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.queue_stats_entry.unpack)'),
-
-    'list(of_table_stats_entry_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.table_stats_entry.unpack)'),
-
-    'list(of_uint8_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.uint8.unpack)'),
-
-    'list(of_port_stats_entry_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.port_stats_entry.unpack)'),
-
-    'list(of_bsn_interface_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.bsn_interface.unpack)'),
-
-    'list(of_meter_band_stats_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.meter_band_stats.unpack)'),
-
-    'list(of_port_desc_t)': OFTypeData(
-        init='[]',
-        pack='util.pack_list(%s)',
-        unpack='loxi.generic_util.unpack_list(%s, common.port_desc.unpack)'),
 }
 
+## Fixed length strings
+
+# Map from class name to length
+fixed_length_strings = {
+    'of_port_name_t': 16,
+    'of_table_name_t': 32,
+    'of_serial_num_t': 32,
+    'of_desc_str_t': 256,
+}
+
+for (cls, length) in fixed_length_strings.items():
+    type_data[cls] = OFTypeData(
+        init='""',
+        pack='struct.pack("!%ds", %%s)' % length,
+        unpack='%%s.read("!%ds")[0].rstrip("\\x00")' % length)
+
+## Embedded structs
+
+# Map from class name to Python class name
+embedded_structs = {
+    'of_match_t': 'common.match',
+    'of_port_desc_t': 'common.port_desc',
+    'of_meter_features_t': 'common.meter_features',
+    'of_bsn_vport_q_in_q_t': 'common.bsn_vport_q_in_q',
+}
+
+for (cls, pyclass) in embedded_structs.items():
+    type_data[cls] = OFTypeData(
+        init='%s()' % pyclass,
+        pack='%s.pack()',
+        unpack='%s.unpack(%%s)' % pyclass)
+
+## Variable element length lists
+
+# Map from list class name to list deserializer
+variable_elem_len_lists = {
+    'list(of_action_t)': 'action.unpack_list',
+    'list(of_bucket_t)': 'common.unpack_list_bucket',
+    'list(of_flow_stats_entry_t)': 'common.unpack_list_flow_stats_entry',
+    'list(of_group_desc_stats_entry_t)': 'common.unpack_list_group_desc_stats_entry',
+    'list(of_group_stats_entry_t)': 'common.unpack_list_group_stats_entry',
+    'list(of_hello_elem_t)': 'common.unpack_list_hello_elem',
+    'list(of_instruction_t)': 'instruction.unpack_list',
+    'list(of_meter_band_t)': 'meter_band.unpack_list',
+    'list(of_meter_stats_t)': 'common.unpack_list_meter_stats',
+    'list(of_packet_queue_t)': 'common.unpack_list_packet_queue',
+    'list(of_queue_prop_t)': 'common.unpack_list_queue_prop',
+}
+
+for (cls, deserializer) in variable_elem_len_lists.items():
+    type_data[cls] = OFTypeData(
+        init='[]',
+        pack='util.pack_list(%s)',
+        unpack='%s(%%s)' % deserializer)
+
+## Fixed element length lists
+
+# Map from list class name to list element deserializer
+fixed_elem_len_lists = {
+    'list(of_bsn_interface_t)': 'common.bsn_interface.unpack',
+    'list(of_bucket_counter_t)': 'common.bucket_counter.unpack',
+    'list(of_meter_band_stats_t)': 'common.meter_band_stats.unpack',
+    'list(of_port_desc_t)': 'common.port_desc.unpack',
+    'list(of_port_stats_entry_t)': 'common.port_stats_entry.unpack',
+    'list(of_queue_stats_entry_t)': 'common.queue_stats_entry.unpack',
+    'list(of_table_stats_entry_t)': 'common.table_stats_entry.unpack',
+    'list(of_uint32_t)': 'common.uint32.unpack',
+    'list(of_uint8_t)': 'common.uint8.unpack',
+}
+
+for (cls, element_deserializer) in fixed_elem_len_lists.items():
+    type_data[cls] = OFTypeData(
+        init='[]',
+        pack='util.pack_list(%s)',
+        unpack='loxi.generic_util.unpack_list(%%s, %s)' % element_deserializer)
+
+
 class OFType(object):
     """
     Encapsulates knowledge about the OpenFlow type system.