/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef META_API_FUNCTION_H
#define META_API_FUNCTION_H
#include
#include
#include
#include
#include
#include
META_BEGIN_NAMESPACE()
/**
* @brief Function implementation that is used for the meta function system.
*/
template
class DefaultFunction : public IntroduceInterfaces {
public:
~DefaultFunction() override = default;
DefaultFunction& operator=(const DefaultFunction&) noexcept = delete;
META_NO_MOVE(DefaultFunction)
BASE_NS::string GetName() const override
{
return name_;
}
IObject::ConstPtr GetDestination() const override
{
return interface_pointer_cast(obj_);
}
void Invoke(const ICallContext::Ptr& context) const override
{
func_(obj_, context);
}
ICallContext::Ptr CreateCallContext() const override
{
if (context_) {
return context_();
}
return nullptr;
}
BASE_NS::shared_ptr GetClone() const override
{
BASE_NS::shared_ptr p(new DefaultFunction(*this));
return interface_pointer_cast(p);
}
DefaultFunction(
BASE_NS::string n, BASE_NS::weak_ptr> obj, Func f, Internal::FContext* context)
: name_(BASE_NS::move(n)), obj_(obj), func_(BASE_NS::move(f)), context_(context)
{}
protected:
DefaultFunction(const DefaultFunction& s) : name_(s.name_), obj_(s.obj_), func_(s.func_), context_(s.context_) {}
protected:
BASE_NS::string name_;
BASE_NS::weak_ptr> obj_ {};
Func func_;
Internal::FContext* const context_;
};
/**
* @brief Create DefaultFunction object for obj+memfun (used in metadata initialisation)
*/
template
IFunction::Ptr CreateFunction(BASE_NS::string_view name, Obj* obj, MemFun func, Internal::FContext* context)
{
using ObjType = BASE_NS::shared_ptr>;
ObjType objPtr { obj->GetSelf(), obj };
auto l = [func](auto obj, const ICallContext::Ptr& context) {
if (auto o = obj.lock()) {
(o.get()->*func)(context);
}
};
return IFunction::Ptr(
new DefaultFunction(BASE_NS::string(name), objPtr, BASE_NS::move(l), context));
}
/**
* @brief Create DefaultFunction object from lambda
*/
template>
IFunction::Ptr CreateBindFunction(Func func)
{
auto ccontext = []() {
::BASE_NS::string_view arr[] = { "" };
return CreateCallContextImpl(ParamNameToView(arr));
};
// wrap to make CallFunction to work with operator()(auto...)
auto wrapper = [func]() mutable { return func(); };
auto l = [wrapper](auto, const ICallContext::Ptr& context) { ::META_NS::CallFunction(context, wrapper); };
return IFunction::Ptr(new DefaultFunction("Bind", nullptr, BASE_NS::move(l), ccontext));
}
/**
* @brief Create DefaultFunction object from lambda
*/
template
IFunction::Ptr CreateBindFunctionSafe(Func func, Args&&... args)
{
return CreateBindFunction(CaptureSafe(BASE_NS::move(func), BASE_NS::forward(args)...));
}
/**
* @brief Create forwarding function object
*/
inline IFunction::Ptr CreateFunction(const IObject::Ptr& obj, BASE_NS::string_view name)
{
if (auto f = META_NS::GetObjectRegistry().Create(ClassId::SettableFunction)) {
if (f->SetTarget(obj, name)) {
return f;
}
}
return nullptr;
}
/**
* @brief Helper class for meta function call result.
*/
template
struct CallResult {
explicit operator bool() const
{
return success;
}
/**
* @brief Call context that was used for the call.
*/
ICallContext::Ptr context;
/**
* @brief True if it was possible to make the call (i.e. the argument types match the parameters).
*/
bool success {};
/**
* @brief Return value of the function call.
*/
Type value {};
};
template<>
struct CallResult {
explicit operator bool() const
{
return success;
}
ICallContext::Ptr context;
bool success {};
};
template
CallResult CallMetaFunctionImpl(const IFunction::Ptr& func, IndexSequence, Args&&... args)
{
auto context = func->CreateCallContext();
if (!context) {
return {};
}
auto params = context->GetParameters();
// Allow to use defaults from call context
if (params.size() < sizeof...(Args)) {
context->ReportError("invalid meta call");
return { context };
}
if (!(true && ... && Set>(context, params[Index].name, args))) {
context->ReportError("invalid meta call");
return { context };
}
func->Invoke(context);
if (context->Succeeded()) {
if constexpr (BASE_NS::is_same_v) {
return CallResult { context, true };
}
if constexpr (!BASE_NS::is_same_v) {
if (auto p = GetResult(context)) {
return CallResult { context, true, *p };
}
}
}
return { context };
}
/**
* @brief Call function via interface with the given arguments.
* @param func Function to call.
* @param args Arguments for the function call.
* @return Result of the call.
* @see CallResult
*/
template
CallResult CallMetaFunction(const IFunction::Ptr& func, Args&&... args)
{
return CallMetaFunctionImpl(func, MakeIndexSequenceFor(), BASE_NS::forward(args)...);
}
template
struct IsFunctionCompatibleImpl;
template
struct IsFunctionCompatibleImpl {
template
static bool Call(const IFunction::Ptr& func, IndexSequence)
{
auto context = func->CreateCallContext();
// e.g. wrong number of parameters when implementing meta function
if (!context) {
return false;
}
auto params = context->GetParameters();
// Allow to use defaults from call context
if (params.size() < sizeof...(Args)) {
return false;
}
// if we have void, allow any return type, it will just be ignored
if constexpr (!BASE_NS::is_same_v) {
if (!IsCompatibleWith(context->GetResult())) {
return false;
}
}
if (!(true && ... && IsCompatibleWith(*params[Index].value))) {
return false;
}
return true;
}
static bool Call(const IFunction::Ptr& func)
{
if constexpr ((true && ... && HasUid_v>)) {
return Call(func, MakeIndexSequenceFor());
}
return false;
}
};
/**
* @brief Check if function is compatible with given signature (The return type and parameter types match).
*/
template
bool IsFunctionCompatible(const IFunction::Ptr& func)
{
return IsFunctionCompatibleImpl::Call(func);
}
META_END_NAMESPACE()
#endif