/*
* 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_CALL_CONTEXT_H
#define META_API_CALL_CONTEXT_H
#include
#include
#include
#include
META_BEGIN_NAMESPACE()
/**
* @brief Set argument value for parameter
* @param context Call context where the argument is set
* @param name Name of the defined parameter
* @param value to set, type must match with the defined parameter type.
* @return True on success.
*/
template
bool Set(const ICallContext::Ptr& context, BASE_NS::string_view name, const Type& value)
{
return context->Set(name, Any(value));
}
/**
* @brief Set result value
* @param context Call context where the result is set
* @param value to set, type must match with the defined result type.
*/
template
bool SetResult(const ICallContext::Ptr& context, const Type& value)
{
return context->SetResult(Any(value));
}
/**
* @brief Set void result value
* @param context Call context where the result is set
*/
template>>
bool SetResult(const ICallContext::Ptr& context)
{
return context->SetResult();
}
/**
* @brief Get argument value if set, otherwise parameter default value.
* @param context Call context to get from.
* @param Name of the defined parameter, the type must match.
* @return Pointer to contained value if successful, otherwise null.
*/
template
Expected Get(const ICallContext::Ptr& context, BASE_NS::string_view name)
{
if (auto any = context->Get(name)) {
Type t;
if (any->GetValue(t)) {
return t;
}
}
return GenericError::FAIL;
}
/**
* @brief Get result value/type
* @param context Call context to get from.
* @return Pointer to contained result if type matches, otherwise null.
* Note: One should first use ICallContext Success function to see if the call was
* successful before querying for value.
*/
template
Expected GetResult(const ICallContext::Ptr& context)
{
if (auto any = context->GetResult()) {
Type t;
if (any->GetValue(t)) {
return t;
}
}
return GenericError::FAIL;
}
/**
* @brief Define parameter name, type and default value in call context
* @param context Call context to define parameter.
* @param name Name of the parameter.
* @param value Type and default value of the parameter.
*/
template
bool DefineParameter(const ICallContext::Ptr& context, BASE_NS::string_view name, const Type& value = {})
{
return context->DefineParameter(name, IAny::Ptr(new Any(value)));
}
/**
* @brief Define result type in call context
* @param context Call context for define result type
* @value Type of the result, the value is ignored if SetResult is used.
*/
template
bool DefineResult(const ICallContext::Ptr& context, const Type& value)
{
return context->DefineResult(IAny::Ptr(new Any(value)));
}
/**
* @brief Define result type in call context
*/
template
bool DefineResult(const ICallContext::Ptr& context)
{
if constexpr (BASE_NS::is_same_v) {
return context->DefineResult(nullptr);
}
if constexpr (!BASE_NS::is_same_v) {
return DefineResult(context, Type {});
}
}
/**
* @brief Set values for call context, used for setting parameters for meta function calls.
*/
template
bool SetContextValues(
const ICallContext::Ptr& context, IndexSequence, const BASE_NS::array_view& names)
{
return (true && ... && DefineParameter(context, names[Index]));
}
/**
* @brief Create call context for meta function calls with parameter names.
* @param Ret Return type of the meta function.
* @param Args Types of the meta function parameters.
* @param names Names of the meta function parameters.
*/
template
ICallContext::Ptr CreateCallContextImpl(const BASE_NS::array_view& names)
{
if (sizeof...(Args) != names.size()) {
CORE_LOG_E("Invalid amount of parameter names");
return nullptr;
}
auto context = GetObjectRegistry().ConstructDefaultCallContext();
if (!SetContextValues...>(context, MakeIndexSequenceFor(), names)) {
CORE_LOG_E("Failed setting parameters");
return nullptr;
}
if (!DefineResult>(context)) {
CORE_LOG_E("Failed setting return type");
return nullptr;
}
return context;
}
/**
* @brief Create call context for meta function calls by deducing types from member function.
*/
template
ICallContext::Ptr CreateCallContext(Ret (Obj::*)(Args...), const BASE_NS::array_view& names)
{
return CreateCallContextImpl(names);
}
/**
* @brief Create call context for meta function calls by deducing types from member function.
*/
template
ICallContext::Ptr CreateCallContext(Ret (Obj::*)(Args...) const, const BASE_NS::array_view& names)
{
return CreateCallContextImpl(names);
}
// convert array of string views to array view and ignore the first one (workaround for empty arrays)
template
BASE_NS::array_view ParamNameToView(BASE_NS::string_view (&arr)[S])
{
return BASE_NS::array_view(arr + 1, arr + S);
}
template&>>
struct CallArg {
using Type = PlainType_t;
explicit CallArg(IAny::Ptr any) : any_(any) {}
/* NOLINTNEXTLINE(google-explicit-constructor) */
operator Type() const
{
Type t;
any_->GetValue(t);
return t;
}
private:
IAny::Ptr any_;
};
template
struct CallArg {
using Type = PlainType_t;
explicit CallArg(IAny::Ptr any) : any_(any)
{
any_->GetValue(t_);
}
~CallArg()
{
any_->SetValue(t_);
}
META_DEFAULT_COPY_MOVE(CallArg)
/* NOLINTNEXTLINE(google-explicit-constructor) */
operator Type&() const
{
return t_;
}
private:
IAny::Ptr any_;
mutable Type t_;
};
template
struct CallFunctionImpl {
template
static bool Call(
const ICallContext::Ptr& context, Func func, BASE_NS::array_view argView, IndexSequence)
{
if (!(true && ... && IsGetCompatibleWith(*argView[Index]))) {
context->ReportError("invalid argument type for function call");
return false;
}
return [&](const auto&... args) {
if constexpr (BASE_NS::is_same_v) {
func(args...);
SetResult(context);
return true;
} else {
auto ret = func(args...);
return SetResult(context, ret);
}
}(CallArg(argView[Index])...);
}
template
static bool Call(const ICallContext::Ptr& context, Func func, IndexSequence ind)
{
auto params = context->GetParameters();
if (params.size() != sizeof...(Args)) {
context->ReportError("invalid function call");
return false;
}
if constexpr (sizeof...(Args) != 0) {
IAny::Ptr args[] = { params[Index].value... };
return Call(context, func, args, ind);
} else {
return Call(context, func, BASE_NS::array_view {}, ind);
}
}
template
static bool Call(const ICallContext::Ptr& context, Func func,
const BASE_NS::array_view& names, IndexSequence ind)
{
auto params = context->GetParameters();
if (params.size() != sizeof...(Args)) {
context->ReportError("invalid function call");
return false;
}
if constexpr (sizeof...(Args) != 0) {
IAny::Ptr args[] = { context->Get(names[Index])... };
return Call(context, func, args, ind);
} else {
return Call(context, func, BASE_NS::array_view {}, ind);
}
}
};
/**
* @brief Call meta function, giving the arguments in the order they are in the context.
* @param context Call context for meta function (i.e. arguments and return type information).
* @param obj Target object to call function for.
* @param func Member function to call.
*/
template
bool CallFunction(const ICallContext::Ptr& context, Obj* obj, Ret (Obj::*func)(Args...))
{
return CallFunctionImpl::Call(
context, [&](Args... args) { return (obj->*func)(args...); }, MakeIndexSequenceFor());
}
template
bool CallFunction(const ICallContext::Ptr& context, const Obj* obj, Ret (Obj::*func)(Args...) const)
{
return CallFunctionImpl::Call(
context, [&](Args... args) { return (obj->*func)(args...); }, MakeIndexSequenceFor());
}
template
bool CallFunction(const ICallContext::Ptr& context, Func func)
{
return CallFunction(context, &func, &Func::operator());
}
/**
* @brief Call meta function, giving the arguments in the order of the given parameter names.
* @param context Call context for meta function (i.e. arguments and return type information).
* @param obj Target object to call function for.
* @param func Member function to call.
*/
template
bool CallFunction(const ICallContext::Ptr& context, Obj* obj, Ret (Obj::*func)(Args...),
const BASE_NS::array_view& names)
{
return CallFunctionImpl::Call(
context, [&](Args... args) { return (obj->*func)(args...); }, names, MakeIndexSequenceFor());
}
template
bool CallFunction(const ICallContext::Ptr& context, const Obj* obj, Ret (Obj::*func)(Args...) const,
const BASE_NS::array_view& names)
{
return CallFunctionImpl::Call(
context, [&](Args... args) { return (obj->*func)(args...); }, names, MakeIndexSequenceFor());
}
META_END_NAMESPACE()
#endif