/** * Copyright (c) 2017 by Contributors * @file dgl/runtime/packed_func.h * @brief Type-erased function used across DGL API. */ #ifndef DGL_RUNTIME_PACKED_FUNC_H_ #define DGL_RUNTIME_PACKED_FUNC_H_ #include #include #include #include #include #include #include #include #include #include "c_runtime_api.h" #include "module.h" #include "ndarray.h" // Whether use DGL runtime in header only mode. #ifndef DGL_RUNTIME_HEADER_ONLY #define DGL_RUNTIME_HEADER_ONLY 0 #endif namespace dgl { namespace runtime { // Forward declare ObjectRef and Object for extensions. // This header works fine without depend on ObjectRef // as long as it is not used. class Object; class ObjectRef; // forward declarations class DGLArgs; class DGLArgValue; class DGLRetValue; class DGLArgsSetter; /** * @brief Packed function is a type-erased function. * The arguments are passed by packed format. * * This is an useful unified interface to call generated functions, * It is the unified function function type of DGL. * It corresponds to DGLFunctionHandle in C runtime API. */ class PackedFunc { public: /** * @brief The internal std::function * @param args The arguments to the function. * @param rv The return value. * * @code * // Example code on how to implemented FType * void MyPackedFunc(DGLArgs args, DGLRetValue* rv) { * // automatically convert arguments to desired type. * int a0 = args[0]; * float a1 = args[1]; * ... * // automatically assign values to rv * std::string my_return_value = "x"; * *rv = my_return_value; * } * @endcode */ using FType = std::function; /** @brief default constructor */ PackedFunc() {} /** * @brief constructing a packed function from a std::function. * @param body the internal container of packed function. */ explicit PackedFunc(FType body) : body_(body) {} /** * @brief Call packed function by directly passing in unpacked format. * @param args Arguments to be passed. * @tparam Args arguments to be passed. * * @code * // Example code on how to call packed function * void CallPacked(PackedFunc f) { * // call like normal functions by pass in arguments * // return value is automatically converted back * int rvalue = f(1, 2.0); * } * @endcode */ template inline DGLRetValue operator()(Args&&... args) const; /** * @brief Call the function in packed format. * @param args The arguments * @param rv The return value. */ inline void CallPacked(DGLArgs args, DGLRetValue* rv) const; /** @return the internal body function */ inline FType body() const; /** @return Whether the packed function is nullptr */ bool operator==(std::nullptr_t null) const { return body_ == nullptr; } /** @return Whether the packed function is not nullptr */ bool operator!=(std::nullptr_t null) const { return body_ != nullptr; } private: /** @brief internal container of packed function */ FType body_; }; /** * @brief Please refer to \ref TypedPackedFuncAnchor * "TypedPackedFunc" */ template class TypedPackedFunc; /** * @anchor TypedPackedFuncAnchor * @brief A PackedFunc wrapper to provide typed function signature. * It is backed by a PackedFunc internally. * * TypedPackedFunc enables compile time type checking. * TypedPackedFunc works with the runtime system: * - It can be passed as an argument of PackedFunc. * - It can be assigned to DGLRetValue. * - It can be directly converted to a type-erased PackedFunc. * * Developers should prefer TypedPackedFunc over PackedFunc in C++ code * as it enables compile time checking. * We can construct a TypedPackedFunc from a lambda function * with the same signature. * * @code * // user defined lambda function. * auto addone = [](int x)->int { * return x + 1; * }; * // We can directly convert * // lambda function to TypedPackedFunc * TypedPackedFunc ftyped(addone); * // invoke the function. * int y = ftyped(1); * // Can be directly converted to PackedFunc * PackedFunc packed = ftype; * @endcode * @tparam R The return value of the function. * @tparam Args The argument signature of the function. */ template class TypedPackedFunc { public: /** @brief short hand for this function type */ using TSelf = TypedPackedFunc; /** @brief default constructor */ TypedPackedFunc() {} /** * @brief construct by wrap a PackedFunc * * Example usage: * @code * PackedFunc packed([](DGLArgs args, DGLRetValue *rv) { * int x = args[0]; * *rv = x + 1; * }); * // construct from packed function * TypedPackedFunc ftyped(packed); * // call the typed version. * CHECK_EQ(ftyped(1), 2); * @endcode * * @param packed The packed function */ inline explicit TypedPackedFunc(PackedFunc packed); /** * @brief construct from a lambda function with the same signature. * * Example usage: * @code * auto typed_lambda = [](int x)->int { return x + 1; } * // construct from packed function * TypedPackedFunc ftyped(typed_lambda); * // call the typed version. * CHECK_EQ(ftyped(1), 2); * @endcode * * @param typed_lambda typed lambda function. * @tparam FLambda the type of the lambda function. */ template < typename FLambda, typename = typename std::enable_if >::value>::type> explicit TypedPackedFunc(const FLambda& typed_lambda) { this->AssignTypedLambda(typed_lambda); } /** * @brief copy assignment operator from typed lambda * * Example usage: * @code * // construct from packed function * TypedPackedFunc ftyped; * ftyped = [](int x) { return x + 1; } * // call the typed version. * CHECK_EQ(ftyped(1), 2); * @endcode * * @param typed_lambda typed lambda function. * @tparam FLambda the type of the lambda function. * @returns reference to self. */ template < typename FLambda, typename = typename std::enable_if >::value>::type> TSelf& operator=(FLambda typed_lambda) { // NOLINT(*) this->AssignTypedLambda(typed_lambda); return *this; } /** * @brief copy assignment operator from PackedFunc. * @param packed The packed function. * @returns reference to self. */ TSelf& operator=(PackedFunc packed) { packed_ = packed; return *this; } /** * @brief Invoke the operator. * @param args The arguments * @returns The return value. */ inline R operator()(Args... args) const; /** * @brief convert to PackedFunc * @return the internal PackedFunc */ operator PackedFunc() const { return packed(); } /** * @return reference the internal PackedFunc */ const PackedFunc& packed() const { return packed_; } private: friend class DGLRetValue; /** @brief The internal packed function */ PackedFunc packed_; /** * @brief Assign the packed field using a typed lambda function. * * @param flambda The lambda function. * @tparam FLambda The lambda function type. * @note We capture the lambda when possible for maximum efficiency. */ template inline void AssignTypedLambda(FLambda flambda); }; /** @brief Arguments into DGL functions. */ class DGLArgs { public: const DGLValue* values; const int* type_codes; int num_args; /** * @brief constructor * @param values The argument values * @param type_codes The argument type codes * @param num_args number of arguments. */ DGLArgs(const DGLValue* values, const int* type_codes, int num_args) : values(values), type_codes(type_codes), num_args(num_args) {} /** @return size of the arguments */ inline int size() const; /** * @brief Get i-th argument * @param i the index. * @return the ith argument. */ inline DGLArgValue operator[](int i) const; }; /** * @brief Type traits to mark if a class is dgl extension type. * * To enable extension type in C++ must be register () ed via marco. * DGL_REGISTER_EXT_TYPE(TypeName) after defining this with this traits. * * Extension class can be passed and returned via PackedFunc in all dgl runtime. * Internally extension class is stored as T*. * * @tparam T the typename */ template struct extension_class_info { static const int code = 0; }; /** * @brief Runtime function table about extension type. */ class ExtTypeVTable { public: /** @brief function to be called to delete a handle */ void (*destroy)(void* handle); /** @brief function to be called when clone a handle */ void* (*clone)(void* handle); /** * @brief Register type * @tparam T The type to be register. * @return The registered vtable. */ template static inline ExtTypeVTable* Register_(); /** * @brief Get a vtable based on type code. * @param type_code The type code * @return The registered vtable. */ DGL_DLL static ExtTypeVTable* Get(int type_code); private: // Internal registration function. DGL_DLL static ExtTypeVTable* RegisterInternal( int type_code, const ExtTypeVTable& vt); }; /** * @brief Internal base class to * handle conversion to POD values. */ class DGLPODValue_ { public: operator double() const { // Allow automatic conversion from int to float // This avoids errors when user pass in int from // the frontend while the API expects a float. if (type_code_ == kDGLInt) { return static_cast(value_.v_int64); } DGL_CHECK_TYPE_CODE(type_code_, kDGLFloat); return value_.v_float64; } operator int64_t() const { DGL_CHECK_TYPE_CODE(type_code_, kDGLInt); return value_.v_int64; } operator uint64_t() const { DGL_CHECK_TYPE_CODE(type_code_, kDGLInt); return value_.v_int64; } operator int() const { DGL_CHECK_TYPE_CODE(type_code_, kDGLInt); CHECK_LE(value_.v_int64, std::numeric_limits::max()); return static_cast(value_.v_int64); } operator bool() const { DGL_CHECK_TYPE_CODE(type_code_, kDGLInt); return value_.v_int64 != 0; } operator void*() const { if (type_code_ == kNull) return nullptr; if (type_code_ == kArrayHandle) return value_.v_handle; DGL_CHECK_TYPE_CODE(type_code_, kHandle); return value_.v_handle; } operator DGLArray*() const { if (type_code_ == kArrayHandle || type_code_ == kNDArrayContainer) { return static_cast(value_.v_handle); } else { if (type_code_ == kNull) return nullptr; LOG(FATAL) << "Expected " << "DGLArray* or NDArray but get " << TypeCode2Str(type_code_); return nullptr; } } operator NDArray() const { if (type_code_ == kNull) return NDArray(); DGL_CHECK_TYPE_CODE(type_code_, kNDArrayContainer); return NDArray(static_cast(value_.v_handle)); } operator DGLContext() const { DGL_CHECK_TYPE_CODE(type_code_, kDGLContext); return value_.v_ctx; } template const TExtension& AsExtension() const { CHECK_LT(type_code_, kExtEnd); return static_cast(value_.v_handle)[0]; } int type_code() const { return type_code_; } /** * @brief return handle as specific pointer type. * @tparam T the data type. * @return The pointer type. */ template T* ptr() const { return static_cast(value_.v_handle); } protected: friend class DGLArgsSetter; friend class DGLRetValue; DGLPODValue_() : type_code_(kNull) {} DGLPODValue_(DGLValue value, int type_code) : value_(value), type_code_(type_code) {} /** @brief The value */ DGLValue value_; /** @brief the type code */ int type_code_; }; /** * @brief A single argument value to PackedFunc. * Containing both type_code and DGLValue * * Provides utilities to do type cast into other types. */ class DGLArgValue : public DGLPODValue_ { public: /** @brief default constructor */ DGLArgValue() {} /** * @brief constructor * @param value of the function * @param type_code The type code. */ DGLArgValue(DGLValue value, int type_code) : DGLPODValue_(value, type_code) {} // reuse converter from parent using DGLPODValue_::operator double; using DGLPODValue_::operator int64_t; using DGLPODValue_::operator uint64_t; using DGLPODValue_::operator int; using DGLPODValue_::operator bool; using DGLPODValue_::operator void*; using DGLPODValue_::operator DGLArray*; using DGLPODValue_::operator NDArray; using DGLPODValue_::operator DGLContext; // conversion operator. operator std::string() const { if (type_code_ == kDGLDataType) { return DGLDataType2String(operator DGLDataType()); } else if (type_code_ == kBytes) { DGLByteArray* arr = static_cast(value_.v_handle); return std::string(arr->data, arr->size); } else { DGL_CHECK_TYPE_CODE(type_code_, kStr); return std::string(value_.v_str); } } operator DGLDataType() const { if (type_code_ == kStr) { return String2DGLDataType(operator std::string()); } DGL_CHECK_TYPE_CODE(type_code_, kDGLDataType); return value_.v_type; } operator PackedFunc() const { if (type_code_ == kNull) return PackedFunc(); DGL_CHECK_TYPE_CODE(type_code_, kFuncHandle); return *ptr(); } template operator TypedPackedFunc() const { return TypedPackedFunc(operator PackedFunc()); } operator Module() const { DGL_CHECK_TYPE_CODE(type_code_, kModuleHandle); return *ptr(); } const DGLValue& value() const { return value_; } // Deferred extension handler. template inline TObjectRef AsObjectRef() const; // Convert this value to arbitrary class type template < typename T, typename = typename std::enable_if::value>::type> inline operator T() const; // Return true if the value is of TObjectRef type template < typename TObjectRef, typename = typename std::enable_if< std::is_class::value>::type> inline bool IsObjectType() const; // get internal node ptr, if it is node inline std::shared_ptr& obj_sptr(); }; /** * @brief Return Value container, * Unlike DGLArgValue, which only holds reference and do not delete * the underlying container during destruction. * * DGLRetValue holds value and will manage the underlying containers * when it stores a complicated data type. */ class DGLRetValue : public DGLPODValue_ { public: /** @brief default constructor */ DGLRetValue() {} /** * @brief move constructor from anoter return value. * @param other The other return value. */ DGLRetValue(DGLRetValue&& other) : DGLPODValue_(other.value_, other.type_code_) { other.value_.v_handle = nullptr; other.type_code_ = kNull; } /** @brief destructor */ ~DGLRetValue() { this->Clear(); } // reuse converter from parent using DGLPODValue_::operator double; using DGLPODValue_::operator int64_t; using DGLPODValue_::operator uint64_t; using DGLPODValue_::operator int; using DGLPODValue_::operator bool; using DGLPODValue_::operator void*; using DGLPODValue_::operator DGLArray*; using DGLPODValue_::operator DGLContext; using DGLPODValue_::operator NDArray; // Disable copy and assign from another value, but allow move. DGLRetValue(const DGLRetValue& other) { this->Assign(other); } // conversion operators operator std::string() const { if (type_code_ == kDGLDataType) { return DGLDataType2String(operator DGLDataType()); } else if (type_code_ == kBytes) { return *ptr(); } DGL_CHECK_TYPE_CODE(type_code_, kStr); return *ptr(); } operator DGLDataType() const { if (type_code_ == kStr) { return String2DGLDataType(operator std::string()); } DGL_CHECK_TYPE_CODE(type_code_, kDGLDataType); return value_.v_type; } operator PackedFunc() const { if (type_code_ == kNull) return PackedFunc(); DGL_CHECK_TYPE_CODE(type_code_, kFuncHandle); return *ptr(); } template operator TypedPackedFunc() const { return TypedPackedFunc(operator PackedFunc()); } operator Module() const { DGL_CHECK_TYPE_CODE(type_code_, kModuleHandle); return *ptr(); } // Assign operators DGLRetValue& operator=(DGLRetValue&& other) { this->Clear(); value_ = other.value_; type_code_ = other.type_code_; other.type_code_ = kNull; return *this; } DGLRetValue& operator=(double value) { this->SwitchToPOD(kDGLFloat); value_.v_float64 = value; return *this; } DGLRetValue& operator=(std::nullptr_t value) { this->SwitchToPOD(kNull); value_.v_handle = value; return *this; } DGLRetValue& operator=(void* value) { this->SwitchToPOD(kHandle); value_.v_handle = value; return *this; } DGLRetValue& operator=(int64_t value) { this->SwitchToPOD(kDGLInt); value_.v_int64 = value; return *this; } DGLRetValue& operator=(int value) { this->SwitchToPOD(kDGLInt); value_.v_int64 = value; return *this; } DGLRetValue& operator=(DGLDataType t) { this->SwitchToPOD(kDGLDataType); value_.v_type = t; return *this; } DGLRetValue& operator=(DGLContext ctx) { this->SwitchToPOD(kDGLContext); value_.v_ctx = ctx; return *this; } DGLRetValue& operator=(bool value) { this->SwitchToPOD(kDGLInt); value_.v_int64 = value; return *this; } DGLRetValue& operator=(std::string value) { this->SwitchToClass(kStr, value); return *this; } DGLRetValue& operator=(DGLByteArray value) { this->SwitchToClass(kBytes, std::string(value.data, value.size)); return *this; } DGLRetValue& operator=(NDArray other) { this->Clear(); type_code_ = kNDArrayContainer; value_.v_handle = other.data_; other.data_ = nullptr; return *this; } DGLRetValue& operator=(PackedFunc f) { this->SwitchToClass(kFuncHandle, f); return *this; } template DGLRetValue& operator=(const TypedPackedFunc& f) { return operator=(f.packed()); } DGLRetValue& operator=(Module m) { this->SwitchToClass(kModuleHandle, m); return *this; } DGLRetValue& operator=(const DGLRetValue& other) { // NOLINT(*0 this->Assign(other); return *this; } DGLRetValue& operator=(const DGLArgValue& other) { this->Assign(other); return *this; } template < typename T, typename = typename std::enable_if< extension_class_info::code != 0>::type> DGLRetValue& operator=(const T& other) { this->SwitchToClass(extension_class_info::code, other); return *this; } /** * @brief Move the value back to front-end via C API. * This marks the current container as null. * The managed resources is moved to front-end and * the front end should take charge in managing them. * * @param ret_value The return value. * @param ret_type_code The return type code. */ void MoveToCHost(DGLValue* ret_value, int* ret_type_code) { // cannot move str; need specially handle. CHECK(type_code_ != kStr && type_code_ != kBytes); *ret_value = value_; *ret_type_code = type_code_; type_code_ = kNull; } /** @return The value field, if the data is POD */ const DGLValue& value() const { CHECK( type_code_ != kObjectHandle && type_code_ != kFuncHandle && type_code_ != kModuleHandle && type_code_ != kStr) << "DGLRetValue.value can only be used for POD data"; return value_; } // ObjectRef related extenstions: in dgl/packed_func_ext.h template < typename T, typename = typename std::enable_if::value>::type> inline operator T() const; template inline TObjectRef AsObjectRef() const; inline DGLRetValue& operator=(const ObjectRef& other); inline DGLRetValue& operator=(const std::shared_ptr& other); private: template void Assign(const T& other) { switch (other.type_code()) { case kStr: { SwitchToClass(kStr, other); break; } case kBytes: { SwitchToClass(kBytes, other); break; } case kFuncHandle: { SwitchToClass(kFuncHandle, other); break; } case kModuleHandle: { SwitchToClass(kModuleHandle, other); break; } case kNDArrayContainer: { *this = other.operator NDArray(); break; } case kObjectHandle: { SwitchToClass >( kObjectHandle, *other.template ptr >()); break; } default: { if (other.type_code() < kExtBegin) { SwitchToPOD(other.type_code()); value_ = other.value_; } else { #if DGL_RUNTIME_HEADER_ONLY LOG(FATAL) << "Header only mode do not support ext type"; #else this->Clear(); type_code_ = other.type_code(); value_.v_handle = (*(ExtTypeVTable::Get(other.type_code())->clone))( other.value().v_handle); #endif } break; } } } // get the internal container. void SwitchToPOD(int type_code) { if (type_code_ != type_code) { this->Clear(); type_code_ = type_code; } } template void SwitchToClass(int type_code, T v) { if (type_code_ != type_code) { this->Clear(); type_code_ = type_code; value_.v_handle = new T(v); } else { *static_cast(value_.v_handle) = v; } } void Clear() { if (type_code_ == kNull) return; switch (type_code_) { case kStr: case kBytes: delete ptr(); break; case kFuncHandle: delete ptr(); break; case kModuleHandle: delete ptr(); break; case kObjectHandle: delete ptr >(); break; case kNDArrayContainer: { static_cast(value_.v_handle)->DecRef(); break; } } if (type_code_ > kExtBegin) { #if DGL_RUNTIME_HEADER_ONLY LOG(FATAL) << "Header only mode do not support ext type"; #else (*(ExtTypeVTable::Get(type_code_)->destroy))(value_.v_handle); #endif } type_code_ = kNull; } }; // implementation details inline DGLArgValue DGLArgs::operator[](int i) const { CHECK_LT(i, num_args) << "not enough argument passed, " << num_args << " passed" << " but request arg[" << i << "]."; return DGLArgValue(values[i], type_codes[i]); } inline int DGLArgs::size() const { return num_args; } inline void PackedFunc::CallPacked(DGLArgs args, DGLRetValue* rv) const { body_(args, rv); } inline PackedFunc::FType PackedFunc::body() const { return body_; } // internal namespace namespace detail { template struct for_each_dispatcher { template static void run(const F& f, T&& value, Args&&... args) { // NOLINT(*) f(I, std::forward(value)); for_each_dispatcher::run( f, std::forward(args)...); } }; template struct for_each_dispatcher { static void run(const F& f) {} // NOLINT(*) }; template inline void for_each(const F& f, Args&&... args) { // NOLINT(*) for_each_dispatcher::run( f, std::forward(args)...); } } // namespace detail /* @brief argument settter to PackedFunc */ class DGLArgsSetter { public: DGLArgsSetter(DGLValue* values, int* type_codes) : values_(values), type_codes_(type_codes) {} // setters for POD types template < typename T, typename = typename std::enable_if::value>::type> void operator()(size_t i, T value) const { values_[i].v_int64 = static_cast(value); type_codes_[i] = kDGLInt; } void operator()(size_t i, uint64_t value) const { values_[i].v_int64 = static_cast(value); CHECK_LE(value, static_cast(std::numeric_limits::max())); type_codes_[i] = kDGLInt; } void operator()(size_t i, double value) const { values_[i].v_float64 = value; type_codes_[i] = kDGLFloat; } void operator()(size_t i, std::nullptr_t value) const { values_[i].v_handle = value; type_codes_[i] = kNull; } void operator()(size_t i, const DGLArgValue& value) const { values_[i] = value.value_; type_codes_[i] = value.type_code_; } void operator()(size_t i, void* value) const { values_[i].v_handle = value; type_codes_[i] = kHandle; } void operator()(size_t i, DGLArray* value) const { values_[i].v_handle = value; type_codes_[i] = kArrayHandle; } void operator()(size_t i, DGLContext value) const { values_[i].v_ctx = value; type_codes_[i] = kDGLContext; } void operator()(size_t i, DGLDataType value) const { values_[i].v_type = value; type_codes_[i] = kDGLDataType; } void operator()(size_t i, const char* value) const { values_[i].v_str = value; type_codes_[i] = kStr; } // setters for container type // They must be reference(instead of const ref) // to make sure they are alive in the tuple(instead of getting converted) void operator()(size_t i, const std::string& value) const { // NOLINT(*) values_[i].v_str = value.c_str(); type_codes_[i] = kStr; } void operator()(size_t i, const DGLByteArray& value) const { // NOLINT(*) values_[i].v_handle = const_cast(&value); type_codes_[i] = kBytes; } void operator()(size_t i, const PackedFunc& value) const { // NOLINT(*) values_[i].v_handle = const_cast(&value); type_codes_[i] = kFuncHandle; } template void operator()( size_t i, const TypedPackedFunc& value) const { // NOLINT(*) operator()(i, value.packed()); } void operator()(size_t i, const Module& value) const { // NOLINT(*) values_[i].v_handle = const_cast(&value); type_codes_[i] = kModuleHandle; } void operator()(size_t i, const NDArray& value) const { // NOLINT(*) values_[i].v_handle = value.data_; type_codes_[i] = kNDArrayContainer; } void operator()(size_t i, const DGLRetValue& value) const { // NOLINT(*) if (value.type_code() == kStr) { values_[i].v_str = value.ptr()->c_str(); type_codes_[i] = kStr; } else { CHECK_NE(value.type_code(), kBytes) << "not handled."; values_[i] = value.value_; type_codes_[i] = value.type_code(); } } // extension template < typename T, typename = typename std::enable_if< extension_class_info::code != 0>::type> inline void operator()(size_t i, const T& value) const; // ObjectRef related extenstions: in dgl/packed_func_ext.h inline void operator()(size_t i, const ObjectRef& other) const; // NOLINT(*) private: /** @brief The values fields */ DGLValue* values_; /** @brief The type code fields */ int* type_codes_; }; template inline DGLRetValue PackedFunc::operator()(Args&&... args) const { const int kNumArgs = sizeof...(Args); const int kArraySize = kNumArgs > 0 ? kNumArgs : 1; DGLValue values[kArraySize]; int type_codes[kArraySize]; detail::for_each( DGLArgsSetter(values, type_codes), std::forward(args)...); DGLRetValue rv; body_(DGLArgs(values, type_codes, kNumArgs), &rv); return rv; } namespace detail { template struct unpack_call_dispatcher { template static void run( const F& f, const DGLArgs& args_pack, DGLRetValue* rv, Args&&... unpacked_args) { unpack_call_dispatcher::run( f, args_pack, rv, std::forward(unpacked_args)..., args_pack[index]); } }; template struct unpack_call_dispatcher { template static void run( const F& f, const DGLArgs& args_pack, DGLRetValue* rv, Args&&... unpacked_args) { *rv = R(f(std::forward(unpacked_args)...)); } }; template struct unpack_call_dispatcher { template static void run( const F& f, const DGLArgs& args_pack, DGLRetValue* rv, Args&&... unpacked_args) { f(std::forward(unpacked_args)...); } }; template inline void unpack_call(const F& f, const DGLArgs& args, DGLRetValue* rv) { unpack_call_dispatcher::run(f, args, rv); } template inline R call_packed(const PackedFunc& pf, Args&&... args) { return R(pf(std::forward(args)...)); } template struct typed_packed_call_dispatcher { template static inline R run(const PackedFunc& pf, Args&&... args) { return pf(std::forward(args)...); } }; template <> struct typed_packed_call_dispatcher { template static inline void run(const PackedFunc& pf, Args&&... args) { pf(std::forward(args)...); } }; } // namespace detail template TypedPackedFunc::TypedPackedFunc(PackedFunc packed) : packed_(packed) {} template template inline void TypedPackedFunc::AssignTypedLambda(FType flambda) { packed_ = PackedFunc([flambda](const DGLArgs& args, DGLRetValue* rv) { detail::unpack_call(flambda, args, rv); }); } template inline R TypedPackedFunc::operator()(Args... args) const { return detail::typed_packed_call_dispatcher::run( packed_, std::forward(args)...); } // extension and node type handling namespace detail { template struct DGLValueCast { static T Apply(const TSrc* self) { return self->template AsObjectRef(); } }; template struct DGLValueCast { static T Apply(const TSrc* self) { return self->template AsExtension(); } }; } // namespace detail template inline DGLArgValue::operator T() const { return detail::DGLValueCast< T, DGLArgValue, extension_class_info::code != 0>::Apply(this); } template inline DGLRetValue::operator T() const { return detail::DGLValueCast< T, DGLRetValue, extension_class_info::code != 0>::Apply(this); } template inline void DGLArgsSetter::operator()(size_t i, const T& value) const { static_assert( extension_class_info::code != 0, "Need to have extesion code"); type_codes_[i] = extension_class_info::code; values_[i].v_handle = const_cast(&value); } // extension type handling template struct ExtTypeInfo { static void destroy(void* handle) { delete static_cast(handle); } static void* clone(void* handle) { return new T(*static_cast(handle)); } }; template inline ExtTypeVTable* ExtTypeVTable::Register_() { const int code = extension_class_info::code; static_assert( code != 0, "require extension_class_info traits to be declared with non-zero code"); ExtTypeVTable vt; vt.clone = ExtTypeInfo::clone; vt.destroy = ExtTypeInfo::destroy; return ExtTypeVTable::RegisterInternal(code, vt); } // Implement Module::GetFunction // Put implementation in this file so we have seen the PackedFunc inline PackedFunc Module::GetFunction( const std::string& name, bool query_imports) { PackedFunc pf = node_->GetFunction(name, node_); if (pf != nullptr) return pf; if (query_imports) { for (const Module& m : node_->imports_) { pf = m.node_->GetFunction(name, m.node_); if (pf != nullptr) return pf; } } return pf; } } // namespace runtime } // namespace dgl #endif // DGL_RUNTIME_PACKED_FUNC_H_