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