1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef META_API_FUNCTION_H
17 #define META_API_FUNCTION_H
18 
19 #include <meta/api/call_context.h>
20 #include <meta/interface/builtin_objects.h>
21 #include <meta/interface/interface_helpers.h>
22 #include <meta/interface/intf_cloneable.h>
23 #include <meta/interface/intf_function.h>
24 #include <meta/interface/object_macros.h>
25 
META_BEGIN_NAMESPACE()26 META_BEGIN_NAMESPACE()
27 
28 /**
29  * @brief Function implementation that is used for the meta function system.
30  */
31 template<typename Obj, typename Func>
32 class DefaultFunction : public IntroduceInterfaces<IFunction, ICloneable> {
33 public:
34     ~DefaultFunction() override = default;
35     DefaultFunction& operator=(const DefaultFunction&) noexcept = delete;
36     META_NO_MOVE(DefaultFunction)
37 
38     BASE_NS::string GetName() const override
39     {
40         return name_;
41     }
42 
43     IObject::ConstPtr GetDestination() const override
44     {
45         return interface_pointer_cast<IObject>(obj_);
46     }
47 
48     void Invoke(const ICallContext::Ptr& context) const override
49     {
50         func_(obj_, context);
51     }
52 
53     ICallContext::Ptr CreateCallContext() const override
54     {
55         if (context_) {
56             return context_();
57         }
58         return nullptr;
59     }
60 
61     BASE_NS::shared_ptr<CORE_NS::IInterface> GetClone() const override
62     {
63         BASE_NS::shared_ptr<DefaultFunction> p(new DefaultFunction(*this));
64         return interface_pointer_cast<CORE_NS::IInterface>(p);
65     }
66 
67     DefaultFunction(
68         BASE_NS::string n, BASE_NS::weak_ptr<BASE_NS::remove_const_t<Obj>> obj, Func f, Internal::FContext* context)
69         : name_(BASE_NS::move(n)), obj_(obj), func_(BASE_NS::move(f)), context_(context)
70     {}
71 
72 protected:
73     DefaultFunction(const DefaultFunction& s) : name_(s.name_), obj_(s.obj_), func_(s.func_), context_(s.context_) {}
74 
75 protected:
76     BASE_NS::string name_;
77     BASE_NS::weak_ptr<BASE_NS::remove_const_t<Obj>> obj_ {};
78     Func func_;
79     Internal::FContext* const context_;
80 };
81 
82 /**
83  * @brief Create DefaultFunction object for obj+memfun (used in metadata initialisation)
84  */
85 template<typename Obj, typename MemFun>
CreateFunction(BASE_NS::string_view name,Obj * obj,MemFun func,Internal::FContext * context)86 IFunction::Ptr CreateFunction(BASE_NS::string_view name, Obj* obj, MemFun func, Internal::FContext* context)
87 {
88     using ObjType = BASE_NS::shared_ptr<BASE_NS::remove_const_t<Obj>>;
89     ObjType objPtr { obj->GetSelf(), obj };
90     auto l = [func](auto obj, const ICallContext::Ptr& context) {
91         if (auto o = obj.lock()) {
92             (o.get()->*func)(context);
93         }
94     };
95     return IFunction::Ptr(
96         new DefaultFunction<Obj, decltype(l)>(BASE_NS::string(name), objPtr, BASE_NS::move(l), context));
97 }
98 
99 /**
100  * @brief Create DefaultFunction object from lambda
101  */
102 template<typename Func, typename = EnableIfBindFunction<Func>>
CreateBindFunction(Func func)103 IFunction::Ptr CreateBindFunction(Func func)
104 {
105     auto ccontext = []() {
106         ::BASE_NS::string_view arr[] = { "" };
107         return CreateCallContextImpl<decltype(func())>(ParamNameToView(arr));
108     };
109     // wrap to make CallFunction to work with operator()(auto...)
110     auto wrapper = [func]() mutable { return func(); };
111     auto l = [wrapper](auto, const ICallContext::Ptr& context) { ::META_NS::CallFunction(context, wrapper); };
112     return IFunction::Ptr(new DefaultFunction<IObject, decltype(l)>("Bind", nullptr, BASE_NS::move(l), ccontext));
113 }
114 
115 /**
116  * @brief Create DefaultFunction object from lambda
117  */
118 template<typename Func, typename... Args>
CreateBindFunctionSafe(Func func,Args &&...args)119 IFunction::Ptr CreateBindFunctionSafe(Func func, Args&&... args)
120 {
121     return CreateBindFunction(CaptureSafe(BASE_NS::move(func), BASE_NS::forward<Args>(args)...));
122 }
123 
124 /**
125  * @brief Create forwarding function object
126  */
CreateFunction(const IObject::Ptr & obj,BASE_NS::string_view name)127 inline IFunction::Ptr CreateFunction(const IObject::Ptr& obj, BASE_NS::string_view name)
128 {
129     if (auto f = META_NS::GetObjectRegistry().Create<ISettableFunction>(ClassId::SettableFunction)) {
130         if (f->SetTarget(obj, name)) {
131             return f;
132         }
133     }
134     return nullptr;
135 }
136 
137 /**
138  * @brief Helper class for meta function call result.
139  */
140 template<typename Type>
141 struct CallResult {
142     explicit operator bool() const
143     {
144         return success;
145     }
146 
147     /**
148      * @brief Call context that was used for the call.
149      */
150     ICallContext::Ptr context;
151     /**
152      * @brief True if it was possible to make the call (i.e. the argument types match the parameters).
153      */
154     bool success {};
155     /**
156      * @brief Return value of the function call.
157      */
158     Type value {};
159 };
160 
161 template<>
162 struct CallResult<void> {
163     explicit operator bool() const
164     {
165         return success;
166     }
167 
168     ICallContext::Ptr context;
169     bool success {};
170 };
171 
172 template<typename Ret, typename... Args, size_t... Index>
173 CallResult<Ret> CallMetaFunctionImpl(const IFunction::Ptr& func, IndexSequence<Index...>, Args&&... args)
174 {
175     auto context = func->CreateCallContext();
176     if (!context) {
177         return {};
178     }
179     auto params = context->GetParameters();
180     // Allow to use defaults from call context
181     if (params.size() < sizeof...(Args)) {
182         context->ReportError("invalid meta call");
183         return { context };
184     }
185 
186     if (!(true && ... && Set<PlainType_t<Args>>(context, params[Index].name, args))) {
187         context->ReportError("invalid meta call");
188         return { context };
189     }
190 
191     func->Invoke(context);
192     if (context->Succeeded()) {
193         if constexpr (BASE_NS::is_same_v<Ret, void>) {
194             return CallResult<Ret> { context, true };
195         }
196         if constexpr (!BASE_NS::is_same_v<Ret, void>) {
197             if (auto p = GetResult<Ret>(context)) {
198                 return CallResult<Ret> { context, true, *p };
199             }
200         }
201     }
202     return { context };
203 }
204 
205 /**
206  * @brief Call function via interface with the given arguments.
207  * @param func Function to call.
208  * @param args Arguments for the function call.
209  * @return Result of the call.
210  * @see CallResult
211  */
212 template<typename Ret, typename... Args>
213 CallResult<Ret> CallMetaFunction(const IFunction::Ptr& func, Args&&... args)
214 {
215     return CallMetaFunctionImpl<Ret>(func, MakeIndexSequenceFor<Args...>(), BASE_NS::forward<Args>(args)...);
216 }
217 
218 template<typename Signature>
219 struct IsFunctionCompatibleImpl;
220 
221 template<typename Ret, typename... Args>
222 struct IsFunctionCompatibleImpl<Ret(Args...)> {
223     template<size_t... Index>
224     static bool Call(const IFunction::Ptr& func, IndexSequence<Index...>)
225     {
226         auto context = func->CreateCallContext();
227         // e.g. wrong number of parameters when implementing meta function
228         if (!context) {
229             return false;
230         }
231         auto params = context->GetParameters();
232         // Allow to use defaults from call context
233         if (params.size() < sizeof...(Args)) {
234             return false;
235         }
236 
237         // if we have void, allow any return type, it will just be ignored
238         if constexpr (!BASE_NS::is_same_v<Ret, void>) {
239             if (!IsCompatibleWith<Ret>(context->GetResult())) {
240                 return false;
241             }
242         }
243 
244         if (!(true && ... && IsCompatibleWith<Args>(*params[Index].value))) {
245             return false;
246         }
247         return true;
248     }
249     static bool Call(const IFunction::Ptr& func)
250     {
251         if constexpr ((true && ... && HasUid_v<PlainType_t<Args>>)) {
252             return Call(func, MakeIndexSequenceFor<Args...>());
253         }
254         return false;
255     }
256 };
257 
258 /**
259  * @brief Check if function is compatible with given signature (The return type and parameter types match).
260  */
261 template<typename FuncSignature>
262 bool IsFunctionCompatible(const IFunction::Ptr& func)
263 {
264     return IsFunctionCompatibleImpl<FuncSignature>::Call(func);
265 }
266 
267 META_END_NAMESPACE()
268 
269 #endif
270