1089 lines
32 KiB
C++
1089 lines
32 KiB
C++
/**
|
|
* 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 <dmlc/logging.h>
|
|
|
|
#include <functional>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#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<void(DGLArgs args, DGLRetValue* rv)>;
|
|
/** @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 <typename... Args>
|
|
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<R(Args..)>"
|
|
*/
|
|
template <typename FType>
|
|
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<int(int)> 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 <typename R, typename... Args>
|
|
class TypedPackedFunc<R(Args...)> {
|
|
public:
|
|
/** @brief short hand for this function type */
|
|
using TSelf = TypedPackedFunc<R(Args...)>;
|
|
/** @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<int(int)> 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<int(int)> 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<std::is_convertible<
|
|
FLambda, std::function<R(Args...)> >::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<int(int)> 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<std::is_convertible<
|
|
FLambda,
|
|
std::function<R(Args...)> >::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 <typename FLambda>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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<double>(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<int>::max());
|
|
return static_cast<int>(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<DGLArray*>(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<NDArray::Container*>(value_.v_handle));
|
|
}
|
|
operator DGLContext() const {
|
|
DGL_CHECK_TYPE_CODE(type_code_, kDGLContext);
|
|
return value_.v_ctx;
|
|
}
|
|
template <typename TExtension>
|
|
const TExtension& AsExtension() const {
|
|
CHECK_LT(type_code_, kExtEnd);
|
|
return static_cast<TExtension*>(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 <typename T>
|
|
T* ptr() const {
|
|
return static_cast<T*>(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<DGLByteArray*>(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<PackedFunc>();
|
|
}
|
|
template <typename FType>
|
|
operator TypedPackedFunc<FType>() const {
|
|
return TypedPackedFunc<FType>(operator PackedFunc());
|
|
}
|
|
operator Module() const {
|
|
DGL_CHECK_TYPE_CODE(type_code_, kModuleHandle);
|
|
return *ptr<Module>();
|
|
}
|
|
const DGLValue& value() const { return value_; }
|
|
|
|
// Deferred extension handler.
|
|
template <typename TObjectRef>
|
|
inline TObjectRef AsObjectRef() const;
|
|
|
|
// Convert this value to arbitrary class type
|
|
template <
|
|
typename T,
|
|
typename = typename std::enable_if<std::is_class<T>::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<TObjectRef>::value>::type>
|
|
inline bool IsObjectType() const;
|
|
|
|
// get internal node ptr, if it is node
|
|
inline std::shared_ptr<Object>& 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<std::string>();
|
|
}
|
|
DGL_CHECK_TYPE_CODE(type_code_, kStr);
|
|
return *ptr<std::string>();
|
|
}
|
|
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<PackedFunc>();
|
|
}
|
|
template <typename FType>
|
|
operator TypedPackedFunc<FType>() const {
|
|
return TypedPackedFunc<FType>(operator PackedFunc());
|
|
}
|
|
operator Module() const {
|
|
DGL_CHECK_TYPE_CODE(type_code_, kModuleHandle);
|
|
return *ptr<Module>();
|
|
}
|
|
// 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 <typename FType>
|
|
DGLRetValue& operator=(const TypedPackedFunc<FType>& 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<T>::code != 0>::type>
|
|
DGLRetValue& operator=(const T& other) {
|
|
this->SwitchToClass<T>(extension_class_info<T>::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<std::is_class<T>::value>::type>
|
|
inline operator T() const;
|
|
template <typename TObjectRef>
|
|
inline TObjectRef AsObjectRef() const;
|
|
inline DGLRetValue& operator=(const ObjectRef& other);
|
|
inline DGLRetValue& operator=(const std::shared_ptr<Object>& other);
|
|
|
|
private:
|
|
template <typename T>
|
|
void Assign(const T& other) {
|
|
switch (other.type_code()) {
|
|
case kStr: {
|
|
SwitchToClass<std::string>(kStr, other);
|
|
break;
|
|
}
|
|
case kBytes: {
|
|
SwitchToClass<std::string>(kBytes, other);
|
|
break;
|
|
}
|
|
case kFuncHandle: {
|
|
SwitchToClass<PackedFunc>(kFuncHandle, other);
|
|
break;
|
|
}
|
|
case kModuleHandle: {
|
|
SwitchToClass<Module>(kModuleHandle, other);
|
|
break;
|
|
}
|
|
case kNDArrayContainer: {
|
|
*this = other.operator NDArray();
|
|
break;
|
|
}
|
|
case kObjectHandle: {
|
|
SwitchToClass<std::shared_ptr<Object> >(
|
|
kObjectHandle, *other.template ptr<std::shared_ptr<Object> >());
|
|
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 <typename T>
|
|
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<T*>(value_.v_handle) = v;
|
|
}
|
|
}
|
|
void Clear() {
|
|
if (type_code_ == kNull) return;
|
|
switch (type_code_) {
|
|
case kStr:
|
|
case kBytes:
|
|
delete ptr<std::string>();
|
|
break;
|
|
case kFuncHandle:
|
|
delete ptr<PackedFunc>();
|
|
break;
|
|
case kModuleHandle:
|
|
delete ptr<Module>();
|
|
break;
|
|
case kObjectHandle:
|
|
delete ptr<std::shared_ptr<Object> >();
|
|
break;
|
|
case kNDArrayContainer: {
|
|
static_cast<NDArray::Container*>(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 <bool stop, std::size_t I, typename F>
|
|
struct for_each_dispatcher {
|
|
template <typename T, typename... Args>
|
|
static void run(const F& f, T&& value, Args&&... args) { // NOLINT(*)
|
|
f(I, std::forward<T>(value));
|
|
for_each_dispatcher<sizeof...(Args) == 0, (I + 1), F>::run(
|
|
f, std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
|
|
template <std::size_t I, typename F>
|
|
struct for_each_dispatcher<true, I, F> {
|
|
static void run(const F& f) {} // NOLINT(*)
|
|
};
|
|
|
|
template <typename F, typename... Args>
|
|
inline void for_each(const F& f, Args&&... args) { // NOLINT(*)
|
|
for_each_dispatcher<sizeof...(Args) == 0, 0, F>::run(
|
|
f, std::forward<Args>(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<std::is_integral<T>::value>::type>
|
|
void operator()(size_t i, T value) const {
|
|
values_[i].v_int64 = static_cast<int64_t>(value);
|
|
type_codes_[i] = kDGLInt;
|
|
}
|
|
void operator()(size_t i, uint64_t value) const {
|
|
values_[i].v_int64 = static_cast<int64_t>(value);
|
|
CHECK_LE(value, static_cast<uint64_t>(std::numeric_limits<int64_t>::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<DGLByteArray*>(&value);
|
|
type_codes_[i] = kBytes;
|
|
}
|
|
void operator()(size_t i, const PackedFunc& value) const { // NOLINT(*)
|
|
values_[i].v_handle = const_cast<PackedFunc*>(&value);
|
|
type_codes_[i] = kFuncHandle;
|
|
}
|
|
template <typename FType>
|
|
void operator()(
|
|
size_t i, const TypedPackedFunc<FType>& value) const { // NOLINT(*)
|
|
operator()(i, value.packed());
|
|
}
|
|
void operator()(size_t i, const Module& value) const { // NOLINT(*)
|
|
values_[i].v_handle = const_cast<Module*>(&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<std::string>()->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<T>::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 <typename... Args>
|
|
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>(args)...);
|
|
DGLRetValue rv;
|
|
body_(DGLArgs(values, type_codes, kNumArgs), &rv);
|
|
return rv;
|
|
}
|
|
|
|
namespace detail {
|
|
template <typename R, int nleft, int index, typename F>
|
|
struct unpack_call_dispatcher {
|
|
template <typename... Args>
|
|
static void run(
|
|
const F& f, const DGLArgs& args_pack, DGLRetValue* rv,
|
|
Args&&... unpacked_args) {
|
|
unpack_call_dispatcher<R, nleft - 1, index + 1, F>::run(
|
|
f, args_pack, rv, std::forward<Args>(unpacked_args)...,
|
|
args_pack[index]);
|
|
}
|
|
};
|
|
|
|
template <typename R, int index, typename F>
|
|
struct unpack_call_dispatcher<R, 0, index, F> {
|
|
template <typename... Args>
|
|
static void run(
|
|
const F& f, const DGLArgs& args_pack, DGLRetValue* rv,
|
|
Args&&... unpacked_args) {
|
|
*rv = R(f(std::forward<Args>(unpacked_args)...));
|
|
}
|
|
};
|
|
|
|
template <int index, typename F>
|
|
struct unpack_call_dispatcher<void, 0, index, F> {
|
|
template <typename... Args>
|
|
static void run(
|
|
const F& f, const DGLArgs& args_pack, DGLRetValue* rv,
|
|
Args&&... unpacked_args) {
|
|
f(std::forward<Args>(unpacked_args)...);
|
|
}
|
|
};
|
|
|
|
template <typename R, int nargs, typename F>
|
|
inline void unpack_call(const F& f, const DGLArgs& args, DGLRetValue* rv) {
|
|
unpack_call_dispatcher<R, nargs, 0, F>::run(f, args, rv);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
inline R call_packed(const PackedFunc& pf, Args&&... args) {
|
|
return R(pf(std::forward<Args>(args)...));
|
|
}
|
|
|
|
template <typename R>
|
|
struct typed_packed_call_dispatcher {
|
|
template <typename... Args>
|
|
static inline R run(const PackedFunc& pf, Args&&... args) {
|
|
return pf(std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct typed_packed_call_dispatcher<void> {
|
|
template <typename... Args>
|
|
static inline void run(const PackedFunc& pf, Args&&... args) {
|
|
pf(std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
} // namespace detail
|
|
|
|
template <typename R, typename... Args>
|
|
TypedPackedFunc<R(Args...)>::TypedPackedFunc(PackedFunc packed)
|
|
: packed_(packed) {}
|
|
|
|
template <typename R, typename... Args>
|
|
template <typename FType>
|
|
inline void TypedPackedFunc<R(Args...)>::AssignTypedLambda(FType flambda) {
|
|
packed_ = PackedFunc([flambda](const DGLArgs& args, DGLRetValue* rv) {
|
|
detail::unpack_call<R, sizeof...(Args)>(flambda, args, rv);
|
|
});
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
inline R TypedPackedFunc<R(Args...)>::operator()(Args... args) const {
|
|
return detail::typed_packed_call_dispatcher<R>::run(
|
|
packed_, std::forward<Args>(args)...);
|
|
}
|
|
|
|
// extension and node type handling
|
|
namespace detail {
|
|
template <typename T, typename TSrc, bool is_ext>
|
|
struct DGLValueCast {
|
|
static T Apply(const TSrc* self) { return self->template AsObjectRef<T>(); }
|
|
};
|
|
|
|
template <typename T, typename TSrc>
|
|
struct DGLValueCast<T, TSrc, true> {
|
|
static T Apply(const TSrc* self) { return self->template AsExtension<T>(); }
|
|
};
|
|
} // namespace detail
|
|
|
|
template <typename T, typename>
|
|
inline DGLArgValue::operator T() const {
|
|
return detail::DGLValueCast<
|
|
T, DGLArgValue, extension_class_info<T>::code != 0>::Apply(this);
|
|
}
|
|
|
|
template <typename T, typename>
|
|
inline DGLRetValue::operator T() const {
|
|
return detail::DGLValueCast<
|
|
T, DGLRetValue, extension_class_info<T>::code != 0>::Apply(this);
|
|
}
|
|
|
|
template <typename T, typename>
|
|
inline void DGLArgsSetter::operator()(size_t i, const T& value) const {
|
|
static_assert(
|
|
extension_class_info<T>::code != 0, "Need to have extesion code");
|
|
type_codes_[i] = extension_class_info<T>::code;
|
|
values_[i].v_handle = const_cast<T*>(&value);
|
|
}
|
|
|
|
// extension type handling
|
|
template <typename T>
|
|
struct ExtTypeInfo {
|
|
static void destroy(void* handle) { delete static_cast<T*>(handle); }
|
|
static void* clone(void* handle) { return new T(*static_cast<T*>(handle)); }
|
|
};
|
|
|
|
template <typename T>
|
|
inline ExtTypeVTable* ExtTypeVTable::Register_() {
|
|
const int code = extension_class_info<T>::code;
|
|
static_assert(
|
|
code != 0,
|
|
"require extension_class_info traits to be declared with non-zero code");
|
|
ExtTypeVTable vt;
|
|
vt.clone = ExtTypeInfo<T>::clone;
|
|
vt.destroy = ExtTypeInfo<T>::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_
|