Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: compiled/bindings.ipp

Issue 29333474: Issue 4125 - [emscripten] Convert filter classes to C++ (Closed)
Patch Set: Got rid of extra output in bindings.js file Created June 9, 2016, 1:35 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #pragma once
sergei 2016/06/16 21:16:53 Why does it not print 'use strict;'?
sergei 2016/06/16 21:16:57 Why are "-1" at the beginning of compiled/bindings
sergei 2016/06/16 21:17:08 I don't think that it is worth paying attention to
Wladimir Palant 2016/12/06 10:47:55 The bindings are only a small part of the resultin
Wladimir Palant 2016/12/06 10:48:12 That's output from an abort() call not being suppr
2
3 #include <cstdio>
4 #include <cstdlib>
5 #include <cstring>
6 #include <exception>
7 #include <map>
8 #include <string>
9 #include <type_traits>
10 #include <utility>
11 #include <vector>
sergei 2016/06/16 21:16:50 What do you think about excluding std classes from
Wladimir Palant 2016/12/06 10:48:00 Headers don't result in any code, they are merely
12
13 #include <emscripten.h>
14
15 #include "String.h"
16 #include "intrusive_ptr.h"
17
18 namespace bindings_internal
sergei 2016/06/16 21:17:05 It would be good to have some comment here which e
Wladimir Palant 2016/12/06 10:47:52 With the approach changing with each iteration, I
19 {
20 typedef void* TYPEID;
21
22 enum class TypeCategory
23 {
24 UNKNOWN,
25 VOID,
26 INT,
27 DEPENDENT_STRING,
28 OWNED_STRING,
29 STRING_REF,
30 CLASS_PTR
31 };
32
33 template<typename T>
34 struct TypeInfo
35 {
36 static char c;
sergei 2016/06/16 21:17:10 I think it would be better to call it something li
Wladimir Palant 2016/12/06 10:47:59 Done.
37 constexpr operator TYPEID() const
38 {
39 return &c;
40 }
41
42 constexpr operator TypeCategory() const
43 {
44 if (std::is_void<T>())
45 return TypeCategory::VOID;
46
47 if (std::is_integral<T>() || std::is_enum<T>())
48 return TypeCategory::INT;
49
50 if (std::is_same<DependentString,T>() || std::is_same<const DependentStrin g,T>())
51 return TypeCategory::DEPENDENT_STRING;
52
53 if (std::is_same<OwnedString,T>() || std::is_same<const OwnedString,T>())
54 return TypeCategory::OWNED_STRING;
55
56 if (std::is_same<String&,T>() || std::is_same<const String&,T>() ||
57 std::is_same<DependentString&,T>())
58 {
59 return TypeCategory::STRING_REF;
60 }
61
62 if (std::is_pointer<T>() && std::is_class<typename std::remove_pointer<T>: :type>())
63 return TypeCategory::CLASS_PTR;
64
65 return TypeCategory::UNKNOWN;
66 }
67
68 TYPEID pointer_type() const
sergei 2016/06/16 21:16:51 Actually it might be makes sense to make the metho
Wladimir Palant 2016/12/06 10:47:42 Done.
69 {
70 if (std::is_pointer<T>())
71 return TypeInfo<typename std::remove_pointer<T>::type>();
72 else
73 return nullptr;
74 }
75 };
76
77 template<typename T>
78 char TypeInfo<T>::c;
79
80 struct FunctionInfo
81 {
82 TypeCategory returnType;
83 TYPEID pointerType;
84 std::vector<TypeCategory> args;
85 bool instance_function;
86 int effectiveArgs;
87 TypeCategory effectiveReturnType;
88 char name[1024];
89
90 FunctionInfo()
91 {
92 name[0] = 0;
sergei 2016/06/16 21:17:02 Although it makes no difference I personally prefe
Wladimir Palant 2016/12/06 10:47:39 Done.
93 }
94
95 FunctionInfo(TypeCategory returnType, TYPEID pointerType,
96 std::initializer_list<TypeCategory> argTypes, bool instance_function,
97 void* function)
98 : returnType(returnType), pointerType(pointerType),
99 instance_function(instance_function)
100 {
101 name[0] = 0;
102
103 // The function parameter is a pointer to the function pointer.
104 // Emscripten's "function pointers" are actually integers indicating the
105 // position in the call table. 0 represents nullptr.
106 if (!*reinterpret_cast<int*>(function))
107 return;
108
109 for (auto it = argTypes.begin(); it != argTypes.end(); ++it)
110 {
111 if (*it != TypeCategory::INT && *it != TypeCategory::STRING_REF &&
112 *it != TypeCategory::CLASS_PTR)
113 {
114 throw std::runtime_error("Unexpected function argument type");
115 }
116 args.push_back(*it);
117 }
118
119 if (returnType != TypeCategory::VOID && returnType != TypeCategory::INT &&
120 returnType != TypeCategory::DEPENDENT_STRING &&
121 returnType != TypeCategory::OWNED_STRING &&
122 returnType != TypeCategory::STRING_REF &&
123 returnType != TypeCategory::CLASS_PTR)
124 {
125 throw std::runtime_error("Unexpected function return type");
126 }
127
128 effectiveArgs = args.size();
129 effectiveReturnType = returnType;
130 if (instance_function)
131 effectiveArgs++;
132
133 if (returnType == TypeCategory::DEPENDENT_STRING ||
134 returnType == TypeCategory::OWNED_STRING)
135 {
136 effectiveArgs++;
137 effectiveReturnType = TypeCategory::VOID;
138 }
139
140 get_function_name(function, effectiveArgs,
141 effectiveReturnType == TypeCategory::VOID);
142 }
143
144 template<typename ReturnType, typename... Args>
145 FunctionInfo(ReturnType (*function)(Args...))
146 : FunctionInfo(TypeInfo<ReturnType>(),
147 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false,
148 &function)
149 {
150 }
151
152 template<typename ClassType, typename ReturnType, typename... Args>
153 FunctionInfo(ReturnType (ClassType::*function)(Args...))
154 : FunctionInfo(TypeInfo<ReturnType>(),
155 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true,
156 &function)
157 {
158 }
159
160 template<typename ClassType, typename ReturnType, typename... Args>
161 FunctionInfo(ReturnType (ClassType::*function)(Args...) const)
162 : FunctionInfo(TypeInfo<ReturnType>(),
163 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true,
164 &function)
165 {
166 }
167
168 bool empty() const
169 {
170 return name[0] == 0;
171 }
172
173 void get_function_name(void* ptr, int numArgs, bool voidResult)
174 {
175 // This is a hack, C++ won't let us get the mangled function name.
176 // JavaScript is more dynamic so we pass the pointer to our function
177 // there. With that and the function signature we can call the function -
178 // with a full stack so that we will cause it to abort. Sometimes the
179 // function we are calling will also be missing from the build. The result
180 // is the same: abort() is called which in turn calls stackTrace(). By
181 // replacing stackTrace() we get access to the call stack and search it
182 // for the name of our function.
183
184 EM_ASM_ARGS({
185 var signature = $3 ? "v" : "i";
186 var args = [];
187 for (var i = 0; i < $2; i++)
188 {
189 signature += "i";
190 args.push(0);
191 }
192
193 var oldPrint = Module.print;
194 var oldPrintErr = Module.printErr;
195 var oldStackTrace = stackTrace;
196 var sp = Runtime.stackSave();
197 Module.print = function(){};
198 Module.printErr = function(){};
199 stackTrace = function()
200 {
201 var stack = [];
202 for (var f = arguments.callee.caller; f; f = f.caller)
203 {
204 if (f.name)
205 {
206 if (f.name.indexOf("dynCall") == 0)
207 break;
208 else
209 stack.push(f.name);
210 }
211 }
212
213 result = stack[stack.length - 1];
214 if (result && result.indexOf("__wrapper") >= 0)
215 result = stack[stack.length - 2];
216 throw result;
217 };
218
219 Runtime.stackRestore(STACK_MAX);
220
221 try
222 {
223 Runtime.dynCall(signature, HEAP32[$1 >> 2], args);
224 }
225 catch(e)
226 {
227 Module.stringToAscii(e, $0);
228 }
229 finally
230 {
231 Runtime.stackRestore(sp);
232 Module.print = oldPrint;
233 Module.printErr = oldPrintErr;
234 stackTrace = oldStackTrace;
235 }
236 }, name, ptr, numArgs, voidResult);
237 }
238 };
239
240 class NoBaseClass
241 {
242 };
243
244 struct PropertyInfo
245 {
246 std::string name;
247 FunctionInfo getter;
248 FunctionInfo setter;
249 std::string jsValue;
250 };
251
252 struct MethodInfo
253 {
254 std::string name;
255 FunctionInfo call;
256 };
257
258 struct DifferentiatorInfo
259 {
260 FunctionInfo call;
261 std::vector<std::pair<int,std::string>> mapping;
262 };
263
264 struct ClassInfo
265 {
266 ClassInfo* baseClass;
267 std::string name;
268 std::vector<PropertyInfo> properties;
269 std::vector<MethodInfo> methods;
270 std::vector<FunctionInfo> initializers;
271 DifferentiatorInfo subclass_differentiator;
272 };
273
274 std::map<TYPEID,ClassInfo> classes;
275
276 void register_class(const char* name, TYPEID classID, TYPEID baseClassID)
277 {
278 auto it = classes.find(classID);
279 if (it != classes.end())
280 throw std::runtime_error(std::string("Duplicate definition for class ") + name);
281
282 ClassInfo* baseClass = nullptr;
283 if (baseClassID != TypeInfo<NoBaseClass>())
284 {
285 it = classes.find(baseClassID);
286 if (it == classes.end())
287 throw std::runtime_error(std::string("Unknown base class defined for cla ss ") + name);
288 baseClass = &(it->second);
289 }
290
291 ClassInfo classInfo;
292 classInfo.baseClass = baseClass;
293 classInfo.name = name;
294 classes[classID] = classInfo;
295 }
296
297 void register_property(TYPEID classID, const char* name,
298 const FunctionInfo& getter, const FunctionInfo& setter,
299 const char* jsValue = "")
300 {
301 auto it = classes.find(classID);
302 if (it == classes.end())
303 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name);
304
305 PropertyInfo propertyInfo;
306 propertyInfo.name = name;
307 propertyInfo.getter = getter;
308 propertyInfo.setter = setter;
309 propertyInfo.jsValue = jsValue;
310 it->second.properties.push_back(propertyInfo);
311 }
312
313 void register_method(TYPEID classID, const char* name,
314 const FunctionInfo& call)
315 {
316 auto it = classes.find(classID);
317 if (it == classes.end())
318 throw std::runtime_error(std::string("Method defined on unknown class: ") + name);
319
320 MethodInfo methodInfo;
321 methodInfo.name = name;
322 methodInfo.call = call;
323 it->second.methods.push_back(methodInfo);
324 }
325
326 void register_initializer(TYPEID classID, const FunctionInfo& call)
327 {
328 auto it = classes.find(classID);
329 if (it == classes.end())
330 throw std::runtime_error("Initializer defined on unknown class");
331
332 it->second.initializers.push_back(call);
333 }
334
335 void register_differentiator(TYPEID classID, const FunctionInfo& call,
336 std::vector<std::pair<int,std::string>>& mapping)
337 {
338 auto it = classes.find(classID);
339 if (it == classes.end())
340 throw std::runtime_error("Subclass differentiator defined on unknown class ");
341
342 if (!it->second.subclass_differentiator.call.empty())
343 throw std::runtime_error("More than one subclass differentiator defined fo r class " + it->second.name);
344
345 DifferentiatorInfo differentiatorInfo;
346 differentiatorInfo.call = call;
347 differentiatorInfo.mapping = mapping;
348 it->second.subclass_differentiator = differentiatorInfo;
349 }
350
351 const std::string generateCall(const FunctionInfo& call,
352 std::vector<std::string>& params)
353 {
354 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
355 call.returnType == TypeCategory::OWNED_STRING)
356 {
357 params.insert(params.begin(), "string");
358 }
359
360 std::string call_str(call.name);
361 call_str += "(";
362 for (int i = 0; i < params.size(); i++)
363 {
364 if (i > 0)
365 call_str += ", ";
366 call_str += params[i];
367 }
368 call_str += ")";
369
370 if (call.returnType == TypeCategory::VOID)
371 return " " + call_str + ";\n";
372 else if (call.returnType == TypeCategory::INT)
373 return " var result = " + call_str + ";\n";
374 else if (call.returnType == TypeCategory::DEPENDENT_STRING ||
375 call.returnType == TypeCategory::OWNED_STRING)
376 {
377 std::string result;
378 result += " var string = createString();\n";
379 result += " " + call_str + ";\n";
380 result += " var result = getStringData(string);\n";
381 if (call.returnType == TypeCategory::OWNED_STRING)
382 result += " Module._DestroyString(string);\n";
383 return result;
384 }
385 else if (call.returnType == TypeCategory::STRING_REF)
386 {
387 return " var result = getStringData(" + call_str + ");\n";
388 }
389 else if (call.returnType == TypeCategory::CLASS_PTR)
390 {
391 std::string result;
392 result += " var result = " + call_str + ";\n";
393 result += " if (result)\n";
394 result += " {\n";
395 result += " Module._AddRef(result);\n";
396
397 auto it = classes.find(call.pointerType);
398 if (it == classes.end())
399 throw std::runtime_error("Function " + std::string(call.name) + " return s pointer to unknown class");
400
401 const ClassInfo& cls = it->second;
402 if (cls.subclass_differentiator.call.empty())
403 result += " result = " + cls.name + "(result);\n";
404 else
405 {
406 result += " var type = " +
407 std::string(cls.subclass_differentiator.call.name) + "(result);\n";
408 result += " if (type in " + cls.name + "_mapping)\n";
409 result += " result = new (exports[" + cls.name + "_mapping[type]])( result);\n";
410 result += " else\n";
411 result += " throw new Error('Unexpected " + cls.name + " type: ' + type);\n";
412 }
413
414 result += " }\n";
415 return result;
416 }
417 else
418 throw std::runtime_error("Unexpected return type for " + std::string(call. name));
419 }
420
421 const std::string wrapCall(const FunctionInfo& call)
422 {
423 char buffer[20];
424 bool hasStringArgs = false;
425 std::vector<std::string> params;
426 std::string prefix = "function(";
427 for (int i = 0; i < call.args.size(); i++)
428 {
429 sprintf(buffer, "arg%i", i);
430 if (i > 0)
431 prefix += ", ";
432 prefix += buffer;
433
sergei 2016/06/16 21:16:45 Additional line.
Wladimir Palant 2016/12/06 10:47:57 Done.
434
435 if (call.args[i] == TypeCategory::STRING_REF)
436 {
437 hasStringArgs = true;
438 params.push_back(std::string("createString(") + buffer + ")");
439 }
440 else
441 params.push_back(buffer);
442 }
443 prefix += ")\n{\n";
444
445 std::string suffix = "}";
446 if (call.returnType != TypeCategory::VOID)
447 suffix = " return result;\n" + suffix;
448
449 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
450 call.returnType == TypeCategory::OWNED_STRING || hasStringArgs)
451 {
452 prefix += " var sp = Runtime.stackSave();\n";
453 suffix = " Runtime.stackRestore(sp);\n" + suffix;
454 }
455
456 if (call.instance_function)
457 params.insert(params.begin(), "this._pointer");
458
459 return prefix + generateCall(call, params) + suffix;
460 }
461
462 const std::string generatePropertyDescriptor(const PropertyInfo& property)
sergei 2016/06/16 21:16:56 I'm not sure that we need the `const` for the retu
Wladimir Palant 2016/12/06 10:48:05 Done.
463 {
464 if (!property.jsValue.empty())
465 return "value: " + property.jsValue;
466
467 std::string result("get: " + wrapCall(property.getter));
468 if (!property.setter.empty())
469 result += ", set: " + wrapCall(property.setter);
470 return result;
471 }
472
473 void printHelpers()
474 {
475 printf("var sizeofString = %i\n", sizeof(String));
sergei 2016/06/16 21:17:16 What about ';' after "%i"?
Wladimir Palant 2016/12/06 10:48:07 Done.
476
477 puts(R"(
478 function copyString(str, buffer)
479 {
480 var length = str.length;
481 for (var i = 0, pointer = (buffer >> 1); i < length; i++, pointer++)
482 HEAP16[pointer] = str.charCodeAt(i);
483 return length;
484 }
485
486 function createString(str)
487 {
488 var length = 0;
489 var buffer = 0;
490 if (str)
491 {
492 buffer = Runtime.stackAlloc(str.length * 2);
493 length = copyString(str, buffer);
494 }
495
496 var result = Module.Runtime.stackAlloc(sizeofString);
497 Module._InitString(result, buffer, length);
498 return result;
499 }
500
501 function getStringData(str)
sergei 2016/06/16 21:17:15 What do you think about renaming of this method. C
Wladimir Palant 2016/12/06 10:48:10 Ok, readString() it is - hopefully more obvious.
502 {
503 var length = Module._GetStringLength(str);
504 var pointer = Module._GetStringData(str) >> 1;
505 return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + len gth));
sergei 2016/06/16 21:16:46 Shouldn't we use fromCodePoint instead of fromChar
Wladimir Palant 2016/12/06 10:48:08 No, we shouldn't - we are using 16-bit characters.
506 }
507
508 function createClass(superclass)
509 {
510 var result = function(pointer)
511 {
512 this._pointer = pointer;
sergei 2016/06/16 21:17:11 We should call Module._AddRef on stored _pointer h
Wladimir Palant 2016/12/06 10:47:41 No, we shouldn't - that's a raw pointer we got her
513 };
514 if (superclass)
515 result.prototype = Object.create(superclass.prototype);
516 result.prototype.delete = function()
517 {
518 Module._ReleaseRef(this._pointer);
519 };
520 return result;
521 })");
522 }
523
524 void printClass(ClassInfo& cls)
525 {
526 DifferentiatorInfo differentiator = cls.subclass_differentiator;
527 if (!differentiator.call.empty())
528 {
529 printf("var %s_mapping = \n", cls.name.c_str());
530 puts("{");
531 for (auto it = differentiator.mapping.begin(); it != differentiator.mappin g.end(); ++it)
532 printf(" %i: '%s',\n", it->first, it->second.c_str());
533 puts("};");
534 }
535
536 printf("exports.%s = createClass(%s);\n", cls.name.c_str(),
537 (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : ""));
538
539 for (auto it = cls.properties.begin(); it != cls.properties.end(); ++it)
540 {
541 printf("Object.defineProperty(exports.%s.prototype, '%s', {%s});\n",
542 cls.name.c_str(), it->name.c_str(),
543 generatePropertyDescriptor(*it).c_str());
544 }
545
546 for (auto it = cls.methods.begin(); it != cls.methods.end(); ++it)
547 {
548 std::string obj("exports." + cls.name);
549 if (it->call.instance_function)
550 obj += ".prototype";
551 printf("%s.%s = %s;\n", obj.c_str(), it->name.c_str(),
552 wrapCall(it->call).c_str());
553 }
554
555 for (auto it = cls.initializers.begin(); it != cls.initializers.end(); ++it)
556 printf("%s()\n", it->name);
557 }
558
559 void printBindings()
560 {
561 printHelpers();
562
563 for (auto it = classes.begin(); it != classes.end(); ++it)
564 printClass(it->second);
565 }
566 }
567
568 #if defined(PRINT_BINDINGS)
569 // Bindings generation step: collect bindings information and print
570 // corresponding JS code.
571 #define EMSCRIPTEN_BINDINGS \
572 static struct BindingsInitializer {\
sergei 2016/06/16 21:16:54 I think static is not required here.
Wladimir Palant 2016/12/06 10:47:44 Done.
573 BindingsInitializer();\
574 BindingsInitializer(bool dummy)\
575 {\
576 try\
577 {\
578 BindingsInitializer();\
579 bindings_internal::printBindings();\
580 }\
581 catch (const std::exception& e)\
582 {\
583 EM_ASM_ARGS(\
584 console.error("Error occurred generating JavaScript bindings: " +\
585 Module.AsciiToString($0)), e.what()\
586 );\
587 abort();\
588 }\
589 }\
590 } BindingsInitializer_instance(true);\
591 BindingsInitializer::BindingsInitializer()
592 #else
593 // Actual compilation step: ignore bindings information but define some
594 // exported helper functions necessary for the bindings.
595 #define EMSCRIPTEN_BINDINGS \
596 extern "C"\
597 {\
598 void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\
599 String::value_type* data, String::size_type len)\
600 {\
601 /* String is already allocated on stack, we merely need to call*/\
602 /* constructor.*/\
603 new (str) DependentString(data, len);\
604 }\
605 void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\
606 {\
607 /* Stack memory will be freed automatically, we need to call*/\
608 /* destructor explicitly however.*/\
609 str->~OwnedString();\
610 }\
611 String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\
612 const String& str)\
613 {\
614 return str.length();\
615 }\
616 const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\
617 const String& str)\
618 {\
619 return str.data();\
620 }\
621 void EMSCRIPTEN_KEEPALIVE AddRef(ref_counted* ptr)\
622 {\
623 ptr->AddRef();\
624 }\
625 void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\
626 {\
627 ptr->ReleaseRef();\
628 }\
629 }\
630 void BindingsInitializer_dummy()
631 #endif
632
633 template<typename ClassType,
634 typename BaseClass = bindings_internal::NoBaseClass,
635 typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::typ e* = nullptr>
636 class class_
637 {
638 public:
639 class_(const char* name)
640 {
641 bindings_internal::register_class(name,
642 bindings_internal::TypeInfo<ClassType>(),
643 bindings_internal::TypeInfo<BaseClass>());
644 }
645
646 template<typename FieldType>
647 const class_& property(const char* name,
648 FieldType (ClassType::*getter)() const,
649 void (ClassType::*setter)(FieldType) = 0) const
sergei 2016/06/16 21:17:07 I would rather use nullptr instead of 0.
Wladimir Palant 2016/12/06 10:47:50 Done.
650 {
651 bindings_internal::register_property(
652 bindings_internal::TypeInfo<ClassType>(), name, getter, setter);
653 return *this;
654 }
655
656 const class_& class_property(const char* name,
657 const char* jsValue) const
658 {
659 bindings_internal::register_property(
660 bindings_internal::TypeInfo<ClassType>(), name,
661 bindings_internal::FunctionInfo(), bindings_internal::FunctionInfo(),
662 jsValue);
663 return *this;
664 }
665
666 template<typename ReturnType, typename... Args>
667 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..)) const
668 {
669 bindings_internal::register_method(
670 bindings_internal::TypeInfo<ClassType>(), name, method);
671 return *this;
672 }
673
674 template<typename ReturnType, typename... Args>
675 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..) const) const
676 {
677 bindings_internal::register_method(
678 bindings_internal::TypeInfo<ClassType>(), name, method);
679 return *this;
680 }
681
682 template<typename ReturnType, typename... Args>
683 const class_& class_function(const char* name, ReturnType (*method)(Args...)) const
684 {
685 bindings_internal::register_method(
686 bindings_internal::TypeInfo<ClassType>(), name, method);
687 return *this;
688 }
689
690 const class_& class_initializer(void (*function)()) const
691 {
692 bindings_internal::register_initializer(
693 bindings_internal::TypeInfo<ClassType>(), function);
694 return *this;
695 }
696
697 template<typename ReturnType,
698 typename std::enable_if<std::is_convertible<ReturnType, int>::value>::type * = nullptr>
699 const class_& subclass_differentiator(ReturnType (*function)(ClassType*),
700 std::initializer_list<std::pair<ReturnType,const char*>> list) const
sergei 2016/06/16 21:17:04 Just wonder, why are no spaces between types for s
Wladimir Palant 2016/12/06 10:47:54 Done.
701 {
702 std::vector<std::pair<int,std::string>> mapping;
703 for (auto it = list.begin(); it != list.end(); ++it)
704 mapping.push_back(std::pair<int,std::string>(it->first, it->second));
sergei 2016/06/16 21:16:59 Merely a shorter syntax, here and above we can use
Wladimir Palant 2016/12/06 10:47:48 Done.
705
706 bindings_internal::register_differentiator(
707 bindings_internal::TypeInfo<ClassType>(), function, mapping);
708 return *this;
709 }
710 };
OLDNEW

Powered by Google App Engine
This is Rietveld