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/base/capture.h>
21 #include <meta/interface/builtin_objects.h>
22 #include <meta/interface/interface_helpers.h>
23 #include <meta/interface/intf_cloneable.h>
24 #include <meta/interface/intf_function.h>
25 #include <meta/interface/object_macros.h>
26
META_BEGIN_NAMESPACE()27 META_BEGIN_NAMESPACE()
28
29 /**
30 * @brief Function implementation that is used for the meta function system.
31 */
32 template<typename Interface, typename Func>
33 class DefaultFunction final : public IntroduceInterfaces<IObject, IFunction, ICloneable> {
34 public:
35 ~DefaultFunction() override = default;
36 DefaultFunction& operator=(const DefaultFunction&) noexcept = delete;
37 META_NO_MOVE(DefaultFunction)
38
39 BASE_NS::string GetName() const override
40 {
41 return name_;
42 }
43
44 IObject::ConstPtr GetDestination() const override
45 {
46 return interface_pointer_cast<IObject>(obj_);
47 }
48
49 void Invoke(const ICallContext::Ptr& context) const override
50 {
51 func_(obj_, context);
52 }
53
54 ICallContext::Ptr CreateCallContext() const override
55 {
56 if (context_) {
57 return GetValue<ICallContext::Ptr>(*context_());
58 }
59 return nullptr;
60 }
61
62 BASE_NS::shared_ptr<CORE_NS::IInterface> GetClone() const override
63 {
64 BASE_NS::shared_ptr<DefaultFunction> p(new DefaultFunction(*this));
65 return interface_pointer_cast<CORE_NS::IInterface>(p);
66 }
67
68 ObjectId GetClassId() const override
69 {
70 return {};
71 }
72 BASE_NS::string_view GetClassName() const override
73 {
74 return "Function";
75 }
76 BASE_NS::vector<BASE_NS::Uid> GetInterfaces() const override
77 {
78 return GetInterfacesVector();
79 }
80
81 DefaultFunction(BASE_NS::string n, BASE_NS::shared_ptr<Interface> obj, Func f, Internal::MetaValue* context)
82 : name_(BASE_NS::move(n)), obj_(obj), func_(BASE_NS::move(f)), context_(context)
83 {}
84
85 protected:
86 DefaultFunction(const DefaultFunction& s) : name_(s.name_), obj_(s.obj_), func_(s.func_), context_(s.context_) {}
87
88 protected:
89 BASE_NS::string name_;
90 BASE_NS::weak_ptr<Interface> obj_;
91 Func func_;
92 Internal::MetaValue* const context_;
93 };
94
95 /**
96 * @brief Create DefaultFunction object for obj+memfun (used in metadata initialisation)
97 */
98 template<typename Interface, typename Func>
CreateFunction(BASE_NS::string_view name,BASE_NS::shared_ptr<Interface> obj,Func func,Internal::MetaValue * context)99 IFunction::Ptr CreateFunction(
100 BASE_NS::string_view name, BASE_NS::shared_ptr<Interface> obj, Func func, Internal::MetaValue* context)
101 {
102 auto l = [func](auto obj, const ICallContext::Ptr& context) {
103 if (auto o = obj.lock()) {
104 CallFunction(context, o.get(), func);
105 }
106 };
107 return IFunction::Ptr(
108 new DefaultFunction<Interface, decltype(l)>(BASE_NS::string(name), obj, BASE_NS::move(l), context));
109 }
110
111 /**
112 * @brief Create DefaultFunction object from lambda
113 */
114 template<typename Func, typename = EnableIfBindFunction<Func>>
CreateBindFunction(Func func)115 IFunction::Ptr CreateBindFunction(Func func)
116 {
117 auto ccontext = []() {
118 ::BASE_NS::string_view arr[] = { "" };
119 return META_NS::ConstructAny<META_NS::ICallContext::Ptr>(
120 CreateCallContextImpl<decltype(func())>(ParamNameToView(arr)));
121 };
122 // wrap to make CallFunction to work with operator()(auto...)
123 auto wrapper = [func]() mutable { return func(); };
124 auto l = [wrapper](auto, const ICallContext::Ptr& context) { ::META_NS::CallFunction(context, wrapper); };
125 return IFunction::Ptr(new DefaultFunction<IObject, decltype(l)>("Bind", nullptr, BASE_NS::move(l), ccontext));
126 }
127
128 /**
129 * @brief Create DefaultFunction object from lambda
130 */
131 template<typename Func, typename... Args>
CreateBindFunctionSafe(Func func,Args &&...args)132 IFunction::Ptr CreateBindFunctionSafe(Func func, Args&&... args)
133 {
134 return CreateBindFunction(CaptureSafe(BASE_NS::move(func), BASE_NS::forward<Args>(args)...));
135 }
136
137 /**
138 * @brief Create forwarding function object
139 */
CreateFunction(const IObject::Ptr & obj,BASE_NS::string_view name)140 inline IFunction::Ptr CreateFunction(const IObject::Ptr& obj, BASE_NS::string_view name)
141 {
142 if (auto f = META_NS::GetObjectRegistry().Create<ISettableFunction>(ClassId::SettableFunction)) {
143 if (f->SetTarget(obj, name)) {
144 return f;
145 }
146 }
147 return nullptr;
148 }
149
150 /**
151 * @brief Helper class for meta function call result.
152 */
153 template<typename Type>
154 struct CallResult {
155 explicit operator bool() const
156 {
157 return success;
158 }
159
160 /**
161 * @brief Call context that was used for the call.
162 */
163 ICallContext::Ptr context;
164 /**
165 * @brief True if it was possible to make the call (i.e. the argument types match the parameters).
166 */
167 bool success {};
168 /**
169 * @brief Return value of the function call.
170 */
171 Type value {};
172 };
173
174 template<>
175 struct CallResult<void> {
176 explicit operator bool() const
177 {
178 return success;
179 }
180
181 ICallContext::Ptr context;
182 bool success {};
183 };
184
185 template<typename Ret, typename... Args, size_t... Index>
186 CallResult<Ret> CallMetaFunctionImpl(const IFunction::Ptr& func, IndexSequence<Index...>, Args&&... args)
187 {
188 auto context = func->CreateCallContext();
189 if (!context) {
190 return {};
191 }
192 auto params = context->GetParameters();
193 // Allow to use defaults from call context
194 if (params.size() < sizeof...(Args)) {
195 context->ReportError("invalid meta call");
196 return { context };
197 }
198
199 if (!(true && ... && Set<PlainType_t<Args>>(context, params[Index].name, args))) {
200 context->ReportError("invalid meta call");
201 return { context };
202 }
203
204 func->Invoke(context);
205 if (context->Succeeded()) {
206 if constexpr (BASE_NS::is_same_v<Ret, void>) {
207 return CallResult<Ret> { context, true };
208 }
209 if constexpr (!BASE_NS::is_same_v<Ret, void>) {
210 if (auto p = GetResult<Ret>(context)) {
211 return CallResult<Ret> { context, true, *p };
212 }
213 }
214 }
215 return { context };
216 }
217
218 /**
219 * @brief Call function via interface with the given arguments.
220 * @param func Function to call.
221 * @param args Arguments for the function call.
222 * @return Result of the call.
223 * @see CallResult
224 */
225 template<typename Ret, typename... Args>
226 CallResult<Ret> CallMetaFunction(const IFunction::Ptr& func, Args&&... args)
227 {
228 return CallMetaFunctionImpl<Ret>(func, MakeIndexSequenceFor<Args...>(), BASE_NS::forward<Args>(args)...);
229 }
230
231 template<typename Signature>
232 struct IsFunctionCompatibleImpl;
233
234 template<typename Ret, typename... Args>
235 struct IsFunctionCompatibleImpl<Ret(Args...)> {
236 template<size_t... Index>
237 static bool Call(const IFunction::Ptr& func, IndexSequence<Index...>)
238 {
239 auto context = func->CreateCallContext();
240 // e.g. wrong number of parameters when implementing meta function
241 if (!context) {
242 return false;
243 }
244
245 auto params = context->GetParameters();
246 // Allow to use defaults from call context
247 if (params.size() < sizeof...(Args)) {
248 return false;
249 }
250
251 // if we have void, allow any return type, it will just be ignored
252 if constexpr (!BASE_NS::is_same_v<Ret, void>) {
253 if (!IsCompatibleWith<Ret>(context->GetResult())) {
254 return false;
255 }
256 }
257
258 if (!(true && ... && IsCompatibleWith<Args>(*params[Index].value))) {
259 return false;
260 }
261 return true;
262 }
263 static bool Call(const IFunction::Ptr& func)
264 {
265 if constexpr ((true && ... && HasUid_v<PlainType_t<Args>>)) {
266 return Call(func, MakeIndexSequenceFor<Args...>());
267 }
268 return false;
269 }
270 };
271
272 /**
273 * @brief Check if function is compatible with given signature (The return type and parameter types match).
274 */
275 template<typename FuncSignature>
276 bool IsFunctionCompatible(const IFunction::Ptr& func)
277 {
278 return IsFunctionCompatibleImpl<FuncSignature>::Call(func);
279 }
280
281 META_END_NAMESPACE()
282
283 #endif
284