1# Copyright 2016 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS-IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14import itertools
15import networkx as nx
16
17def generate_files(injection_graph, use_new_delete, use_interfaces, generate_runtime_bench_code):
18    file_content_by_name = dict()
19
20    for node_id in injection_graph.nodes_iter():
21        deps = injection_graph.successors(node_id)
22        if use_interfaces:
23            file_content_by_name['class%s_interface.h' % node_id] = _generate_class_interface_header(node_id)
24            file_content_by_name['class%s.h' % node_id] = _generate_class_header_with_interfaces(node_id, deps)
25            file_content_by_name['class%s.cpp' % node_id] = _generate_class_cpp_file_with_interfaces(node_id, deps)
26        else:
27            file_content_by_name['class%s.h' % node_id] = _generate_class_header_without_interfaces(node_id, deps)
28            file_content_by_name['class%s.cpp' % node_id] = _generate_class_cpp_file_without_interfaces(node_id, deps)
29
30    file_content_by_name['main.cpp'] = _generate_main(injection_graph, use_interfaces, use_new_delete, generate_runtime_bench_code)
31
32    return file_content_by_name
33
34def _generate_class_interface_header(class_index):
35    template = """
36#ifndef CLASS{class_index}_INTERFACE_H
37#define CLASS{class_index}_INTERFACE_H
38
39// Example include that the code might use
40#include <vector>
41
42struct Interface{class_index} {{
43  virtual void foo() = 0;
44  virtual ~Interface{class_index}();
45}};
46
47#endif // CLASS{class_index}_INTERFACE_H
48"""
49    return template.format(**locals())
50
51def _generate_class_header_with_interfaces(class_index, deps):
52    include_directives = ''.join('#include "class%s_interface.h"\n' % index
53                                 for index in itertools.chain(deps, (class_index,)))
54    fields = ''.join('Interface%s& x%s;\n' % (index, index)
55                     for index in deps)
56    constructor_params = ', '.join('Interface%s& x%s' % (index, index)
57                                   for index in deps)
58
59    template = """
60#ifndef CLASS{class_index}_H
61#define CLASS{class_index}_H
62
63{include_directives}
64
65struct Class{class_index} : public Interface{class_index} {{
66  {fields}
67  Class{class_index}({constructor_params});
68
69  virtual void foo() override;
70
71  virtual ~Class{class_index}();
72}};
73
74#endif // CLASS{class_index}_H
75"""
76    return template.format(**locals())
77
78def _generate_class_header_without_interfaces(class_index, deps):
79    include_directives = ''.join('#include "class%s.h"\n' % index
80                                 for index in deps)
81    fields = ''.join('Class%s& x%s;\n' % (index, index)
82                     for index in deps)
83    constructor_params = ', '.join('Class%s& x%s' % (index, index)
84                                   for index in deps)
85
86    template = """
87#ifndef CLASS{class_index}_H
88#define CLASS{class_index}_H
89
90// Example include that the code might use
91#include <vector>
92
93{include_directives}
94
95struct Class{class_index} {{
96  {fields}
97  Class{class_index}({constructor_params});
98}};
99
100#endif // CLASS{class_index}_H
101"""
102    return template.format(**locals())
103
104def _generate_class_cpp_file_with_interfaces(class_index, deps):
105    constructor_params = ', '.join('Interface%s& x%s' % (index, index)
106                                   for index in deps)
107    field_initializers = ', '.join('x%s(x%s)' % (index, index)
108                                   for index in deps)
109    if field_initializers:
110        field_initializers = ': ' + field_initializers
111
112    template = """
113#include "class{class_index}.h"
114
115Interface{class_index}::~Interface{class_index}() {{
116}}
117
118Class{class_index}::Class{class_index}({constructor_params})
119  {field_initializers} {{
120}}
121
122void Class{class_index}::foo() {{
123}}
124
125Class{class_index}::~Class{class_index}() {{
126}}
127"""
128    return template.format(**locals())
129
130def _generate_class_cpp_file_without_interfaces(class_index, deps):
131    constructor_params = ', '.join('Class%s& x%s' % (index, index)
132                                   for index in deps)
133    field_initializers = ', '.join('x%s(x%s)' % (index, index)
134                                   for index in deps)
135    if field_initializers:
136        field_initializers = ': ' + field_initializers
137
138    template = """
139#include "class{class_index}.h"
140
141Class{class_index}::Class{class_index}({constructor_params})
142  {field_initializers} {{
143}}
144"""
145    return template.format(**locals())
146
147
148def _generate_main(injection_graph, use_interfaces, use_new_delete, generate_runtime_bench_code):
149    [toplevel_class_index] = [node_id
150                              for node_id in injection_graph.nodes_iter()
151                                if not injection_graph.predecessors(node_id)]
152
153    if use_interfaces:
154        include_directives = ''.join('#include "class%s.h"\n' % index
155                                     for index in injection_graph.nodes_iter())
156    else:
157        include_directives = '#include "class%s.h"\n' % toplevel_class_index
158
159    if use_new_delete:
160        instance_creations = ''.join('std::unique_ptr<Class%s> x%s(new Class%s(%s));\n' % (class_index,
161                                                                                           class_index,
162                                                                                           class_index,
163                                                                                           ', '.join('*x%s' % dep_index
164                                                                                                     for dep_index in injection_graph.successors(class_index)))
165                                     for class_index in reversed(list(nx.topological_sort(injection_graph))))
166    else:
167        instance_creations = ''.join('Class%s x%s{%s};\n' % (class_index,
168                                                             class_index,
169                                                             ', '.join('x%s' % dep_index
170                                                                       for dep_index in injection_graph.successors(class_index)))
171                                     for class_index in reversed(list(nx.topological_sort(injection_graph))))
172
173    void_casts = ''.join('(void) x%s;\n' % index
174                         for index in injection_graph.nodes_iter())
175
176    if generate_runtime_bench_code:
177        template = """
178{include_directives}
179
180#include <ctime>
181#include <iostream>
182#include <cstdlib>
183#include <iomanip>
184#include <chrono>
185
186using namespace std;
187
188void do_injection() {{
189  {instance_creations}
190  {void_casts}
191}}
192
193int main(int argc, char* argv[]) {{
194  if (argc != 2) {{
195    std::cout << "Need to specify num_loops as argument." << std::endl;
196    exit(1);
197  }}
198  size_t num_loops = std::atoi(argv[1]);
199  std::chrono::high_resolution_clock::time_point start_time;
200
201  start_time = std::chrono::high_resolution_clock::now();
202  for (size_t i = 0; i < 1 + num_loops/100; i++) {{
203    do_injection();
204  }}
205  double fullInjectionTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
206
207  std::cout << std::fixed;
208  std::cout << std::setprecision(15);
209  std::cout << "Total per request          = " << fullInjectionTime * 100 / num_loops << std::endl;
210  return 0;
211}}
212"""
213    else:
214        template = """
215{include_directives}
216
217#include <memory>
218#include <iostream>
219
220int main() {{
221  {instance_creations}
222  {void_casts}
223  std::cout << "Hello, world" << std::endl;
224  return 0;
225}}
226"""
227    return template.format(**locals())
228