| Index: compiled/bindings.h | 
| =================================================================== | 
| new file mode 100644 | 
| --- /dev/null | 
| +++ b/compiled/bindings.h | 
| @@ -0,0 +1,680 @@ | 
| +#pragma once | 
| + | 
| +#include <cstdio> | 
| +#include <cstdlib> | 
| +#include <cstring> | 
| +#include <exception> | 
| +#include <map> | 
| +#include <string> | 
| +#include <type_traits> | 
| +#include <utility> | 
| +#include <vector> | 
| + | 
| +#include <emscripten.h> | 
| + | 
| +#include "String.h" | 
| +#include "intrusive_ptr.h" | 
| + | 
| +typedef void* TYPEID; | 
| + | 
| +enum class TypeCategory | 
| +{ | 
| +  UNKNOWN, | 
| +  VOID, | 
| +  INT, | 
| +  DEPENDENT_STRING, | 
| +  OWNED_STRING, | 
| +  STRING_REF, | 
| +  CLASS_PTR | 
| +}; | 
| + | 
| +template<typename T> | 
| +struct TypeInfo | 
| +{ | 
| +  static char c; | 
| +  constexpr operator TYPEID() const | 
| +  { | 
| +    return &c; | 
| +  } | 
| + | 
| +  constexpr operator TypeCategory() const | 
| +  { | 
| +    if (std::is_void<T>()) | 
| +      return TypeCategory::VOID; | 
| + | 
| +    if (std::is_integral<T>() || std::is_enum<T>()) | 
| +      return TypeCategory::INT; | 
| + | 
| +    if (std::is_same<DependentString,T>() || std::is_same<const DependentString,T>()) | 
| +      return TypeCategory::DEPENDENT_STRING; | 
| + | 
| +    if (std::is_same<OwnedString,T>() || std::is_same<const OwnedString,T>()) | 
| +      return TypeCategory::OWNED_STRING; | 
| + | 
| +    if (std::is_same<String&,T>() || std::is_same<const String&,T>() || | 
| +      std::is_same<DependentString&,T>()) | 
| +    { | 
| +      return TypeCategory::STRING_REF; | 
| +    } | 
| + | 
| +    if (std::is_pointer<T>() && std::is_class<typename std::remove_pointer<T>::type>()) | 
| +      return TypeCategory::CLASS_PTR; | 
| + | 
| +    return TypeCategory::UNKNOWN; | 
| +  } | 
| + | 
| +  TYPEID pointer_type() const | 
| +  { | 
| +    if (std::is_pointer<T>()) | 
| +      return TypeInfo<typename std::remove_pointer<T>::type>(); | 
| +    else | 
| +      return nullptr; | 
| +  } | 
| +}; | 
| + | 
| +template<typename T> | 
| +char TypeInfo<T>::c; | 
| + | 
| +struct FunctionInfo | 
| +{ | 
| +  TypeCategory returnType; | 
| +  TYPEID pointerType; | 
| +  std::vector<TypeCategory> args; | 
| +  bool instance_function; | 
| +  int effectiveArgs; | 
| +  TypeCategory effectiveReturnType; | 
| +  char name[1024]; | 
| + | 
| +  FunctionInfo() | 
| +  { | 
| +    name[0] = 0; | 
| +  } | 
| + | 
| +  FunctionInfo(TypeCategory returnType, TYPEID pointerType, | 
| +      std::initializer_list<TypeCategory> argTypes, bool instance_function, | 
| +      void* function) | 
| +      : returnType(returnType), pointerType(pointerType), | 
| +        instance_function(instance_function) | 
| +  { | 
| +    name[0] = 0; | 
| +    if (!*reinterpret_cast<int*>(function)) | 
| +      return; | 
| + | 
| +    for (auto it = argTypes.begin(); it != argTypes.end(); ++it) | 
| +    { | 
| +      if (*it != TypeCategory::INT && *it != TypeCategory::STRING_REF && | 
| +          *it != TypeCategory::CLASS_PTR) | 
| +      { | 
| +        throw std::runtime_error("Unexpected function argument type"); | 
| +      } | 
| +      args.push_back(*it); | 
| +    } | 
| + | 
| +    if (returnType != TypeCategory::VOID && returnType != TypeCategory::INT && | 
| +        returnType != TypeCategory::DEPENDENT_STRING && | 
| +        returnType != TypeCategory::OWNED_STRING && | 
| +        returnType != TypeCategory::CLASS_PTR) | 
| +    { | 
| +      throw std::runtime_error("Unexpected function return type"); | 
| +    } | 
| + | 
| +    effectiveArgs = args.size(); | 
| +    effectiveReturnType = returnType; | 
| +    if (instance_function) | 
| +      effectiveArgs++; | 
| + | 
| +    if (returnType == TypeCategory::DEPENDENT_STRING || | 
| +        returnType == TypeCategory::OWNED_STRING) | 
| +    { | 
| +      effectiveArgs++; | 
| +      effectiveReturnType = TypeCategory::VOID; | 
| +    } | 
| + | 
| +    get_function_name(function, effectiveArgs, | 
| +        effectiveReturnType == TypeCategory::VOID); | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  FunctionInfo(ReturnType (*function)(Args...)) | 
| +      : FunctionInfo(TypeInfo<ReturnType>(), | 
| +        TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, | 
| +        &function) | 
| +  { | 
| +  } | 
| + | 
| +  template<typename ClassType, typename ReturnType, typename... Args> | 
| +  FunctionInfo(ReturnType (ClassType::*function)(Args...)) | 
| +      : FunctionInfo(TypeInfo<ReturnType>(), | 
| +        TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 
| +        &function) | 
| +  { | 
| +  } | 
| + | 
| +  template<typename ClassType, typename ReturnType, typename... Args> | 
| +  FunctionInfo(ReturnType (ClassType::*function)(Args...) const) | 
| +      : FunctionInfo(TypeInfo<ReturnType>(), | 
| +        TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 
| +        &function) | 
| +  { | 
| +  } | 
| + | 
| +  bool empty() const | 
| +  { | 
| +    return name[0] == 0; | 
| +  } | 
| + | 
| +  void get_function_name(void* ptr, int numArgs, bool voidResult) | 
| +  { | 
| +    // This is a hack, C++ won't let us get the mangled function name. JavaScript | 
| +    // is more dynamic so we pass the pointer to our function there. With that | 
| +    // and the function signature we can call the function - with intentionally | 
| +    // invalid parameters so that we will cause a segmentation fault (pointers | 
| +    // cannot be negative). Sometimes the function we are calling will also be | 
| +    // missing from the build. The result is the same: abort() is called. By | 
| +    // replacing abort() we get access to the call stack and search it for the | 
| +    // name of our function. | 
| + | 
| +    EM_ASM_ARGS({ | 
| +      var signature = $3 ? "v" : "i"; | 
| +      var args = []; | 
| +      for (var i = 0; i < $2; i++) | 
| +      { | 
| +        signature += "i"; | 
| +        args.push(-10000); | 
| +      } | 
| + | 
| +      var oldPrintErr = Module.printErr; | 
| +      var oldAbort = abort; | 
| +      Module.printErr = function(){}; | 
| +      abort = function() | 
| +      { | 
| +        var stack = []; | 
| +        for (var f = arguments.callee.caller; f; f = f.caller) | 
| +        { | 
| +          if (f.name) | 
| +          { | 
| +            if (f.name.indexOf("dynCall") == 0) | 
| +              break; | 
| +            else | 
| +              stack.push(f.name); | 
| +          } | 
| +        } | 
| + | 
| +        result = stack[stack.length - 1]; | 
| +        if (result && result.indexOf("__wrapper") >= 0) | 
| +          result = stack[stack.length - 2]; | 
| +        throw result; | 
| +      }; | 
| + | 
| +      try | 
| +      { | 
| +        Runtime.dynCall(signature, HEAP32[$1 >> 2], args); | 
| +      } | 
| +      catch(e) | 
| +      { | 
| +        Module.stringToAscii(e, $0); | 
| +      } | 
| +      finally | 
| +      { | 
| +        Module.printErr = oldPrintErr; | 
| +        abort = oldAbort; | 
| +      } | 
| +    }, name, ptr, numArgs, voidResult); | 
| +  } | 
| +}; | 
| + | 
| +class NoBaseClass | 
| +{ | 
| +}; | 
| + | 
| +struct PropertyInfo | 
| +{ | 
| +  std::string name; | 
| +  FunctionInfo getter; | 
| +  FunctionInfo setter; | 
| +  std::string jsValue; | 
| +}; | 
| + | 
| +struct MethodInfo | 
| +{ | 
| +  std::string name; | 
| +  FunctionInfo call; | 
| +}; | 
| + | 
| +struct DifferentiatorInfo | 
| +{ | 
| +  FunctionInfo call; | 
| +  std::vector<std::pair<int,std::string>> mapping; | 
| +}; | 
| + | 
| +struct ClassInfo | 
| +{ | 
| +  ClassInfo* baseClass; | 
| +  std::string name; | 
| +  std::vector<PropertyInfo> properties; | 
| +  std::vector<MethodInfo> methods; | 
| +  std::vector<FunctionInfo> initializers; | 
| +  DifferentiatorInfo subclass_differentiator; | 
| +}; | 
| + | 
| +std::map<TYPEID,ClassInfo> classes; | 
| + | 
| +void register_class(const char* name, TYPEID classID, TYPEID baseClassID) | 
| +{ | 
| +  auto it = classes.find(classID); | 
| +  if (it != classes.end()) | 
| +    throw std::runtime_error(std::string("Duplicate definition for class ") + name); | 
| + | 
| +  ClassInfo* baseClass = nullptr; | 
| +  if (baseClassID != TypeInfo<NoBaseClass>()) | 
| +  { | 
| +    it = classes.find(baseClassID); | 
| +    if (it == classes.end()) | 
| +      throw std::runtime_error(std::string("Unknown base class defined for class ") + name); | 
| +    baseClass = &(it->second); | 
| +  } | 
| + | 
| +  ClassInfo classInfo; | 
| +  classInfo.baseClass = baseClass; | 
| +  classInfo.name = name; | 
| +  classes[classID] = classInfo; | 
| +} | 
| + | 
| +void register_property(TYPEID classID, const char* name, | 
| +    const FunctionInfo& getter, const FunctionInfo& setter, | 
| +    const char* jsValue = "") | 
| +{ | 
| +  auto it = classes.find(classID); | 
| +  if (it == classes.end()) | 
| +    throw std::runtime_error(std::string("Property defined on unknown class: ") + name); | 
| + | 
| +  PropertyInfo propertyInfo; | 
| +  propertyInfo.name = name; | 
| +  propertyInfo.getter = getter; | 
| +  propertyInfo.setter = setter; | 
| +  propertyInfo.jsValue = jsValue; | 
| +  it->second.properties.push_back(propertyInfo); | 
| +} | 
| + | 
| +void register_method(TYPEID classID, const char* name, | 
| +    const FunctionInfo& call) | 
| +{ | 
| +  auto it = classes.find(classID); | 
| +  if (it == classes.end()) | 
| +    throw std::runtime_error(std::string("Method defined on unknown class: ") + name); | 
| + | 
| +  MethodInfo methodInfo; | 
| +  methodInfo.name = name; | 
| +  methodInfo.call = call; | 
| +  it->second.methods.push_back(methodInfo); | 
| +} | 
| + | 
| +void register_initializer(TYPEID classID, const FunctionInfo& call) | 
| +{ | 
| +  auto it = classes.find(classID); | 
| +  if (it == classes.end()) | 
| +    throw std::runtime_error("Initializer defined on unknown class"); | 
| + | 
| +  it->second.initializers.push_back(call); | 
| +} | 
| + | 
| +void register_differentiator(TYPEID classID, const FunctionInfo& call, | 
| +    std::vector<std::pair<int,std::string>>& mapping) | 
| +{ | 
| +  auto it = classes.find(classID); | 
| +  if (it == classes.end()) | 
| +    throw std::runtime_error("Subclass differentiator defined on unknown class"); | 
| + | 
| +  if (!it->second.subclass_differentiator.call.empty()) | 
| +    throw std::runtime_error("More than one subclass differentiator defined for class " + it->second.name); | 
| + | 
| +  DifferentiatorInfo differentiatorInfo; | 
| +  differentiatorInfo.call = call; | 
| +  differentiatorInfo.mapping = mapping; | 
| +  it->second.subclass_differentiator = differentiatorInfo; | 
| +} | 
| + | 
| +const std::string generateCall(const FunctionInfo& call, | 
| +    std::vector<std::string>& params) | 
| +{ | 
| +  if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
| +      call.returnType == TypeCategory::OWNED_STRING) | 
| +  { | 
| +    params.insert(params.begin(), "string"); | 
| +  } | 
| + | 
| +  std::string call_str(call.name); | 
| +  call_str += "("; | 
| +  for (int i = 0; i < params.size(); i++) | 
| +  { | 
| +    if (i > 0) | 
| +      call_str += ", "; | 
| +    call_str += params[i]; | 
| +  } | 
| +  call_str += ")"; | 
| + | 
| +  if (call.returnType == TypeCategory::VOID) | 
| +    return "  " + call_str + ";\n"; | 
| +  else if (call.returnType == TypeCategory::INT) | 
| +    return "  var result = " + call_str + ";\n"; | 
| +  else if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
| +      call.returnType == TypeCategory::OWNED_STRING) | 
| +  { | 
| +    std::string result; | 
| +    result += "  var string = createString();\n"; | 
| +    result += "  " + call_str + ";\n"; | 
| +    result += "  var result = getStringData(string);\n"; | 
| +    if (call.returnType == TypeCategory::OWNED_STRING) | 
| +      result += "  Module._DestroyString(string);\n"; | 
| +    return result; | 
| +  } | 
| +  else if (call.returnType == TypeCategory::CLASS_PTR) | 
| +  { | 
| +    std::string result; | 
| +    result += "  var result = " + call_str + ";\n"; | 
| +    result += "  if (result)\n"; | 
| +    result += "  {\n"; | 
| +    result += "    Module._AddRef(result);\n"; | 
| + | 
| +    auto it = classes.find(call.pointerType); | 
| +    if (it == classes.end()) | 
| +      throw std::runtime_error("Function " + std::string(call.name) + " returns pointer to unknown class"); | 
| + | 
| +    const ClassInfo& cls = it->second; | 
| +    if (cls.subclass_differentiator.call.empty()) | 
| +      result += "    result = " + cls.name + "(result);\n"; | 
| +    else | 
| +    { | 
| +      result += "    var type = " + | 
| +          std::string(cls.subclass_differentiator.call.name) + "(result);\n"; | 
| +      result += "    if (type in " + cls.name + "_mapping)\n"; | 
| +      result += "      result = new (exports[" + cls.name + "_mapping[type]])(result);\n"; | 
| +      result += "    else\n"; | 
| +      result += "      throw new Error('Unexpected " + cls.name + " type: ' + type);\n"; | 
| +    } | 
| + | 
| +    result += "  }\n"; | 
| +    return result; | 
| +  } | 
| +  else | 
| +    throw std::runtime_error("Unexpected return type for " + std::string(call.name)); | 
| +} | 
| + | 
| +const std::string wrapCall(const FunctionInfo& call) | 
| +{ | 
| +  char buffer[20]; | 
| +  bool hasStringArgs = false; | 
| +  std::vector<std::string> params; | 
| +  std::string prefix = "function("; | 
| +  for (int i = 0; i < call.args.size(); i++) | 
| +  { | 
| +    sprintf(buffer, "arg%i", i); | 
| +    if (i > 0) | 
| +      prefix += ", "; | 
| +    prefix += buffer; | 
| + | 
| + | 
| +    if (call.args[i] == TypeCategory::STRING_REF) | 
| +    { | 
| +      hasStringArgs = true; | 
| +      params.push_back(std::string("createString(") + buffer + ")"); | 
| +    } | 
| +    else | 
| +      params.push_back(buffer); | 
| +  } | 
| +  prefix += ")\n{\n"; | 
| + | 
| +  std::string suffix = "}"; | 
| +  if (call.returnType != TypeCategory::VOID) | 
| +    suffix = "  return result;\n" + suffix; | 
| + | 
| +  if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
| +      call.returnType == TypeCategory::OWNED_STRING || hasStringArgs) | 
| +  { | 
| +    prefix += "  var sp = Runtime.stackSave();\n"; | 
| +    suffix = "  Runtime.stackRestore(sp);\n" + suffix; | 
| +  } | 
| + | 
| +  if (call.instance_function) | 
| +    params.insert(params.begin(), "this._pointer"); | 
| + | 
| +  return prefix + generateCall(call, params) + suffix; | 
| +} | 
| + | 
| +const std::string generatePropertyDescriptor(const PropertyInfo& property) | 
| +{ | 
| +  if (!property.jsValue.empty()) | 
| +    return "value: " + property.jsValue; | 
| + | 
| +  std::string result("get: " + wrapCall(property.getter)); | 
| +  if (!property.setter.empty()) | 
| +    result += ", set: " + wrapCall(property.setter); | 
| +  return result; | 
| +} | 
| + | 
| +void printHelpers() | 
| +{ | 
| +  printf("var sizeofString = %i\n", sizeof(String)); | 
| + | 
| +  puts(R"( | 
| +function copyString(str, buffer) | 
| +{ | 
| +  var length = str.length; | 
| +  for (var i = 0, pointer = (buffer >> 1); i < length; i++, pointer++) | 
| +    HEAP16[pointer] = str.charCodeAt(i); | 
| +  return length; | 
| +} | 
| + | 
| +function createString(str) | 
| +{ | 
| +  var length = 0; | 
| +  var buffer = 0; | 
| +  if (str) | 
| +  { | 
| +    buffer = Runtime.stackAlloc(str.length * 2); | 
| +    length = copyString(str, buffer); | 
| +  } | 
| + | 
| +  var result = Module.Runtime.stackAlloc(sizeofString); | 
| +  Module._InitString(result, buffer, length); | 
| +  return result; | 
| +} | 
| + | 
| +function getStringData(str) | 
| +{ | 
| +  var length = Module._GetStringLength(str); | 
| +  var pointer = Module._GetStringData(str) >> 1; | 
| +  return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + length)); | 
| +} | 
| + | 
| +function createClass(superclass) | 
| +{ | 
| +  var result = function(pointer) | 
| +  { | 
| +    this._pointer = pointer; | 
| +  }; | 
| +  if (superclass) | 
| +    result.prototype = Object.create(superclass.prototype); | 
| +  result.prototype.delete = function() | 
| +  { | 
| +    Module._ReleaseRef(this._pointer); | 
| +  }; | 
| +  return result; | 
| +})"); | 
| +} | 
| + | 
| +void printClass(ClassInfo& cls) | 
| +{ | 
| +  DifferentiatorInfo differentiator = cls.subclass_differentiator; | 
| +  if (!differentiator.call.empty()) | 
| +  { | 
| +    printf("var %s_mapping = \n", cls.name.c_str()); | 
| +    puts("{"); | 
| +    for (auto it = differentiator.mapping.begin(); it != differentiator.mapping.end(); ++it) | 
| +      printf("  %i: '%s',\n", it->first, it->second.c_str()); | 
| +    puts("};"); | 
| +  } | 
| + | 
| +  printf("exports.%s = createClass(%s);\n", cls.name.c_str(), | 
| +      (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "")); | 
| + | 
| +  for (auto it = cls.properties.begin(); it != cls.properties.end(); ++it) | 
| +  { | 
| +    printf("Object.defineProperty(exports.%s.prototype, '%s', {%s});\n", | 
| +        cls.name.c_str(), it->name.c_str(), | 
| +        generatePropertyDescriptor(*it).c_str()); | 
| +  } | 
| + | 
| +  for (auto it = cls.methods.begin(); it != cls.methods.end(); ++it) | 
| +  { | 
| +    std::string obj("exports." + cls.name); | 
| +    if (it->call.instance_function) | 
| +      obj += ".prototype"; | 
| +    printf("%s.%s = %s;\n", obj.c_str(), it->name.c_str(), | 
| +        wrapCall(it->call).c_str()); | 
| +  } | 
| + | 
| +  for (auto it = cls.initializers.begin(); it != cls.initializers.end(); ++it) | 
| +    printf("%s()\n", it->name); | 
| +} | 
| + | 
| +void printBindings() | 
| +{ | 
| +  printHelpers(); | 
| + | 
| +  for (auto it = classes.begin(); it != classes.end(); ++it) | 
| +    printClass(it->second); | 
| +} | 
| + | 
| +#if defined(PRINT_BINDINGS) | 
| +  // Bindings generation step: collect bindings information and print | 
| +  // corresponding JS code. | 
| +  #define EMSCRIPTEN_BINDINGS \ | 
| +      static struct BindingsInitializer {\ | 
| +          BindingsInitializer();\ | 
| +          BindingsInitializer(bool dummy)\ | 
| +          {\ | 
| +            try\ | 
| +            {\ | 
| +              BindingsInitializer();\ | 
| +              printBindings();\ | 
| +            }\ | 
| +            catch (const std::exception& e)\ | 
| +            {\ | 
| +              EM_ASM_ARGS(\ | 
| +                console.error("Error occurred generating JavaScript bindings: " +\ | 
| +                    Module.AsciiToString($0)), e.what()\ | 
| +              );\ | 
| +              abort();\ | 
| +            }\ | 
| +          }\ | 
| +      } BindingsInitializer_instance(true);\ | 
| +      BindingsInitializer::BindingsInitializer() | 
| +#else | 
| +  // Actual compilation step: ignore bindings information but define some | 
| +  // exported helper functions necessary for the bindings. | 
| +  #define EMSCRIPTEN_BINDINGS \ | 
| +      extern "C"\ | 
| +      {\ | 
| +        void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\ | 
| +            String::value_type* data, String::size_type len)\ | 
| +        {\ | 
| +          /* String is already allocated on stack, we merely need to call*/\ | 
| +          /* constructor.*/\ | 
| +          new (str) DependentString(data, len);\ | 
| +        }\ | 
| +        void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\ | 
| +        {\ | 
| +          /* Stack memory will be freed automatically, we need to call*/\ | 
| +          /* destructor explicitly however.*/\ | 
| +          str->~OwnedString();\ | 
| +        }\ | 
| +        String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\ | 
| +            const String& str)\ | 
| +        {\ | 
| +          return str.length();\ | 
| +        }\ | 
| +        const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\ | 
| +            const String& str)\ | 
| +        {\ | 
| +          return str.data();\ | 
| +        }\ | 
| +        void EMSCRIPTEN_KEEPALIVE AddRef(ref_counted* ptr)\ | 
| +        {\ | 
| +          ptr->AddRef();\ | 
| +        }\ | 
| +        void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\ | 
| +        {\ | 
| +          ptr->ReleaseRef();\ | 
| +        }\ | 
| +      }\ | 
| +      void BindingsInitializer_dummy() | 
| +#endif | 
| + | 
| +template<typename ClassType, typename BaseClass = NoBaseClass, | 
| +    typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::type* = nullptr> | 
| +class class_ | 
| +{ | 
| +public: | 
| +  class_(const char* name) | 
| +  { | 
| +    register_class(name, TypeInfo<ClassType>(), TypeInfo<BaseClass>()); | 
| +  } | 
| + | 
| +  template<typename FieldType> | 
| +  const class_& property(const char* name, | 
| +      FieldType (ClassType::*getter)() const, | 
| +      void (ClassType::*setter)(FieldType) = 0) const | 
| +  { | 
| +    register_property(TypeInfo<ClassType>(), name, getter, setter); | 
| +    return *this; | 
| +  } | 
| + | 
| +  const class_& class_property(const char* name, | 
| +      const char* jsValue) const | 
| +  { | 
| +    register_property(TypeInfo<ClassType>(), name, FunctionInfo(), | 
| +        FunctionInfo(), jsValue); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  const class_& function(const char* name, ReturnType (ClassType::*method)(Args...)) const | 
| +  { | 
| +    register_method(TypeInfo<ClassType>(), name, method); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  const class_& function(const char* name, ReturnType (ClassType::*method)(Args...) const) const | 
| +  { | 
| +    register_method(TypeInfo<ClassType>(), name, method); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  const class_& class_function(const char* name, ReturnType (*method)(Args...)) const | 
| +  { | 
| +    register_method(TypeInfo<ClassType>(), name, method); | 
| +    return *this; | 
| +  } | 
| + | 
| +  const class_& class_initializer(void (*function)()) const | 
| +  { | 
| +    register_initializer(TypeInfo<ClassType>(), function); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, | 
| +      typename std::enable_if<std::is_convertible<ReturnType, int>::value>::type* = nullptr> | 
| +  const class_& subclass_differentiator(ReturnType (*function)(ClassType*), | 
| +      std::initializer_list<std::pair<ReturnType,const char*>> list) const | 
| +  { | 
| +    std::vector<std::pair<int,std::string>> mapping; | 
| +    for (auto it = list.begin(); it != list.end(); ++it) | 
| +      mapping.push_back(std::pair<int,std::string>(it->first, it->second)); | 
| + | 
| +    register_differentiator(TypeInfo<ClassType>(), function, mapping); | 
| +    return *this; | 
| +  } | 
| +}; | 
|  |