text: #pragma once
  
  #if __cplusplus < 201103L
  #error Including <emscripten/bind.h> requires building with -std=c++11 or newer!
  #else
  
  #include <stddef.h>
  #include <assert.h>
  #include <string>
  #include <functional>
  #include <vector>
  #include <map>
  #include <type_traits>
  #include <emscripten/val.h>
  #include <emscripten/wire.h>
  
  namespace emscripten {
      enum class sharing_policy {
          NONE = 0,
          INTRUSIVE = 1,
          BY_EMVAL = 2,
      };
  
      namespace internal {
          typedef long GenericEnumValue;
  
          typedef void* GenericFunction;
          typedef void (*VoidFunctionPtr)(void);
  
          // Implemented in JavaScript.  Don't call these directly.
          extern "C" {
              void _embind_fatal_error(
                  const char* name,
                  const char* payload) __attribute__((noreturn));
  
              void _embind_register_void(
                  TYPEID voidType,
                  const char* name);
  
              void _embind_register_bool(
                  TYPEID boolType,
                  const char* name,
                  size_t size,
                  bool trueValue,
                  bool falseValue);
  
              void _embind_register_integer(
                  TYPEID integerType,
                  const char* name,
                  size_t size,
                  long minRange,
                  unsigned long maxRange);
  
              void _embind_register_float(
                  TYPEID floatType,
                  const char* name,
                  size_t size);
              
              void _embind_register_std_string(
                  TYPEID stringType,
                  const char* name);
  
              void _embind_register_std_wstring(
                  TYPEID stringType,
                  size_t charSize,
                  const char* name);
  
              void _embind_register_emval(
                  TYPEID emvalType,
                  const char* name);
  
              void _embind_register_memory_view(
                  TYPEID memoryViewType,
                  unsigned typedArrayIndex,
                  const char* name);
  
              void _embind_register_function(
                  const char* name,
                  unsigned argCount,
                  const TYPEID argTypes[],
                  const char* signature,
                  GenericFunction invoker,
                  GenericFunction function);
  
              void _embind_register_value_array(
                  TYPEID tupleType,
                  const char* name,
                  const char* constructorSignature,
                  GenericFunction constructor,
                  const char* destructorSignature,
                  GenericFunction destructor);
              
              void _embind_register_value_array_element(
                  TYPEID tupleType,
                  TYPEID getterReturnType,
                  const char* getterSignature,
                  GenericFunction getter,
                  void* getterContext,
                  TYPEID setterArgumentType,
                  const char* setterSignature,
                  GenericFunction setter,
                  void* setterContext);
  
              void _embind_finalize_value_array(TYPEID tupleType);
  
              void _embind_register_value_object(
                  TYPEID structType,
                  const char* fieldName,
                  const char* constructorSignature,
                  GenericFunction constructor,
                  const char* destructorSignature,
                  GenericFunction destructor);
              
              void _embind_register_value_object_field(
                  TYPEID structType,
                  const char* fieldName,
                  TYPEID getterReturnType,
                  const char* getterSignature,
                  GenericFunction getter,
                  void* getterContext,
                  TYPEID setterArgumentType,
                  const char* setterSignature,
                  GenericFunction setter,
                  void* setterContext);
  
              void _embind_finalize_value_object(TYPEID structType);
  
              void _embind_register_class(
                  TYPEID classType,
                  TYPEID pointerType,
                  TYPEID constPointerType,
                  TYPEID baseClassType,
                  const char* getActualTypeSignature,
                  GenericFunction getActualType,
                  const char* upcastSignature,
                  GenericFunction upcast,
                  const char* downcastSignature,
                  GenericFunction downcast,
                  const char* className,
                  const char* destructorSignature,
                  GenericFunction destructor);
  
              void _embind_register_class_constructor(
                  TYPEID classType,
                  unsigned argCount,
                  const TYPEID argTypes[],
                  const char* invokerSignature,
                  GenericFunction invoker,
                  GenericFunction constructor);
  
              void _embind_register_class_function(
                  TYPEID classType,
                  const char* methodName,
                  unsigned argCount,
                  const TYPEID argTypes[],
                  const char* invokerSignature,
                  GenericFunction invoker,
                  void* context,
                  unsigned isPureVirtual);
  
              void _embind_register_class_property(
                  TYPEID classType,
                  const char* fieldName,
                  TYPEID getterReturnType,
                  const char* getterSignature,
                  GenericFunction getter,
                  void* getterContext,
                  TYPEID setterArgumentType,
                  const char* setterSignature,
                  GenericFunction setter,
                  void* setterContext);
  
              void _embind_register_class_class_function(
                  TYPEID classType,
                  const char* methodName,
                  unsigned argCount,
                  const TYPEID argTypes[],
                  const char* invokerSignature,
                  GenericFunction invoker,
                  GenericFunction method);
  
              void _embind_register_class_class_property(
                  TYPEID classType,
                  const char* fieldName,
                  TYPEID fieldType,
                  const void* fieldContext,
                  const char* getterSignature,
                  GenericFunction getter,
                  const char* setterSignature,
                  GenericFunction setter);
  
              EM_VAL _embind_create_inheriting_constructor(
                  const char* constructorName,
                  TYPEID wrapperType,
                  EM_VAL properties);
  
              void _embind_register_enum(
                  TYPEID enumType,
                  const char* name,
                  size_t size,
                  bool isSigned);
  
              void _embind_register_smart_ptr(
                  TYPEID pointerType,
                  TYPEID pointeeType,
                  const char* pointerName,
                  sharing_policy sharingPolicy,
                  const char* getPointeeSignature,
                  GenericFunction getPointee,
                  const char* constructorSignature,
                  GenericFunction constructor,
                  const char* shareSignature,
                  GenericFunction share,
                  const char* destructorSignature,
                  GenericFunction destructor);
  
              void _embind_register_enum_value(
                  TYPEID enumType,
                  const char* valueName,
                  GenericEnumValue value);
  
              void _embind_register_constant(
                  const char* name,
                  TYPEID constantType,
                  uintptr_t value);
          }
      }
  }
  
  namespace emscripten {
      ////////////////////////////////////////////////////////////////////////////////
      // POLICIES
      ////////////////////////////////////////////////////////////////////////////////
  
      template<int Index>
      struct arg {
          static constexpr int index = Index + 1;
      };
  
      struct ret_val {
          static constexpr int index = 0;
      };
  
      /*
      template<typename Slot>
      struct allow_raw_pointer {
          template<typename InputType, int Index>
          struct Transform {
              typedef typename std::conditional<
                  Index == Slot::index,
                  internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>,
                  InputType
              >::type type;
          };
      };
      */
  
      // whitelist all raw pointers
      struct allow_raw_pointers {
          template<typename InputType, int Index>
          struct Transform {
              typedef typename std::conditional<
                  std::is_pointer<InputType>::value,
                  internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>,
                  InputType
              >::type type;
          };
      };
  
      // this is temporary until arg policies are reworked
      template<typename Slot>
      struct allow_raw_pointer : public allow_raw_pointers {
      };
  
      ////////////////////////////////////////////////////////////////////////////////
      // select_overload and select_const
      ////////////////////////////////////////////////////////////////////////////////
  
      template<typename Signature>
      Signature* select_overload(Signature* fn) {
          return fn;
      }
  
      template<typename Signature, typename ClassType>
      auto select_overload(Signature (ClassType::*fn)) -> decltype(fn) {
          return fn;
      }
  
      template<typename ClassType, typename ReturnType, typename... Args>
      auto select_const(ReturnType (ClassType::*method)(Args...) const) -> decltype(method) {
          return method;
      }
  
      namespace internal {        
          // this should be in <type_traits>, but alas, it's not
          template<typename T> struct remove_class;
          template<typename C, typename R, typename... A>
          struct remove_class<R(C::*)(A...)> { using type = R(A...); };
          template<typename C, typename R, typename... A>
          struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
          template<typename C, typename R, typename... A>
          struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
          template<typename C, typename R, typename... A>
          struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };
  
          template<typename LambdaType>
          using LambdaSignature = typename remove_class<
              decltype(&LambdaType::operator())
          >::type;
      }
  
      // requires captureless lambda because implicitly coerces to function pointer
      template<typename LambdaType>
      internal::LambdaSignature<LambdaType>* optional_override(const LambdaType& fp) {
          return fp;
      }
  
      ////////////////////////////////////////////////////////////////////////////////
      // Invoker
      ////////////////////////////////////////////////////////////////////////////////
  
      namespace internal {
          template<typename ReturnType, typename... Args>
          struct Invoker {
              static typename internal::BindingType<ReturnType>::WireType invoke(
                  ReturnType (*fn)(Args...),
                  typename internal::BindingType<Args>::WireType... args
              ) {
                  return internal::BindingType<ReturnType>::toWireType(
                      fn(
                          internal::BindingType<Args>::fromWireType(args)...
                      )
                  );
              }
          };
  
          template<typename... Args>
          struct Invoker<void, Args...> {
              static void invoke(
                  void (*fn)(Args...),
                  typename internal::BindingType<Args>::WireType... args
              ) {
                  return fn(
                      internal::BindingType<Args>::fromWireType(args)...
                  );
              }
          };
      }
  
      ////////////////////////////////////////////////////////////////////////////////
      // SignatureCode, SignatureString
      ////////////////////////////////////////////////////////////////////////////////
  
      namespace internal {
          template<typename T>
          struct SignatureCode {};
  
          template<>
          struct SignatureCode<int> {
              static constexpr char get() {
                  return 'i';
              }
          };
  
          template<>
          struct SignatureCode<void> {
              static constexpr char get() {
                  return 'v';
              }
          };
  
          template<>
          struct SignatureCode<float> {
              static constexpr char get() {
                  return 'f';
              }
          };
  
          template<>
          struct SignatureCode<double> {
              static constexpr char get() {
                  return 'd';
              }
          };
  
          template<typename... Args>
          const char* getGenericSignature() {
              static constexpr char signature[] = { SignatureCode<Args>::get()..., 0 };
              return signature;
          }
  
          template<typename T> struct SignatureTranslator { using type = int; };
          template<> struct SignatureTranslator<void> { using type = void; };
          template<> struct SignatureTranslator<float> { using type = float; };
          template<> struct SignatureTranslator<double> { using type = double; };
  
          template<typename... Args>
          EMSCRIPTEN_ALWAYS_INLINE const char* getSpecificSignature() {
              return getGenericSignature<typename SignatureTranslator<Args>::type...>();
          }
  
          template<typename Return, typename... Args>
          EMSCRIPTEN_ALWAYS_INLINE const char* getSignature(Return (*)(Args...)) {
              return getSpecificSignature<Return, Args...>();
          }
      }
  
      ////////////////////////////////////////////////////////////////////////////////
      // FUNCTIONS
      ////////////////////////////////////////////////////////////////////////////////
  
      extern "C" {
          void* __getDynamicPointerType(void* p);
      }
  
      template<typename ReturnType, typename... Args, typename... Policies>
      void function(const char* name, ReturnType (*fn)(Args...), Policies...) {
          using namespace internal;
          typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
          auto invoker = &Invoker<ReturnType, Args...>::invoke;
          _embind_register_function(
              name,
              args.getCount(),
              args.getTypes(),
              getSignature(invoker),
              reinterpret_cast<GenericFunction>(invoker),
              reinterpret_cast<GenericFunction>(fn));
      }
  
      namespace internal {
          template<typename ClassType, typename... Args>
          ClassType* operator_new(Args&&... args) {
              return new ClassType(std::forward<Args>(args)...);
          }
  
          template<typename WrapperType, typename ClassType, typename... Args>
          WrapperType wrapped_new(Args&&... args) {
              return WrapperType(new ClassType(std::forward<Args>(args)...));
          }
  
          template<typename ClassType, typename... Args>
          ClassType* raw_constructor(
              typename internal::BindingType<Args>::WireType... args
          ) {
              return new ClassType(
                  internal::BindingType<Args>::fromWireType(args)...
              );
          }
  
          template<typename ClassType>
          void raw_destructor(ClassType* ptr) {
              delete ptr;
          }
  
          template<typename FunctionPointerType, typename ReturnType, typename ThisType, typename... Args>
          struct FunctionInvoker {
              static typename internal::BindingType<ReturnType>::WireType invoke(
                  FunctionPointerType* function,
                  typename internal::BindingType<ThisType>::WireType wireThis,
                  typename internal::BindingType<Args>::WireType... args
              ) {
                  return internal::BindingType<ReturnType>::toWireType(
                      (*function)(
                          internal::BindingType<ThisType>::fromWireType(wireThis),
                          internal::BindingType<Args>::fromWireType(args)...)
                  );
              }
          };
  
          template<typename FunctionPointerType, typename ThisType, typename... Args>
          struct FunctionInvoker<FunctionPointerType, void, ThisType, Args...> {
              static void invoke(
                  FunctionPointerType* function,
                  typename internal::BindingType<ThisType>::WireType wireThis,
                  typename internal::BindingType<Args>::WireType... args
              ) {
                  (*function)(
                      internal::BindingType<ThisType>::fromWireType(wireThis),
                      internal::BindingType<Args>::fromWireType(args)...);
              }
          };
  
          template<typename MemberPointer,
                   typename ReturnType,
                   typename ThisType,
                   typename... Args>
          struct MethodInvoker {
              static typename internal::BindingType<ReturnType>::WireType invoke(
                  const MemberPointer& method,
                  typename internal::BindingType<ThisType>::WireType wireThis,
                  typename internal::BindingType<Args>::WireType... args
              ) {
                  return internal::BindingType<ReturnType>::toWireType(
                      (internal::BindingType<ThisType>::fromWireType(wireThis)->*method)(
                          internal::BindingType<Args>::fromWireType(args)...
                      )
                  );
              }
          };
  
          template<typename MemberPointer,
                   typename ThisType,
                   typename... Args>
          struct MethodInvoker<MemberPointer, void, ThisType, Args...> {
              static void invoke(
                  const MemberPointer& method,
                  typename internal::BindingType<ThisType>::WireType wireThis,
                  typename internal::BindingType<Args>::WireType... args
              ) {
                  return (internal::BindingType<ThisType>::fromWireType(wireThis)->*method)(
                      internal::BindingType<Args>::fromWireType(args)...
                  );
              }
          };
  
          template<typename InstanceType, typename MemberType>
          struct MemberAccess {
              typedef MemberType InstanceType::*MemberPointer;
              typedef internal::BindingType<MemberType> MemberBinding;
              typedef typename MemberBinding::WireType WireType;
              
              template<typename ClassType>
              static WireType getWire(
                  const MemberPointer& field,
                  const ClassType& ptr
              ) {
                  return MemberBinding::toWireType(ptr.*field);
              }
              
              template<typename ClassType>
              static void setWire(
                  const MemberPointer& field,
                  ClassType& ptr,
                  WireType value
              ) {
                  ptr.*field = MemberBinding::fromWireType(value);
              }
          };
  
          template<typename FieldType>
          struct GlobalAccess {
              typedef internal::BindingType<FieldType> MemberBinding;
              typedef typename MemberBinding::WireType WireType;
  
              static WireType get(FieldType* context) {
                  return MemberBinding::toWireType(*context);
              }
  
              static void set(FieldType* context, WireType value) {
                  *context = MemberBinding::fromWireType(value);
              }
          };
  
          // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*)
          template<typename T>
          inline T* getContext(const T& t) {
              // not a leak because this is called once per binding
              return new T(t);
          }
  
          template<typename T>
          struct GetterPolicy;
  
          template<typename GetterReturnType, typename GetterThisType>
          struct GetterPolicy<GetterReturnType (GetterThisType::*)() const> {
              typedef GetterReturnType ReturnType;
              typedef GetterReturnType (GetterThisType::*Context)() const;
  
              typedef internal::BindingType<ReturnType> Binding;
              typedef typename Binding::WireType WireType;
  
              template<typename ClassType>
              static WireType get(const Context& context, const ClassType& ptr) {
                  return Binding::toWireType((ptr.*context)());
              }
  
              static void* getContext(Context context) {
                  return internal::getContext(context);
              }
          };
  
          template<typename GetterReturnType, typename GetterThisType>
          struct GetterPolicy<GetterReturnType (*)(const GetterThisType&)> {
              typedef GetterReturnType ReturnType;
              typedef GetterReturnType (*Context)(const GetterThisType&);
  
              typedef internal::BindingType<ReturnType> Binding;
              typedef typename Binding::WireType WireType;
  
              template<typename ClassType>
              static WireType get(const Context& context, const ClassType& ptr) {
                  return Binding::toWireType(context(ptr));
              }
  
              static void* getContext(Context context) {
                  return internal::getContext(context);
              }
          };
  
          template<typename T>
          struct SetterPolicy;
  
          template<typename SetterThisType, typename SetterArgumentType>
          struct SetterPolicy<void (SetterThisType::*)(SetterArgumentType)> {
              typedef SetterArgumentType ArgumentType;
              typedef void (SetterThisType::*Context)(SetterArgumentType);
  
              typedef internal::BindingType<SetterArgumentType> Binding;
              typedef typename Binding::WireType WireType;
  
              template<typename ClassType>
              static void set(const Context& context, ClassType& ptr, WireType wt) {
                  (ptr.*context)(Binding::fromWireType(wt));
              }
  
              static void* getContext(Context context) {
                  return internal::getContext(context);
              }
          };
  
          template<typename SetterThisType, typename SetterArgumentType>
          struct SetterPolicy<void (*)(SetterThisType&, SetterArgumentType)> {
              typedef SetterArgumentType ArgumentType;
              typedef void (*Context)(SetterThisType&, SetterArgumentType);
  
              typedef internal::BindingType<SetterArgumentType> Binding;
              typedef typename Binding::WireType WireType;
  
              template<typename ClassType>
              static void set(const Context& context, ClassType& ptr, WireType wt) {
                  context(ptr, Binding::fromWireType(wt));
              }
  
              static void* getContext(Context context) {
                  return internal::getContext(context);
              }
          };
  
          class noncopyable {
          protected:
              noncopyable() {}
              ~noncopyable() {}
          private:
              noncopyable(const noncopyable&) = delete;
              const noncopyable& operator=(const noncopyable&) = delete;
          };
  
          template<typename ClassType, typename ElementType>
          typename BindingType<ElementType>::WireType get_by_index(int index, ClassType& ptr) {
              return BindingType<ElementType>::toWireType(ptr[index]);
          }
  
          template<typename ClassType, typename ElementType>
          void set_by_index(int index, ClassType& ptr, typename BindingType<ElementType>::WireType wt) {
              ptr[index] = BindingType<ElementType>::fromWireType(wt);
          }
      }
  
      template<int Index>
      struct index {
      };
  
      ////////////////////////////////////////////////////////////////////////////////
      // VALUE TUPLES
      ////////////////////////////////////////////////////////////////////////////////
  
      template<typename ClassType>
      class value_array : public internal::noncopyable {
      public:
          typedef ClassType class_type;
  
          value_array(const char* name) {
              using namespace internal;
  
              auto constructor = &raw_constructor<ClassType>;
              auto destructor = &raw_destructor<ClassType>;
              _embind_register_value_array(
                  TypeID<ClassType>::get(),
                  name,
                  getSignature(constructor),
                  reinterpret_cast<GenericFunction>(constructor),
                  getSignature(destructor),
                  reinterpret_cast<GenericFunction>(destructor));
          }
  
          ~value_array() {
              using namespace internal;
              _embind_finalize_value_array(TypeID<ClassType>::get());
          }
  
          template<typename InstanceType, typename ElementType>
          value_array& element(ElementType InstanceType::*field) {
              using namespace internal;
  
              auto getter = &MemberAccess<InstanceType, ElementType>
                  ::template getWire<ClassType>;
              auto setter = &MemberAccess<InstanceType, ElementType>
                  ::template setWire<ClassType>;
  
              _embind_register_value_array_element(
                  TypeID<ClassType>::get(),
                  TypeID<ElementType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  getContext(field),
                  TypeID<ElementType>::get(),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter),
                  getContext(field));
              return *this;
          }
  
          template<typename Getter, typename Setter>
          value_array& element(Getter getter, Setter setter) {
              using namespace internal;
              typedef GetterPolicy<Getter> GP;
              typedef SetterPolicy<Setter> SP;
  
              auto g = &GP::template get<ClassType>;
              auto s = &SP::template set<ClassType>;
  
              _embind_register_value_array_element(
                  TypeID<ClassType>::get(),
                  TypeID<typename GP::ReturnType>::get(),
                  getSignature(g),
                  reinterpret_cast<GenericFunction>(g),
                  GP::getContext(getter),
                  TypeID<typename SP::ArgumentType>::get(),
                  getSignature(s),
                  reinterpret_cast<GenericFunction>(s),
                  SP::getContext(setter));
              return *this;
          }
  
          template<int Index>
          value_array& element(index<Index>) {
              using namespace internal;
              ClassType* null = 0;
              typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType;
              auto getter = &internal::get_by_index<ClassType, ElementType>;
              auto setter = &internal::set_by_index<ClassType, ElementType>;
  
              _embind_register_value_array_element(
                  TypeID<ClassType>::get(),
                  TypeID<ElementType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  reinterpret_cast<void*>(Index),
                  TypeID<ElementType>::get(),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter),
                  reinterpret_cast<void*>(Index));
              return *this;
          }
      };
  
      ////////////////////////////////////////////////////////////////////////////////
      // VALUE STRUCTS
      ////////////////////////////////////////////////////////////////////////////////
  
      template<typename ClassType>
      class value_object : public internal::noncopyable {
      public:
          typedef ClassType class_type;
  
          value_object(const char* name) {
              using namespace internal;
  
              auto ctor = &raw_constructor<ClassType>;
              auto dtor = &raw_destructor<ClassType>;
  
              _embind_register_value_object(
                  TypeID<ClassType>::get(),
                  name,
                  getSignature(ctor),
                  reinterpret_cast<GenericFunction>(ctor),
                  getSignature(dtor),
                  reinterpret_cast<GenericFunction>(dtor));
          }
  
          ~value_object() {
              using namespace internal;
              _embind_finalize_value_object(internal::TypeID<ClassType>::get());
          }
  
          template<typename InstanceType, typename FieldType>
          value_object& field(const char* fieldName, FieldType InstanceType::*field) {
              using namespace internal;
  
              auto getter = &MemberAccess<InstanceType, FieldType>
                  ::template getWire<ClassType>;
              auto setter = &MemberAccess<InstanceType, FieldType>
                  ::template setWire<ClassType>;
  
              _embind_register_value_object_field(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<FieldType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  getContext(field),
                  TypeID<FieldType>::get(),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter),
                  getContext(field));
              return *this;
          }
  
          template<typename InstanceType, typename ElementType, int N>
          value_object& field(const char* fieldName, ElementType (InstanceType::*field)[N]) {
              using namespace internal;
  
              typedef std::array<ElementType, N> FieldType;
              static_assert(sizeof(FieldType) == sizeof(ElementType[N]));
  
              auto getter = &MemberAccess<InstanceType, FieldType>
                  ::template getWire<ClassType>;
              auto setter = &MemberAccess<InstanceType, FieldType>
                  ::template setWire<ClassType>;
  
              _embind_register_value_object_field(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<FieldType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  getContext(field),
                  TypeID<FieldType>::get(),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter),
                  getContext(field));
              return *this;
          }
  
          template<typename Getter, typename Setter>
          value_object& field(
              const char* fieldName,
              Getter getter,
              Setter setter
          ) {
              using namespace internal;
              typedef GetterPolicy<Getter> GP;
              typedef SetterPolicy<Setter> SP;
  
              auto g = &GP::template get<ClassType>;
              auto s = &SP::template set<ClassType>;
  
              _embind_register_value_object_field(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<typename GP::ReturnType>::get(),
                  getSignature(g),
                  reinterpret_cast<GenericFunction>(g),
                  GP::getContext(getter),
                  TypeID<typename SP::ArgumentType>::get(),
                  getSignature(s),
                  reinterpret_cast<GenericFunction>(s),
                  SP::getContext(setter));
              return *this;
          }
  
          template<int Index>
          value_object& field(const char* fieldName, index<Index>) {
              using namespace internal;
              ClassType* null = 0;
              typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType;
  
              auto getter = &internal::get_by_index<ClassType, ElementType>;
              auto setter = &internal::set_by_index<ClassType, ElementType>;
  
              _embind_register_value_object_field(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<ElementType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  reinterpret_cast<void*>(Index),
                  TypeID<ElementType>::get(),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter),
                  reinterpret_cast<void*>(Index));
              return *this;
          }
      };
  
      ////////////////////////////////////////////////////////////////////////////////
      // SMART POINTERS
      ////////////////////////////////////////////////////////////////////////////////
  
      template<typename PointerType>
      struct default_smart_ptr_trait {
          static sharing_policy get_sharing_policy() {
              return sharing_policy::NONE;
          }
  
          static void* share(void* v) {
              return 0; // no sharing
          }
  
          static PointerType* construct_null() {
              return new PointerType;
          }
      };
  
      // specialize if you have a different pointer type
      template<typename PointerType>
      struct smart_ptr_trait : public default_smart_ptr_trait<PointerType> {
          typedef typename PointerType::element_type element_type;
  
          static element_type* get(const PointerType& ptr) {
              return ptr.get();
          }
      };
  
      template<typename PointeeType>
      struct smart_ptr_trait<std::shared_ptr<PointeeType>> {
          typedef std::shared_ptr<PointeeType> PointerType;
          typedef typename PointerType::element_type element_type;
  
          static element_type* get(const PointerType& ptr) {
              return ptr.get();
          }
  
          static sharing_policy get_sharing_policy() {
              return sharing_policy::BY_EMVAL;
          }
  
          static std::shared_ptr<PointeeType>* share(PointeeType* p, internal::EM_VAL v) {
              return new std::shared_ptr<PointeeType>(
                  p,
                  val_deleter(val::take_ownership(v)));
          }
  
          static PointerType* construct_null() {
              return new PointerType;
          }
  
      private:
          class val_deleter {
          public:
              val_deleter() = delete;
              explicit val_deleter(val v)
                  : v(v)
              {}
              void operator()(void const*) {
                  v();
                  // eventually we'll need to support emptied out val
                  v = val::undefined();
              }
          private:
              val v;
          };
      };
  
  
      ////////////////////////////////////////////////////////////////////////////////
      // CLASSES
      ////////////////////////////////////////////////////////////////////////////////
  
      namespace internal {
          class WrapperBase {
          public:
              void setNotifyJSOnDestruction(bool notify) {
                  notifyJSOnDestruction = notify;
              }
  
          protected:
              bool notifyJSOnDestruction = false;
          };
      }
  
      // abstract classes
      template<typename T>
      class wrapper : public T, public internal::WrapperBase {
      public:
          typedef T class_type;
  
          template<typename... Args>
          explicit wrapper(val&& wrapped, Args&&... args)
              : T(std::forward<Args>(args)...)
              , wrapped(std::forward<val>(wrapped))
          {}
  
          ~wrapper() {
              if (notifyJSOnDestruction) {
                  call<void>("__destruct");
              }
          }
  
          template<typename ReturnType, typename... Args>
          ReturnType call(const char* name, Args&&... args) const {
              return wrapped.call<ReturnType>(name, std::forward<Args>(args)...);
          }
  
      private:
          val wrapped;
      };
  
  #define EMSCRIPTEN_WRAPPER(T)                                           \
      template<typename... Args>                                          \
      T(::emscripten::val&& v, Args&&... args)                            \
          : wrapper(std::forward<::emscripten::val>(v), std::forward<Args>(args)...) \
      {}
  
      namespace internal {
          struct NoBaseClass {
              template<typename ClassType>
              static void verify() {
              }
  
              static TYPEID get() {
                  return nullptr;
              }
  
              template<typename ClassType>
              static VoidFunctionPtr getUpcaster() {
                  return nullptr;
              }
  
              template<typename ClassType>
              static VoidFunctionPtr getDowncaster() {
                  return nullptr;
              }
          };
  
          // NOTE: this returns the class type, not the pointer type
          template<typename T>
          inline TYPEID getActualType(T* ptr) {
              return getLightTypeID(*ptr);
          };
      }
  
      template<typename BaseClass>
      struct base {
          typedef BaseClass class_type;
  
          template<typename ClassType>
          static void verify() {
              static_assert(!std::is_same<ClassType, BaseClass>::value, "Base must not have same type as class");
              static_assert(std::is_base_of<BaseClass, ClassType>::value, "Derived class must derive from base");
          }
  
          static internal::TYPEID get() {
              return internal::TypeID<BaseClass>::get();
          }
          
          template<typename ClassType>
          using Upcaster = BaseClass* (*)(ClassType*);
  
          template<typename ClassType>
          using Downcaster = ClassType* (*)(BaseClass*);
          
          template<typename ClassType>
          static Upcaster<ClassType> getUpcaster() {
              return &convertPointer<ClassType, BaseClass>;
          }
          
          template<typename ClassType>
          static Downcaster<ClassType> getDowncaster() {
              return &convertPointer<BaseClass, ClassType>;
          }
  
          template<typename From, typename To>
          static To* convertPointer(From* ptr) {
              return static_cast<To*>(ptr);
          }
      };
  
      namespace internal {
          template<typename WrapperType>
          val wrapped_extend(const std::string& name, const val& properties) {
              return val::take_ownership(_embind_create_inheriting_constructor(
                  name.c_str(),
                  TypeID<WrapperType>::get(),
                  properties.__get_handle()));
          }
      };
  
      struct pure_virtual {
          template<typename InputType, int Index>
          struct Transform {
              typedef InputType type;
          };
      };
  
      namespace internal {
          template<typename... Policies>
          struct isPureVirtual;
  
          template<typename... Rest>
          struct isPureVirtual<pure_virtual, Rest...> {
              static constexpr bool value = true;
          };
  
          template<typename T, typename... Rest>
          struct isPureVirtual<T, Rest...> {
              static constexpr bool value = isPureVirtual<Rest...>::value;
          };
  
          template<>
          struct isPureVirtual<> {
              static constexpr bool value = false;
          };
      }
  
      template<typename... ConstructorArgs>
      struct constructor {
      };
  
      template<typename ClassType, typename BaseSpecifier = internal::NoBaseClass>
      class class_ {
      public:
          typedef ClassType class_type;
          typedef BaseSpecifier base_specifier;
  
          class_() = delete;
  
          EMSCRIPTEN_ALWAYS_INLINE explicit class_(const char* name) {
              using namespace internal;
  
              BaseSpecifier::template verify<ClassType>();
  
              auto _getActualType = &getActualType<ClassType>;
              auto upcast   = BaseSpecifier::template getUpcaster<ClassType>();
              auto downcast = BaseSpecifier::template getDowncaster<ClassType>();
              auto destructor = &raw_destructor<ClassType>;
  
              _embind_register_class(
                  TypeID<ClassType>::get(),
                  TypeID<AllowedRawPointer<ClassType>>::get(),
                  TypeID<AllowedRawPointer<const ClassType>>::get(),
                  BaseSpecifier::get(),
                  getSignature(_getActualType),
                  reinterpret_cast<GenericFunction>(_getActualType),
                  getSignature(upcast),
                  reinterpret_cast<GenericFunction>(upcast),
                  getSignature(downcast),
                  reinterpret_cast<GenericFunction>(downcast),
                  name,
                  getSignature(destructor),
                  reinterpret_cast<GenericFunction>(destructor));
          }
  
          template<typename PointerType>
          EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr(const char* name) const {
              using namespace internal;
  
              typedef smart_ptr_trait<PointerType> PointerTrait;
              typedef typename PointerTrait::element_type PointeeType;
              
              static_assert(std::is_same<ClassType, typename std::remove_cv<PointeeType>::type>::value, "smart pointer must point to this class");
  
              auto get = &PointerTrait::get;
              auto construct_null = &PointerTrait::construct_null;
              auto share = &PointerTrait::share;
              auto destructor = &raw_destructor<PointerType>;
  
              _embind_register_smart_ptr(
                  TypeID<PointerType>::get(),
                  TypeID<PointeeType>::get(),
                  name,
                  PointerTrait::get_sharing_policy(),
                  getSignature(get),
                  reinterpret_cast<GenericFunction>(get),
                  getSignature(construct_null),
                  reinterpret_cast<GenericFunction>(construct_null),
                  getSignature(share),
                  reinterpret_cast<GenericFunction>(share),
                  getSignature(destructor),
                  reinterpret_cast<GenericFunction>(destructor));
              return *this;
          };
  
          template<typename... ConstructorArgs, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Policies... policies) const {
              return constructor(
                  &internal::operator_new<ClassType, ConstructorArgs...>,
                  policies...);
          }
  
          template<typename... Args, typename ReturnType, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const {
              using namespace internal;
  
              // TODO: allows all raw pointers... policies need a rethink
              typename WithPolicies<allow_raw_pointers, Policies...>::template ArgTypeList<ReturnType, Args...> args;
              auto invoke = &Invoker<ReturnType, Args...>::invoke;
              _embind_register_class_constructor(
                  TypeID<ClassType>::get(),
                  args.getCount(),
                  args.getTypes(),
                  getSignature(invoke),
                  reinterpret_cast<GenericFunction>(invoke),
                  reinterpret_cast<GenericFunction>(factory));
              return *this;
          }
  
          template<typename SmartPtr, typename... Args, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr_constructor(const char* smartPtrName, SmartPtr (*factory)(Args...), Policies...) const {
              using namespace internal;
  
              smart_ptr<SmartPtr>(smartPtrName);
  
              typename WithPolicies<Policies...>::template ArgTypeList<SmartPtr, Args...> args;
              auto invoke = &Invoker<SmartPtr, Args...>::invoke;
              _embind_register_class_constructor(
                  TypeID<ClassType>::get(),
                  args.getCount(),
                  args.getTypes(),
                  getSignature(invoke),
                  reinterpret_cast<GenericFunction>(invoke),
                  reinterpret_cast<GenericFunction>(factory));
              return *this;
          }
  
          template<typename WrapperType, typename... ConstructorArgs>
          EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass(
              const char* wrapperClassName,
              ::emscripten::constructor<ConstructorArgs...> = ::emscripten::constructor<>()
          ) const {
              using namespace internal;
  
              auto cls = class_<WrapperType, base<ClassType>>(wrapperClassName)
                  .function("notifyOnDestruction", select_overload<void(WrapperType&)>([](WrapperType& wrapper) {
                      wrapper.setNotifyJSOnDestruction(true);
                  }))
                  ;
  
              return
                  class_function(
                      "implement",
                      &wrapped_new<WrapperType*, WrapperType, val, ConstructorArgs...>,
                      allow_raw_pointer<ret_val>())
                  .class_function(
                      "extend",
                      &wrapped_extend<WrapperType>)
                  ;
          }
  
          template<typename WrapperType, typename PointerType, typename... ConstructorArgs>
          EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass(
              const char* wrapperClassName,
              const char* pointerName,
              ::emscripten::constructor<ConstructorArgs...> = ::emscripten::constructor<>()
          ) const {
              using namespace internal;
  
              auto cls = class_<WrapperType, base<ClassType>>(wrapperClassName)
                  .function("notifyOnDestruction", select_overload<void(WrapperType&)>([](WrapperType& wrapper) {
                      wrapper.setNotifyJSOnDestruction(true);
                  }))
                  .template smart_ptr<PointerType>(pointerName)
                  ;
  
              return
                  class_function(
                      "implement",
                      &wrapped_new<PointerType, WrapperType, val, ConstructorArgs...>,
                      allow_raw_pointer<ret_val>())
                  .class_function(
                      "extend",
                      &wrapped_extend<WrapperType>)
                  ;
          }
  
          template<typename ReturnType, typename... Args, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) const {
              using namespace internal;
  
              auto invoker = &MethodInvoker<decltype(memberFunction), ReturnType, ClassType*, Args...>::invoke;
  
              typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<ClassType>, Args...> args;
              _embind_register_class_function(
                  TypeID<ClassType>::get(),
                  methodName,
                  args.getCount(),
                  args.getTypes(),
                  getSignature(invoker),
                  reinterpret_cast<GenericFunction>(invoker),
                  getContext(memberFunction),
                  isPureVirtual<Policies...>::value);
              return *this;
          }
  
          template<typename ReturnType, typename... Args, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) const {
              using namespace internal;
  
              auto invoker = &MethodInvoker<decltype(memberFunction), ReturnType, const ClassType*, Args...>::invoke;
  
              typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<const ClassType>, Args...> args;
              _embind_register_class_function(
                  TypeID<ClassType>::get(),
                  methodName,
                  args.getCount(),
                  args.getTypes(),
                  getSignature(invoker),
                  reinterpret_cast<GenericFunction>(invoker),
                  getContext(memberFunction),
                  isPureVirtual<Policies...>::value);
              return *this;
          }
  
          template<typename ReturnType, typename ThisType, typename... Args, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (*function)(ThisType, Args...), Policies...) const {
              using namespace internal;
  
              typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, ThisType, Args...> args;
              auto invoke = &FunctionInvoker<decltype(function), ReturnType, ThisType, Args...>::invoke;
              _embind_register_class_function(
                  TypeID<ClassType>::get(),
                  methodName,
                  args.getCount(),
                  args.getTypes(),
                  getSignature(invoke),
                  reinterpret_cast<GenericFunction>(invoke),
                  getContext(function),
                  false);
              return *this;
          }
  
          template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type>
          EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, const FieldType ClassType::*field) const {
              using namespace internal;
              
              auto getter = &MemberAccess<ClassType, FieldType>::template getWire<ClassType>;
              _embind_register_class_property(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<FieldType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  getContext(field),
                  0,
                  0,
                  0,
                  0);
              return *this;
          }
  
          template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type>
          EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, FieldType ClassType::*field) const {
              using namespace internal;
  
              auto getter = &MemberAccess<ClassType, FieldType>::template getWire<ClassType>;
              auto setter = &MemberAccess<ClassType, FieldType>::template setWire<ClassType>;
              _embind_register_class_property(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<FieldType>::get(),
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  getContext(field),
                  TypeID<FieldType>::get(),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter),
                  getContext(field));
              return *this;
          }
  
          template<typename Getter>
          EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter) const {
              using namespace internal;
              typedef GetterPolicy<Getter> GP;
              auto gter = &GP::template get<ClassType>;
              _embind_register_class_property(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<typename GP::ReturnType>::get(),
                  getSignature(gter),
                  reinterpret_cast<GenericFunction>(gter),
                  GP::getContext(getter),
                  0,
                  0,
                  0,
                  0);
              return *this;
          }
  
          template<typename Getter, typename Setter>
          EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter, Setter setter) const {
              using namespace internal;
              typedef GetterPolicy<Getter> GP;
              typedef SetterPolicy<Setter> SP;
  
              auto gter = &GP::template get<ClassType>;
              auto ster = &SP::template set<ClassType>;
  
              _embind_register_class_property(
                  TypeID<ClassType>::get(),
                  fieldName,
                  TypeID<typename GP::ReturnType>::get(),
                  getSignature(gter),
                  reinterpret_cast<GenericFunction>(gter),
                  GP::getContext(getter),
                  TypeID<typename SP::ArgumentType>::get(),
                  getSignature(ster),
                  reinterpret_cast<GenericFunction>(ster),
                  SP::getContext(setter));
              return *this;
          }
  
          template<typename ReturnType, typename... Args, typename... Policies>
          EMSCRIPTEN_ALWAYS_INLINE const class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) const {
              using namespace internal;
  
              typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
              auto invoke = &internal::Invoker<ReturnType, Args...>::invoke;
              _embind_register_class_class_function(
                  TypeID<ClassType>::get(),
                  methodName,
                  args.getCount(),
                  args.getTypes(),
                  getSignature(invoke),
                  reinterpret_cast<GenericFunction>(invoke),
                  reinterpret_cast<GenericFunction>(classMethod));
              return *this;
          }
  
          template<typename FieldType>
          EMSCRIPTEN_ALWAYS_INLINE const class_& class_property(const char* name, const FieldType* field) const {
              using namespace internal;
  
              auto getter = &GlobalAccess<FieldType>::get;
              _embind_register_class_class_property(
                  TypeID<ClassType>::get(),
                  name,
                  TypeID<FieldType>::get(),
                  field,
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  0,
                  0);
              return *this;
          }
  
          template<typename FieldType>
          EMSCRIPTEN_ALWAYS_INLINE const class_& class_property(const char* name, FieldType* field) const {
              using namespace internal;
  
              auto getter = &GlobalAccess<FieldType>::get;
              auto setter = &GlobalAccess<FieldType>::set;
              _embind_register_class_class_property(
                  TypeID<ClassType>::get(),
                  name,
                  TypeID<FieldType>::get(),
                  field,
                  getSignature(getter),
                  reinterpret_cast<GenericFunction>(getter),
                  getSignature(setter),
                  reinterpret_cast<GenericFunction>(setter));
              return *this;
          }
      };
  
      ////////////////////////////////////////////////////////////////////////////////
      // VECTORS
      ////////////////////////////////////////////////////////////////////////////////
  
      namespace internal {
          template<typename VectorType>
          struct VectorAccess {
              static val get(
                  const VectorType& v,
                  typename VectorType::size_type index
              ) {
                  if (index < v.size()) {
                      return val(v[index]);
                  } else {
                      return val::undefined();
                  }
              }
  
              static bool set(
                  VectorType& v,
                  typename VectorType::size_type index,
                  const typename VectorType::value_type& value
              ) {
                  v[index] = value;
                  return true;
              }
          };
      }
  
      template<typename T>
      class_<std::vector<T>> register_vector(const char* name) {
          typedef std::vector<T> VecType;
  
          void (VecType::*push_back)(const T&) = &VecType::push_back;
          void (VecType::*resize)(const size_t, const T&) = &VecType::resize;
          return class_<std::vector<T>>(name)
              .template constructor<>()
              .function("push_back", push_back)
              .function("resize", resize)
              .function("size", &VecType::size)
              .function("get", &internal::VectorAccess<VecType>::get)
              .function("set", &internal::VectorAccess<VecType>::set)
              ;
      }
  
      ////////////////////////////////////////////////////////////////////////////////
      // MAPS
      ////////////////////////////////////////////////////////////////////////////////
  
      namespace internal {
          template<typename MapType>
          struct MapAccess {
              static val get(
                  const MapType& m,
                  const typename MapType::key_type& k
              ) {
                  auto i = m.find(k);
                  if (i == m.end()) {
                      return val::undefined();
                  } else {
                      return val(i->second);
                  }
              }
  
              static void set(
                  MapType& m,
                  const typename MapType::key_type& k,
                  const typename MapType::mapped_type& v
              ) {
                  m[k] = v;
              }
          };
      }
  
      template<typename K, typename V>
      class_<std::map<K, V>> register_map(const char* name) {
          typedef std::map<K,V> MapType;
  
          return class_<MapType>(name)
              .template constructor<>()
              .function("size", &MapType::size)
              .function("get", internal::MapAccess<MapType>::get)
              .function("set", internal::MapAccess<MapType>::set)
              ;
      }
  
  
      ////////////////////////////////////////////////////////////////////////////////
      // ENUMS
      ////////////////////////////////////////////////////////////////////////////////
  
      template<typename EnumType>
      class enum_ {
      public:
          typedef EnumType enum_type;
  
          enum_(const char* name) {
              using namespace internal;
              _embind_register_enum(
                  internal::TypeID<EnumType>::get(),
                  name,
                  sizeof(EnumType),
                  std::is_signed<typename std::underlying_type<EnumType>::type>::value);
          }
  
          enum_& value(const char* name, EnumType value) {
              using namespace internal;
              // TODO: there's still an issue here.
              // if EnumType is an unsigned long, then JS may receive it as a signed long
              static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue");
  
              _embind_register_enum_value(
                  internal::TypeID<EnumType>::get(),
                  name,
                  static_cast<internal::GenericEnumValue>(value));
              return *this;
          }
      };
  
      ////////////////////////////////////////////////////////////////////////////////
      // CONSTANTS
      ////////////////////////////////////////////////////////////////////////////////
  
      namespace internal {
          template<typename T>
          uintptr_t asGenericValue(T t) {
              return static_cast<uintptr_t>(t);
          }
  
          template<typename T>
          uintptr_t asGenericValue(T* p) {
              return reinterpret_cast<uintptr_t>(p);
          }
      }
  
      template<typename ConstantType>
      void constant(const char* name, const ConstantType& v) {
          using namespace internal;
          typedef BindingType<const ConstantType&> BT;
          _embind_register_constant(
              name,
              TypeID<const ConstantType&>::get(),
              asGenericValue(BT::toWireType(v)));
      }
  }
  
  #define EMSCRIPTEN_BINDINGS(name)                                       \
      static struct EmscriptenBindingInitializer_##name {                 \
          EmscriptenBindingInitializer_##name();                          \
      } EmscriptenBindingInitializer_##name##_instance;                   \
      EmscriptenBindingInitializer_##name::EmscriptenBindingInitializer_##name()
  
  #endif // ~C++11 version check
  