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