Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 209 lines (189 sloc) 8.892 kb
8603286 Initial repo set-up
Elias Karakoulakis authored
1 # a Thrift server generator for OpenZWave
2 # transform a server skeleton file into a fully operational server
3 # a.k.a. "fills in the blanks for you"
4 #
5 # (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>
6 #
7 require 'rubygems'
8 require 'rbgccxml'
9
10 OverloadedRE = /([^_]*)(?:_(.*))/
11
12 MANAGER_INCLUDES = [
13 "gen_cpp",
14 "/usr/local/include/thrift/",
15 "/home/ekarak/ozw/open-zwave-read-only/cpp/tinyxml",
16 "/home/ekarak/ozw/open-zwave-read-only/cpp/src",
17 "/home/ekarak/ozw/open-zwave-read-only/cpp/src/value_classes",
18 "/home/ekarak/ozw/open-zwave-read-only/cpp/src/command_classes",
19 "/home/ekarak/ozw/open-zwave-read-only/cpp/src/platform",
20 ]
21
22 #
23 # must load all source files in a single batch (RbGCCXML gets confused otherwise...)
24 #
25 files = [
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
26 "/home/ekarak/ozw/Thrift4OZW/gen-cpp/RemoteManager_server.skeleton.cpp",
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
27 "/home/ekarak/ozw/open-zwave-read-only/cpp/src/Manager.h"
8603286 Initial repo set-up
Elias Karakoulakis authored
28 ]
29 puts "Parsing:" + files.join("\n\t")
30 RootNode = RbGCCXML.parse(files, :includes => MANAGER_INCLUDES)
31
32 # read skeleton file in memory as an array
33 output = File.open("gen-cpp/RemoteManager_server.skeleton.cpp").readlines
34
35
36 # fix the constructor
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
37 #lineno = RootNode.classes("RemoteManagerHandler").constructors[1]['line'].to_i
8603286 Initial repo set-up
Elias Karakoulakis authored
38 #~ output[lineno] = Constructor
39 # add our extra hidden sauce
40 #lineno = foo.classes("RemoteManagerHandler").constructors[1]['endline'].to_i
41 #output[lineno] = Converter
42
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
43 a = RootNode.classes("RemoteManagerHandler").methods.find(:access => :public)
44 b = RootNode.namespaces("OpenZWave").classes("Manager").methods.find(:access => :public)
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
45 puts "RemoteManagerHandler: #{a.entries.size} public methods, OpenZWave::Manager: #{b.entries.size} public methods"
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
46
8603286 Initial repo set-up
Elias Karakoulakis authored
47 RootNode.classes("RemoteManagerHandler").methods.each { |meth|
48 # find line number, insert critical section enter code
49 lineno = meth['line'].to_i
50 #
51 target_method = nil
52 target_method_name = nil
53 disambiguation_hint = nil
54
55 # skeleton function's name has underscore => Overloaded. Needs disambiguation.
56 if md = OverloadedRE.match(meth.name) then
57 target_method_name = md[1]
58 disambiguation_hint = md[2]
59 else
60 target_method_name = meth.name
61 end
62
63 #
64 # SEARCH FOR MATCHING FUNCTION IN OPENZWAVE::MANAGER
65 #
66 search_result = RootNode.namespaces("OpenZWave").classes("Manager").methods.find(:name => target_method_name, :access => :public)
67 #puts "search result: #{search_result.class.name}"
68 case search_result
69 when RbGCCXML::QueryResult then
24ac133 skip creation of extra functions not found in target library
Elias Karakoulakis authored
70 next if search_result.empty? # skip unknown functions (needed for "void SendAllValues()"
8603286 Initial repo set-up
Elias Karakoulakis authored
71 raise "#{target_method_name}(): no disambiguation hint given!!!" unless disambiguation_hint
72 #puts " ...Overloaded method: #{meth.name}"
73 search_result.each { |node|
74 # last argument's type must match disambiguation_hint
75 target_method = node if node.arguments[-1].cpp_type.to_cpp =~ Regexp.new(disambiguation_hint, Regexp::IGNORECASE)
76 # FIXME:: ListString => list<string>
77 }
78 when RbGCCXML::Method then
79 #puts " ...exact match for #{meth.name}"
80 target_method = search_result
81 end
82
83 raise "Unable to resolve target method! (#{meth.name})" unless target_method
84
85 #
86 # TIME TO BOOGEY
87 #
88
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
89 puts "CREATING MAPPING for (#{meth.return_type.to_cpp}) #{meth.name}" if $DEBUG
8603286 Initial repo set-up
Elias Karakoulakis authored
90
91 #Thrift transforms methods with complex return types (string, vector<...>, user-defined structs etc)
92 # example 1:
93 # (C++) string GetLibraryVersion( uint32 const _homeId );
94 # (thrift) string GetLibraryVersion( 1:i32 _homeId );
95 # (skeleton) void GetLibraryVersion(std::string& _return, const int32_t _homeId)
96 #
97 # example 2:
98 # (C++) uint32 GetNodeNeighbors( uint32 const _homeId, uint8 const _nodeId, uint8** _nodeNeighbors );
99 # (thrift) UInt32_NeighborMap GetNodeNeighbors( 1:i32 _homeId, 2:byte _nodeId);
100 # (skeleton) void GetNodeNeighbors(UInt32_ListByte& _return, const int32_t _homeId, const int8_t _nodeId)
101 # ozw_types.h: class UInt32_ListByte {
102 # int32_t retval;
103 # std::vector<int8_t> arg; *** notice manual copying needed from C-style pointer to pointers of uint8's (not very C++ish)
104 # }
105 #
106 # example 3:
107 # (C++) bool GetValueListItems( ValueID const& _id, vector<string>* o_value );
108 # (thrift) Bool_ListString GetValueListItems( 1:RemoteValueID _id );
109 # (skeleton) void GetValueListItems(Bool_ListString& _return, const RemoteValueID _id)
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
110 # where the Thrift definition for Bool_ListString is:
111 # (ozw_types.h):class Bool_ListString {
8603286 Initial repo set-up
Elias Karakoulakis authored
112 # bool retval;
113 # std::vector<std::string> arg;
114 # }
115 #
116
a41ce7d 1) fix Makefile to produce binaries in their directories
Elias Karakoulakis authored
117 #
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
118 # STEP 1. Map arguments from target (OpenZWave::Manager) to source (skeleton server)
a41ce7d 1) fix Makefile to produce binaries in their directories
Elias Karakoulakis authored
119 #
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
120 argmap = {}
121 # KEY: target argument node
122 # VALUE: hash with
123 # :descriptor => source argument DESCRIPTOR STRING (eg "_return._className")
124 # :node => the actual source argument node (Argument or Field)
125 target_method.arguments.each {|a|
126 # 1) match directly by name
127 if (arg = meth.arguments.find(:name => a.name )).is_a?RbGCCXML::Argument then
128 argmap[a] = {}
129 argmap[a][:descriptor] = arg.name
130 argmap[a][:node] = arg
131 # 2) else, match as a member of Thrift's special "_return" argument (class struct)
132 elsif (_ret = meth.arguments.find(:name => "_return" )) and (_ret.cpp_type.base_type.is_a?RbGCCXML::Class) and
133 (arg = _ret.cpp_type.base_type.variables.find(:name => a.name)).is_a?RbGCCXML::Field then
134 argmap[a] = {}
135 argmap[a][:descriptor] = "_return.#{a.name}"
136 argmap[a][:node] = arg
137 else
138 raise ("Reverse argument mapping: couldn't resolve argument '#{a.name}' in method '#{target_method.name}'!!!")
139 end
140 }
8603286 Initial repo set-up
Elias Karakoulakis authored
141
142 #
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
143 # STEP 2. Resolve the function call's return clause
8603286 Initial repo set-up
Elias Karakoulakis authored
144 #
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
145 function_return_clause = ''
146 if (_return = meth.arguments.find(:name => '_return')).is_a?RbGCCXML::Argument then
147 puts "Thrift special _return argument detected!" if $DEBUG
148 if (_return.cpp_type.base_type.is_a?RbGCCXML::Class) and
149 (retval = _return.cpp_type.base_type.variables.find(:name => 'retval')) and
150 (retval.is_a?RbGCCXML::Field) then
151 function_return_clause = "_return.retval = "
152 else
6a750a2 more documentation added
Elias Karakoulakis authored
153 unless target_method.return_type.name == "void" then
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
154 function_return_clause = "_return = "
6a750a2 more documentation added
Elias Karakoulakis authored
155 end
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
156 end
157 end
158
8603286 Initial repo set-up
Elias Karakoulakis authored
159 #
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
160 # STEP 3. Prepare argument array (ordered by target_method's argument order)
161 #
162 arg_array = []
163 target_method.arguments.each { |tgt_arg|
164 if (hsh = argmap[tgt_arg]) then
165 src_arg = hsh[:node]
166 descriptor = hsh[:descriptor]
167 #puts " src=#{descriptor}\ttgt=#{tgt_arg.qualified_name}"
a41ce7d 1) fix Makefile to produce binaries in their directories
Elias Karakoulakis authored
168 ampersand = (tgt_arg.cpp_type.to_cpp.include?('*') ? '&' : '')
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
169 case src_arg.to_cpp
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
170 when /RemoteValueID/
171 arg_array << "#{descriptor}.toValueID()"
8603286 Initial repo set-up
Elias Karakoulakis authored
172 else
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
173 arg_array << "(#{tgt_arg.cpp_type.to_cpp}) #{ampersand}#{descriptor}"
174 size_src = src_arg.cpp_type.base_type['size'].to_i
175 size_tgt = tgt_arg.cpp_type.base_type['size'].to_i
176 # sanity check
177 puts "WARNING!!! method '#{meth.name}': Argument '#{descriptor}' size mismatch (src=#{size_src} tgt=#{size_tgt}) - CHECK GENERATED CODE!" unless size_src == size_tgt
8603286 Initial repo set-up
Elias Karakoulakis authored
178 end
179 end
180 }
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
181
6a750a2 more documentation added
Elias Karakoulakis authored
182 # Get me the manager, and lock the criticalsection
183 output[lineno] = "\tManager* mgr = Manager::Get();\n\tg_criticalSection.lock();\n"
a41ce7d 1) fix Makefile to produce binaries in their directories
Elias Karakoulakis authored
184 fcall = "#{function_return_clause} mgr->#{target_method.name}(#{arg_array.compact.join(', ')})"
8603286 Initial repo set-up
Elias Karakoulakis authored
185 case meth.return_type.name
186 when "void"
187 output[lineno+1] = "\t#{fcall};\n"
188 else
189 output[lineno+1] = "\t#{meth.return_type.to_cpp} function_result = #{fcall};\n"
190 end
191 # unlock the critical section
192 output[lineno+1] << "\tg_criticalSection.unlock();\n"
193 # output return statement (unless rettype == void)
194 unless meth.return_type.name == "void"
195 output[lineno+1] << "\treturn(function_result);\n"
196 end
197
198 }
199
200 output[0] = "// Automatically generated OpenZWave::Manager_server wrapper\n"
201 output[1] = "// (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>\n"
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
202 # comment out main()
203 ((RootNode.functions("main")["line"].to_i-1)..(output.size)).each{ |i|
204 output[i] = "// #{output[i]}"
205 }
8603286 Initial repo set-up
Elias Karakoulakis authored
206 # write out the generated file
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
207 puts "Writing generated server...."
8603286 Initial repo set-up
Elias Karakoulakis authored
208 File.new("gen-cpp/RemoteManager_server.cpp", File::CREAT|File::TRUNC|File::RDWR, 0644) << output.join
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
209 puts "Done!"
Something went wrong with that request. Please try again.