1/****************************************************************************
2 * Copyright (C) 2016-2018 Woboq GmbH
3 * Olivier Goffart <ogoffart at woboq.com>
4 * https://woboq.com/
5 *
6 * This file is part of Verdigris: a way to use Qt without moc.
7 * https://github.com/woboq/verdigris
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22#pragma once
23
24#include "wobjectdefs.h"
25#include <QtCore/qobject.h>
26
27namespace w_internal {
28
29/** concatenate()
30 * Returns a StaticString which is the concatenation of all the strings in a StaticStringList
31 * Note: keeps the \0 between the strings
32 */
33template<typename I1, typename I2> struct concatenate_helper;
34template<std::size_t... I1, std::size_t... I2> struct concatenate_helper<std::index_sequence<I1...>, std::index_sequence<I2...>> {
35 static constexpr int size = sizeof...(I1) + sizeof...(I2);
36 static constexpr auto concatenate(const StaticString<sizeof...(I1)> &s1, const StaticString<sizeof...(I2)> &s2) {
37 StaticStringArray<size> d = { s1[I1]... , s2[I2]... };
38 return StaticString<size>( d );
39 }
40};
41constexpr StaticString<1> concatenate(StaticStringList<>) { return {""}; }
42template<typename T> constexpr auto concatenate(StaticStringList<binary::Leaf<T>> s) { return s.root.data; }
43template<typename A, typename B> constexpr auto concatenate(StaticStringList<binary::Node<A,B>> s) {
44 auto a = concatenate(binary::tree<A>{s.root.a});
45 auto b = concatenate(binary::tree<B>{s.root.b});
46 return concatenate_helper<make_index_sequence<a.size>, make_index_sequence<b.size>>::concatenate(a, b);
47}
48
49enum { IsUnresolvedType = 0x80000000, IsUnresolvedNotifySignal = 0x70000000 };
50
51/*
52 * The QMetaObject is basically an array of int and an array of string.
53 * Some of the int in the array are index in the string array.
54 *
55 * The IntermediateState class helps building the QMetaObject.
56 * It contains the StaticStringList of strings within this meta object, and the array of int as
57 * template parameter.
58 *
59 * It has helper method that helps extending the state
60 */
61template<typename Strings, uint... Ints>
62struct IntermediateState {
63 Strings strings;
64 /// add a string to the strings state and add its index to the end of the int array
65 template<std::size_t L>
66 constexpr auto addString(const StaticString<L> & s) const {
67 auto s2 = binary::tree_append(strings, s);
68 return IntermediateState<decltype(s2), Ints..., Strings::size>{s2};
69 }
70
71 /// same as before but add the IsUnresolvedType flag
72 template<uint Flag = IsUnresolvedType, std::size_t L>
73 constexpr auto addTypeString(const StaticString<L> & s) const {
74 auto s2 = binary::tree_append(strings, s);
75 return IntermediateState<decltype(s2), Ints...,
76 Flag | Strings::size>{s2};
77 }
78
79 template<uint... Add>
80 constexpr IntermediateState<Strings, Ints..., Add...> add() const
81 { return { strings }; }
82
83 static constexpr std::index_sequence<Ints ...> sequence = {};
84};
85
86/**
87 * Iterate over all the items of a tree and call the Generator::generate function
88 *
89 * The first parameter of the function is the IntermediateState, and it returns a new
90 * InterMediateState with all the information from the tree added to it.
91 *
92 * The 'Ofst' template parameter is the offset in the integer array to which new data can be added.
93 *
94 * The Generator template type has a method generate which will be called for every element of the
95 * tree with the IntermediateState. It is called with the offset as a template parameter.
96 *
97 * The Generator also has an offset() method that returns how much things will be added later in
98 * the array for this element.
99 */
100template<typename Generator, int Ofst, typename State>
101constexpr auto generate(State s, binary::tree<>)
102{ return s; }
103template<typename Generator, int Ofst, typename State, typename Tree>
104constexpr auto generate(State s, Tree t) {
105 return generate<Generator, Ofst + Generator::template offset<binary::tree_element_t<0,Tree>>()>(
106 Generator::template generate<Ofst>(s, binary::tree_head(t)), binary::tree_tail(t));
107}
108
109
110#if defined Q_CC_GNU && !defined Q_CC_CLANG /* Work around GCC bug 69681 */
111template <typename T> constexpr bool getSignalIndexHelperCompare(T f1, T f2) { return f1 == f2; }
112/** Helper to get information about the notify signal of the property within object T */
113template<typename T, int PropIdx, typename BaseT = T>
114struct ResolveNotifySignal {
115 static constexpr auto signalState = w_SignalState(w_number<>{},static_cast<BaseT**>(nullptr));
116 static constexpr auto propertyInfo = w_PropertyState(w_number<>{},static_cast<T**>(nullptr));
117private:
118 static constexpr auto prop = binary::get<PropIdx>(propertyInfo);
119 // We need to use SFINAE because of GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69681
120 // For some reason, GCC fails to consider f1==f2 as a constexpr if f1 and f2 are pointer to
121 // different function of the same type. Fortunately, when both are pointing to the same function
122 // it compiles fine, so we can use SFINAE for it.
123 template<int SigIdx,
124 bool Eq = getSignalIndexHelperCompare(binary::get<SigIdx>(signalState).func,
125 prop.notify)>
126 static constexpr int helper(int)
127 { return Eq ? SigIdx : -1; }
128 template<int SigIdx> static constexpr int helper(...) { return -1; }
129
130 template<size_t... I>
131 static constexpr int computeSignalIndex(index_sequence<I...>)
132 { return std::max({-1, helper<I>(0)...}); }
133public:
134 static constexpr int signalIndex()
135 { return computeSignalIndex(make_index_sequence<signalState.size>()); }
136};
137#else
138/** Helper comparator that compares function pointers and return true if they are the same.
139 * If thier type are different, pointer to member functions are different
140 */
141template <typename T> constexpr bool getSignalIndexHelperCompare(T f1, T f2) { return f1 == f2; }
142template <typename T1, typename T2> constexpr bool getSignalIndexHelperCompare(T1, T2) { return false; }
143
144/** Helper to get information about the notify signal of the property within object T */
145template<typename T>
146struct ResolveNotifySignal {
147 static constexpr auto signalState = w_SignalState(w_number<>{},static_cast<T**>(nullptr));
148private:
149 template<typename F, size_t... I>
150 static constexpr int computeSignalIndex(F f, index_sequence<I...>) {
151 return std::max({-1,
152 (getSignalIndexHelperCompare(binary::get<I>(signalState).func,f) ? int(I) : -1)...});
153 }
154public:
155 template<typename F>
156 static constexpr int signalIndex(F f)
157 { return computeSignalIndex(f, make_index_sequence<signalState.size>()); }
158};
159#endif
160
161/** returns true if the object T has at least one property with a notify signal */
162template <typename T, std::size_t... I>
163static constexpr bool hasNotifySignal(std::index_sequence<I...>)
164{
165 constexpr auto propertyInfo = w_PropertyState(w_number<>{},static_cast<T**>(nullptr));
166 Q_UNUSED(propertyInfo) // in case I is empty
167 return sums(!std::is_same<decltype(binary::get<I>(propertyInfo).notify), std::nullptr_t>::value ...);
168}
169
170/** Holds information about a class, including all the properties and methods */
171template<std::size_t NameLength, typename Methods, typename Constructors, typename Properties,
172 typename Enums, typename ClassInfos, typename Interfaces, int SignalCount>
173struct ObjectInfo {
174 StaticString<NameLength> name;
175 Methods methods;
176 Constructors constructors;
177 Properties properties;
178 Enums enums;
179 ClassInfos classInfos;
180 Interfaces interfaces;
181
182 static constexpr int methodCount = Methods::size;
183 static constexpr int constructorCount = Constructors::size;
184 static constexpr int propertyCount = Properties::size;
185 static constexpr int enumCount = Enums::size;
186 static constexpr int classInfoCount = ClassInfos::size;
187 static constexpr int interfaceCount = Interfaces::size;
188 static constexpr int signalCount = SignalCount;
189};
190
191/**
192 * Constructs a ObjectInfo for the object T.
193 * Pass the (qualified) name as a static string.
194 * Called from W_OBJECT_IMPL
195 */
196template<typename T, int N>
197static constexpr auto makeObjectInfo(StaticStringArray<N> &name) {
198 constexpr auto sigState = w_SignalState(w_number<>{}, static_cast<T**>(nullptr));
199 constexpr auto methodInfo = binary::tree_cat(sigState, w_SlotState(w_number<>{}, static_cast<T**>(nullptr)),
200 w_MethodState(w_number<>{}, static_cast<T**>(nullptr)));
201 constexpr auto constructorInfo = w_ConstructorState(w_number<>{}, static_cast<T**>(nullptr));
202 constexpr auto propertyInfo = w_PropertyState(w_number<>{}, static_cast<T**>(nullptr));
203 constexpr auto enumInfo = w_EnumState(w_number<>{}, static_cast<T**>(nullptr));
204 constexpr auto classInfo = w_ClassInfoState(w_number<>{}, static_cast<T**>(nullptr));
205 constexpr auto interfaceInfo = w_InterfaceState(w_number<>{}, static_cast<T**>(nullptr));
206 constexpr int sigCount = sigState.size;
207 return ObjectInfo<N, decltype(methodInfo), decltype(constructorInfo), decltype(propertyInfo),
208 decltype(enumInfo), decltype(classInfo), decltype(interfaceInfo), sigCount>
209 { {name}, methodInfo, constructorInfo, propertyInfo, enumInfo, classInfo, interfaceInfo };
210}
211
212// Generator for Q_CLASSINFO to be used in generate<>()
213struct ClassInfoGenerator {
214 template<typename> static constexpr int offset() { return 0; }
215 template<int, typename State, typename CI>
216 static constexpr auto generate(State s, CI ci) {
217 return s.addString(ci.first).addString(ci.second);
218 }
219};
220
221/* Helpers to auto-detect the access specifier */
222template <typename T, typename M, typename = void> struct isPublic : std::false_type {};
223template <typename T, typename M> struct isPublic<T, M, decltype(T::w_GetAccessSpecifierHelper(std::declval<M>()))> : std::true_type {};
224template <typename T, typename M, typename = void> struct isProtected : std::false_type {};
225template <typename T, typename = std::enable_if_t<!std::is_final<T>::value>>
226struct Derived : T { template<typename M, typename X = T> static decltype(X::w_GetAccessSpecifierHelper(std::declval<M>())) test(M); };
227template <typename T, typename M> struct isProtected<T, M, decltype(Derived<T>::test(std::declval<M>()))> : std::true_type {};
228
229// Generator for methods to be used in generate<>()
230template <typename T>
231struct MethodGenerator {
232 template<typename Method> static constexpr int offset() { return 1 + Method::argCount * 2; }
233 template<int ParamIndex, typename State, typename Method>
234 static constexpr auto generate(State s, Method method) {
235 return s.addString(method.name). // name
236 template add<Method::argCount,
237 ParamIndex, //parameters
238 1, //tag, always \0
239 adjustFlags(Method::flags, typename Method::IntegralConstant())>();
240 }
241 template<typename M>
242 static constexpr uint adjustFlags(uint f, M) {
243 if (!(f & (W_Access::Protected.value | W_Access::Private.value | W_Access::Public.value))) {
244 // Auto-detect the access specifier
245 f |= isPublic<T, M>::value ? W_Access::Public.value : isProtected<T,M>::value ? W_Access::Protected.value : W_Access::Private.value;
246 }
247 return f & static_cast<uint>(~W_Access::Private.value); // Because QMetaMethod::Private is 0, but not W_Access::Private;
248 }
249};
250
251/** Helper that computes if type T is a builtin QMetaType */
252template <typename T, typename = void> struct MetaTypeIdIsBuiltIn : std::false_type {};
253template <typename T> struct MetaTypeIdIsBuiltIn<T, typename std::enable_if<QMetaTypeId2<T>::IsBuiltIn>::type> : std::true_type{};
254
255/**
256 * Helper to generate the type information of type 'T':
257 * If T is a builtin QMetaType, its meta type id need to be added in the state.
258 * If it's not builtin, then it would be an reference to T's name in the string array.
259 */
260template<typename T, bool Builtin = MetaTypeIdIsBuiltIn<T>::value>
261struct HandleType {
262 template<typename S, typename TypeStr = int>
263 static constexpr auto result(const S&s, TypeStr = {})
264 { return s.template add<QMetaTypeId2<T>::MetaType>(); }
265};
266template<typename T>
267struct HandleType<T, false> {
268 template<typename Strings, typename TypeStr = int>
269 static constexpr auto result(const Strings &ss, TypeStr = {}) {
270 return ss.addTypeString(W_TypeRegistery<T>::name);
271 static_assert(W_TypeRegistery<T>::registered, "Please Register T with W_REGISTER_ARGTYPE");
272 }
273 template<typename Strings, std::size_t N>
274 static constexpr auto result(const Strings &ss, StaticString<N> typeStr,
275 typename std::enable_if<(N>1),int>::type=0) {
276 return ss.addTypeString(typeStr);
277 }
278};
279
280// Generator for properties to be used in generate<>()
281template<typename T>
282struct PropertyGenerator {
283 template<typename> static constexpr int offset() { return 0; }
284 template<int, typename State, typename Prop>
285 static constexpr auto generate(State s, Prop prop) {
286 auto s2 = s.addString(prop.name);
287 auto s3 = HandleType<typename Prop::PropertyType>::result(s2, prop.typeStr);
288 constexpr uint moreFlags = (QtPrivate::IsQEnumHelper<typename Prop::PropertyType>::Value
289 ? uint(PropertyFlags::EnumOrFlag) : 0);
290 constexpr uint finalFlag = std::is_final<T>::value ? 0 | PropertyFlags::Final : 0;
291 constexpr uint defaultFlags = 0 | PropertyFlags::Stored | PropertyFlags::Scriptable
292 | PropertyFlags::Designable;
293 return s3.template add<Prop::flags | moreFlags | finalFlag | defaultFlags>();
294 }
295};
296
297// Generator for notify signals to be used in generate
298template<typename T, bool hasNotify>
299struct NotifySignalGenerator {
300 template<typename> static constexpr int offset() { return 1; }
301 template<int Idx, typename State, typename Prop>
302 static constexpr auto generate(State s, Prop prop) {
303 return process<Idx>(s, prop.notify);
304 }
305private:
306
307 static constexpr auto propertyInfo = w_PropertyState(w_number<>{},static_cast<T**>(nullptr));
308
309 // No notify signal
310 template<int, typename State>
311 static constexpr auto process(State s, Empty) {
312 return s.template add<0>();
313 }
314
315 // Signal in the same class
316 template<int Idx, typename State, typename Func>
317 static constexpr auto process(State s, Func, std::enable_if_t<
318 std::is_same<T, typename QtPrivate::FunctionPointer<Func>::Object>::value, int> = 0)
319 {
320#if defined Q_CC_GNU && !defined Q_CC_CLANG
321 constexpr int signalIndex = ResolveNotifySignal<T, Idx>::signalIndex();
322#else
323 constexpr int signalIndex = ResolveNotifySignal<T>::signalIndex(binary::get<Idx>(propertyInfo).notify);
324#endif
325 static_assert(signalIndex >= 0, "NOTIFY signal not registered as a signal");
326 return s.template add<signalIndex>();
327 }
328
329 // Signal in a parent class
330 template<int Idx, typename State, typename Func>
331 static constexpr auto process(State s, Func, std::enable_if_t<
332 !std::is_same<T, typename QtPrivate::FunctionPointer<Func>::Object>::value, int> = 0)
333 {
334#if defined Q_CC_GNU && !defined Q_CC_CLANG
335 using Finder = ResolveNotifySignal<T, Idx, typename QtPrivate::FunctionPointer<Func>::Object>;
336 constexpr int signalIndex = Finder::signalIndex();
337#else
338 using Finder = ResolveNotifySignal<typename QtPrivate::FunctionPointer<Func>::Object>;
339 constexpr int signalIndex = Finder::signalIndex(binary::get<Idx>(propertyInfo).notify);
340#endif
341 static_assert(signalIndex >= 0, "NOTIFY signal in parent class not registered as a W_SIGNAL");
342 static_assert(signalIndex < 0 || QT_VERSION >= QT_VERSION_CHECK(5, 10, 0),
343 "NOTIFY signal in parent class requires Qt 5.10");
344 constexpr auto sig = binary::get<signalIndex>(Finder::signalState);
345 return s.template addTypeString<IsUnresolvedNotifySignal>(sig.name);
346 }
347};
348template<typename T> struct NotifySignalGenerator<T,false> {
349 template<typename> static constexpr int offset() { return 0; }
350 template<int, typename State, typename Prop>
351 static constexpr auto generate(State s, Prop) { return s; }
352};
353
354// Generator for enums to be used in generate<>()
355struct EnumGenerator {
356 template<typename Enum> static constexpr int offset() { return Enum::count * 2; }
357 template<int DataIndex, typename State, typename Enum>
358 static constexpr auto generate(State s, Enum e, std::enable_if_t<Enum::hasAlias> * = nullptr) {
359 return s.addString(e.name) // name
360#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
361 .addString(e.alias) // alias
362#endif
363 .template add<
364 Enum::flags,
365 Enum::count,
366 DataIndex
367 >();
368 }
369
370 template<int DataIndex, typename State, typename Enum>
371 static constexpr auto generate(State s, Enum e, std::enable_if_t<!Enum::hasAlias> * = nullptr) {
372 return s.addString(e.name) // name
373 .template add<
374#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
375 s.strings.size, // alias is the same as the name
376#endif
377 Enum::flags,
378 Enum::count,
379 DataIndex
380 >();
381 }
382
383};
384
385// Generator for enum values
386struct EnumValuesGenerator {
387 template<typename Strings>
388 static constexpr auto generateSingleEnumValues(const Strings &s, std::index_sequence<>, binary::tree<>)
389 { return s; }
390
391 template<typename Strings, std::size_t Value, std::size_t... I, typename Names>
392 static constexpr auto generateSingleEnumValues(const Strings &s, std::index_sequence<Value, I...>, Names names) {
393 auto s2 = s.addString(binary::tree_head(names)).template add<uint(Value)>();
394 return generateSingleEnumValues(s2, std::index_sequence<I...>{}, binary::tree_tail(names));
395 }
396
397 template<typename> static constexpr int offset() { return 0; }
398 template<int, typename State, typename Enum>
399 static constexpr auto generate(State s, Enum e) {
400 return generateSingleEnumValues(s, typename Enum::Values{}, e.names);
401 }
402};
403
404/**
405 * Helper classes for MethodParametersGenerator::generateSingleMethodParameter:
406 * generate the parameter array
407 */
408template<typename ...Args> struct HandleArgsHelper {
409 template<typename Strings, typename ParamTypes>
410 static constexpr auto result(const Strings &ss, const ParamTypes&)
411 { return ss; }
412};
413template<typename A, typename... Args>
414struct HandleArgsHelper<A, Args...> {
415 template<typename Strings, typename ParamTypes>
416 static constexpr auto result(const Strings &ss, const ParamTypes &paramTypes) {
417 using Type = typename QtPrivate::RemoveConstRef<A>::Type;
418 auto typeStr = binary::tree_head(paramTypes);
419 using ts_t = decltype(typeStr);
420 // This way, the overload of result will not pick the StaticString one if it is a tuple (because registered types have the priority)
421 auto typeStr2 = std::conditional_t<std::is_same<A, Type>::value, ts_t, std::tuple<ts_t>>{typeStr};
422 auto r1 = HandleType<Type>::result(ss, typeStr2);
423 return HandleArgsHelper<Args...>::result(r1, binary::tree_tail(paramTypes));
424 }
425};
426template<std::size_t N> struct HandleArgNames {
427 template<typename Strings, typename Str>
428 static constexpr auto result(const Strings &ss, StaticStringList<Str> pn)
429 {
430 auto s2 = ss.addString(binary::tree_head(pn));
431 auto tail = binary::tree_tail(pn);
432 return HandleArgNames<N-1>::result(s2, tail);
433 }
434 template<typename Strings> static constexpr auto result(const Strings &ss, StaticStringList<> pn)
435 { return HandleArgNames<N-1>::result(ss.template add<1>(), pn); }
436};
437template<> struct HandleArgNames<0> {
438 template<typename Strings, typename PN> static constexpr auto result(const Strings &ss, PN)
439 { return ss; }
440};
441
442// Generator for method parameters to be used in generate<>()
443struct MethodParametersGenerator {
444private:
445 // non-const function
446 template<typename Strings, typename ParamTypes, typename ParamNames, typename Obj, typename Ret, typename... Args>
447 static constexpr auto generateSingleMethodParameter(const Strings &ss, Ret (Obj::*)(Args...),
448 const ParamTypes &paramTypes, const ParamNames &paramNames ) {
449 auto types = HandleArgsHelper<Ret, Args...>::result(ss, binary::tree_prepend(paramTypes, 0));
450 return HandleArgNames<sizeof...(Args)>::result(types, paramNames);
451 }
452 template<typename Strings, typename ParamTypes, typename ParamNames, typename Obj, typename Ret, typename... Args>
453 // const function
454 static constexpr auto generateSingleMethodParameter(const Strings &ss, Ret (Obj::*)(Args...) const,
455 const ParamTypes &paramTypes, const ParamNames &paramNames ) {
456 auto types = HandleArgsHelper<Ret, Args...>::result(ss, binary::tree_prepend(paramTypes, 0));
457 return HandleArgNames<sizeof...(Args)>::result(types, paramNames);
458 }
459 // static member functions
460 template<typename Strings, typename ParamTypes, typename ParamNames, typename Ret, typename... Args>
461 static constexpr auto generateSingleMethodParameter(const Strings &ss, Ret (*)(Args...),
462 const ParamTypes &paramTypes, const ParamNames &paramNames ) {
463 auto types = HandleArgsHelper<Ret, Args...>::result(ss, binary::tree_prepend(paramTypes, 0));
464 return HandleArgNames<sizeof...(Args)>::result(types, paramNames);
465 }
466public:
467
468 template<typename> static constexpr int offset() { return 0; }
469 template<int, typename State, typename Method>
470 static constexpr auto generate(State s, Method method) {
471 // Dispatch either non const function, const function or static function
472 return generateSingleMethodParameter(s, method.func, method.paramTypes, method.paramNames);
473 }
474};
475
476// Generator for constructor parameter to be used in generate<>()
477struct ConstructorParametersGenerator {
478 template<typename> static constexpr int offset() { return 0; }
479 template<int, typename State, std::size_t N, typename... Args>
480 static constexpr auto generate(State s, MetaConstructorInfo<N,Args...>) {
481 auto s2 = s.template add<IsUnresolvedType | 1>();
482 auto s3 = HandleArgsHelper<Args...>::result(s2, binary::tree<>{});
483 return s3.template add<((void)sizeof(Args),1)...>(); // all the names are 1 (for "\0")
484 }
485};
486
487/** Given method, a binary::tree containing information about methods or constructor,
488 * return the amount of item it will add in the int array. */
489template<typename Methods, std::size_t... I>
490constexpr int paramOffset(std::index_sequence<I...>)
491{ return sums(int(1 + binary::tree_element_t<I, Methods>::argCount * 2)...); }
492
493/**
494 * Generate the integer array and the lists of string
495 */
496template<typename T, typename ObjI>
497constexpr auto generateDataArray(const ObjI &objectInfo) {
498
499 constexpr bool hasNotify = hasNotifySignal<T>(make_index_sequence<ObjI::propertyCount>{});
500 constexpr int classInfoOffstet = 14;
501 constexpr int methodOffset = classInfoOffstet + ObjI::classInfoCount * 2;
502 constexpr int propertyOffset = methodOffset + ObjI::methodCount * 5;
503 constexpr int enumOffset = propertyOffset + ObjI::propertyCount * (hasNotify ? 4: 3);
504 constexpr int constructorOffset = enumOffset + ObjI::enumCount * (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) ? 5 : 4);
505 constexpr int paramIndex = constructorOffset + ObjI::constructorCount * 5 ;
506 constexpr int constructorParamIndex = paramIndex +
507 paramOffset<decltype(objectInfo.methods)>(make_index_sequence<ObjI::methodCount>{});
508 constexpr int enumValueOffset = constructorParamIndex +
509 paramOffset<decltype(objectInfo.constructors)>(make_index_sequence<ObjI::constructorCount>{});
510
511 auto stringData = binary::tree_append(binary::tree_append(binary::tree<>{} , objectInfo.name), StaticString<1>(""));
512 IntermediateState<decltype(stringData),
513 QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) ? 8 : 7, // revision
514 0, // classname
515 ObjI::classInfoCount, classInfoOffstet, // classinfo
516 ObjI::methodCount, methodOffset, // methods
517 ObjI::propertyCount, propertyOffset, // properties
518 ObjI::enumCount, enumOffset, // enums/sets
519 ObjI::constructorCount, constructorOffset, // constructors
520 0x4 /* PropertyAccessInStaticMetaCall */, // flags
521 ObjI::signalCount
522 > header = { stringData };
523
524 auto classInfos = generate<ClassInfoGenerator, paramIndex>(header , objectInfo.classInfos);
525 auto methods = generate<MethodGenerator<T>, paramIndex>(classInfos , objectInfo.methods);
526 auto properties = generate<PropertyGenerator<T>, 0>(methods, objectInfo.properties);
527 auto notify = generate<NotifySignalGenerator<T, hasNotify>, 0>(properties, objectInfo.properties);
528 auto enums = generate<EnumGenerator, enumValueOffset>(notify, objectInfo.enums);
529 auto constructors = generate<MethodGenerator<T>, constructorParamIndex>(enums, objectInfo.constructors);
530 auto parametters = generate<MethodParametersGenerator, 0>(constructors, objectInfo.methods);
531 auto parametters2 = generate<ConstructorParametersGenerator, 0>(parametters, objectInfo.constructors);
532 auto enumValues = generate<EnumValuesGenerator, 0>(parametters2, objectInfo.enums);
533 return std::make_pair(enumValues.strings, enumValues.sequence);
534}
535
536/**
537 * Holder for the string data. Just like in the moc generated code.
538 */
539template<std::size_t N, std::size_t L> struct qt_meta_stringdata_t {
540 QByteArrayData data[N];
541 char stringdata[L];
542};
543
544/** Builds the string data
545 * \tparam S an index_sequence that goes from 0 to the full size of the strings
546 * \tparam I an index_sequence that goes from 0 to the number of string
547 * \tparam O an index_sequence of the offsets
548 * \tparam N an index_sequence of the size of each strings
549 * \tparam T the W_MetaObjectCreatorHelper
550 */
551template<typename S, typename I, typename O, typename N, typename T> struct BuildStringDataHelper;
552template<std::size_t... S, std::size_t... I, std::size_t... O, std::size_t...N, typename T>
553struct BuildStringDataHelper<std::index_sequence<S...>, std::index_sequence<I...>, std::index_sequence<O...>, std::index_sequence<N...>, T> {
554 using meta_stringdata_t = const qt_meta_stringdata_t<sizeof...(I), sizeof...(S)>;
555 static meta_stringdata_t qt_meta_stringdata;
556#ifndef Q_CC_MSVC
557 static constexpr qptrdiff stringdata_offset = offsetof(meta_stringdata_t, stringdata);
558#else // offsetof does not work with MSVC
559 static constexpr qptrdiff stringdata_offset = sizeof(meta_stringdata_t::data);
560#endif
561 static constexpr auto concatenated = concatenate(T::string_data);
562};
563template<std::size_t... S, std::size_t... I, std::size_t... O, std::size_t...N, typename T>
564const qt_meta_stringdata_t<sizeof...(I), sizeof...(S)>
565BuildStringDataHelper<std::index_sequence<S...>, std::index_sequence<I...>, std::index_sequence<O...>, std::index_sequence<N...>, T>::qt_meta_stringdata = {
566 {Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(N-1,
567 stringdata_offset + O - I * sizeof(QByteArrayData))...},
568 { concatenated[S]... }
569};
570
571/**
572 * Given N a list of string sizes, compute the list offsets to each of the strings.
573 */
574template<std::size_t... N> struct ComputeOffsets;
575template<> struct ComputeOffsets<> {
576 using Result = std::index_sequence<>;
577};
578template<std::size_t H, std::size_t... T> struct ComputeOffsets<H, T...> {
579 template<std::size_t ... I> static std::index_sequence<0, (I+H)...> func(std::index_sequence<I...>);
580 using Result = decltype(func(typename ComputeOffsets<T...>::Result()));
581};
582
583/**
584 * returns the string data suitable for the QMetaObject from a list of string
585 * T is W_MetaObjectCreatorHelper<ObjectType>
586 */
587// N... are all the sizes of the strings
588template<typename T, int... N>
589constexpr const QByteArrayData *build_string_data() {
590 return BuildStringDataHelper<make_index_sequence<sums(N...)>,
591 make_index_sequence<sizeof...(N)>,
592 typename ComputeOffsets<N...>::Result,
593 std::index_sequence<N...>,
594 T>
595 ::qt_meta_stringdata.data;
596}
597template<typename T, std::size_t... I>
598constexpr const QByteArrayData *build_string_data(std::index_sequence<I...>) {
599 return build_string_data<T, decltype(binary::get<I>(T::string_data))::size...>();
600}
601
602
603/**
604 * returns a pointer to an array of string built at compile time.
605 */
606template<typename I> struct build_int_data;
607template<std::size_t... I> struct build_int_data<std::index_sequence<I...>> {
608 static const uint data[sizeof...(I)];
609};
610template<std::size_t... I> const uint build_int_data<std::index_sequence<I...>>::data[sizeof...(I)] = { uint(I)... };
611
612/** Returns the QMetaObject* of the base type. Use SFINAE to only return it if it exists */
613template<typename T>
614static constexpr auto parentMetaObject(int) W_RETURN(&T::W_BaseType::staticMetaObject)
615template<typename T>
616static constexpr const QMetaObject *parentMetaObject(...) { return nullptr; }
617
618// Bunch of helpers for propertyOperation
619// this uses SFINAE of the return value to work either with F a pointer to member, or to member function
620// or nullptr.
621template <typename F, typename O, typename T>
622inline auto propSet(F f, O *o, const T &t) W_RETURN(((o->*f)(t),0))
623template <typename F, typename O, typename T>
624inline auto propSet(F f, O *o, const T &t) W_RETURN(o->*f = t)
625template <typename O, typename T>
626inline void propSet(Empty, O *, const T &) {}
627
628template <typename F, typename O, typename T>
629inline auto propGet(F f, O *o, T &t) W_RETURN(t = (o->*f)())
630template <typename F, typename O, typename T>
631inline auto propGet(F f, O *o, T &t) W_RETURN(t = o->*f)
632template <typename O, typename T>
633inline void propGet(Empty, O *, T &) {}
634
635template <typename F, typename M, typename O>
636inline auto propNotify(F f, M m, O *o) W_RETURN(((o->*f)(o->*m),0))
637template <typename F, typename M, typename O>
638inline auto propNotify(F f, M, O *o) W_RETURN(((o->*f)(),0))
639template <typename... T>
640inline void propNotify(T...) {}
641
642template <typename F, typename O>
643inline auto propReset(F f, O *o) W_RETURN(((o->*f)(),0))
644template <typename... T>
645inline void propReset(T...) {}
646
647struct FriendHelper {
648/** Instancies the QMetaObject for class T */
649template<typename T>
650static constexpr QMetaObject createMetaObject()
651{
652 using Creator = typename T::W_MetaObjectCreatorHelper;
653 auto string_data = build_string_data<Creator>(make_index_sequence<Creator::string_data.size>());
654 auto int_data = build_int_data<typename std::remove_const<decltype(Creator::int_data)>::type>::data;
655 return { { parentMetaObject<T>(0) , string_data , int_data, T::qt_static_metacall, {}, {} } };
656}
657
658/** Implementation of qt_metacall */
659template<typename T> static int qt_metacall_impl(T *_o, QMetaObject::Call _c, int _id, void** _a) {
660 using Creator = typename T::W_MetaObjectCreatorHelper;
661 _id = _o->T::W_BaseType::qt_metacall(_c, _id, _a);
662 if (_id < 0)
663 return _id;
664 if (_c == QMetaObject::InvokeMetaMethod || _c == QMetaObject::RegisterMethodArgumentMetaType) {
665 constexpr int methodCount = Creator::objectInfo.methodCount;
666 if (_id < methodCount)
667 T::qt_static_metacall(_o, _c, _id, _a);
668 _id -= methodCount;
669 } else if ((_c >= QMetaObject::ReadProperty && _c <= QMetaObject::QueryPropertyUser)
670 || _c == QMetaObject::RegisterPropertyMetaType) {
671 constexpr int propertyCount = Creator::objectInfo.propertyCount;
672 if (_id < propertyCount)
673 T::qt_static_metacall(_o, _c, _id, _a);
674 _id -= propertyCount;
675 }
676 return _id;
677}
678
679/**
680 * Helper for implementation of qt_static_metacall for QMetaObject::IndexOfMethod
681 * T is the class, and I is the index of a method.
682 * Returns I if the argument is equal to the pointer to member function of the signal of index 'I'
683 */
684template<typename T, int I>
685static int indexOfMethod(void **func) {
686 constexpr auto f = binary::get<I>(T::W_MetaObjectCreatorHelper::objectInfo.methods).func;
687 using Ms = decltype(T::W_MetaObjectCreatorHelper::objectInfo.methods);
688 if ((binary::tree_element_t<I,Ms>::flags & 0xc) == W_MethodType::Signal.value
689 && f == *reinterpret_cast<decltype(f)*>(func))
690 return I;
691 return -1;
692}
693
694/**
695 * Helper for implementation of qt_static_metacall for QMetaObject::InvokeMetaMethod
696 * T is the class, and I is the index of a method.
697 * Invoke the method with index I if id == I.
698 */
699template <typename T, int I>
700static void invokeMethod(T *_o, int _id, void **_a) {
701 if (_id == I) {
702 constexpr auto f = binary::get<I>(T::W_MetaObjectCreatorHelper::objectInfo.methods).func;
703 using P = QtPrivate::FunctionPointer<std::remove_const_t<decltype(f)>>;
704 P::template call<typename P::Arguments, typename P::ReturnType>(f, _o, _a);
705 }
706}
707
708/**
709 * Helper for implementation of qt_static_metacall for QMetaObject::RegisterMethodArgumentMetaType
710 * T is the class, and I is the index of a method.
711 */
712template <typename T, int I>
713static void registerMethodArgumentType(int _id, void **_a) {
714 if (_id == I) {
715 constexpr auto f = binary::get<I>(T::W_MetaObjectCreatorHelper::objectInfo.methods).func;
716 using P = QtPrivate::FunctionPointer<std::remove_const_t<decltype(f)>>;
717 auto _t = QtPrivate::ConnectionTypes<typename P::Arguments>::types();
718 uint arg = *reinterpret_cast<uint*>(_a[1]);
719 *reinterpret_cast<int*>(_a[0]) = _t && arg < P::ArgumentCount ?
720 _t[arg] : -1;
721 }
722}
723
724/**
725 * Helper for implementation of qt_static_metacall for any of the operations in a property
726 * T is the class, and I is the index of a property.
727 */
728template<typename T, int I>
729static void propertyOperation(T *_o, QMetaObject::Call _c, int _id, void **_a) {
730 if (_id != I)
731 return;
732 constexpr auto p = binary::get<I>(T::W_MetaObjectCreatorHelper::objectInfo.properties);
733 using Type = typename decltype(p)::PropertyType;
734 switch(+_c) {
735 case QMetaObject::ReadProperty:
736 if (p.getter) {
737 propGet(p.getter, _o, *reinterpret_cast<Type*>(_a[0]));
738 } else if (p.member) {
739 propGet(p.member, _o, *reinterpret_cast<Type*>(_a[0]));
740 }
741 break;
742 case QMetaObject::WriteProperty:
743 if (p.setter) {
744 propSet(p.setter, _o, *reinterpret_cast<Type*>(_a[0]));
745 } else if (p.member) {
746 propSet(p.member, _o, *reinterpret_cast<Type*>(_a[0]));
747 propNotify(p.notify, p.member, _o);
748 }
749 break;
750 case QMetaObject::ResetProperty:
751 if (p.reset) {
752 propReset(p.reset, _o);
753 }
754 break;
755 case QMetaObject::RegisterPropertyMetaType:
756 *reinterpret_cast<int*>(_a[0]) = QtPrivate::QMetaTypeIdHelper<Type>::qt_metatype_id();
757 }
758}
759
760/**
761 * Helper for implementation of qt_static_metacall for QMetaObject::CreateInstance
762 * T is the class, and I is the index of a constructor.
763 */
764template<typename T, int I>
765static void createInstance(int _id, void** _a) {
766 if (_id == I) {
767 constexpr auto m = binary::get<I>(T::W_MetaObjectCreatorHelper::objectInfo.constructors);
768 m.template createInstance<T>(_a, make_index_sequence<decltype(m)::argCount>{});
769 }
770}
771
772/**
773 * Function that does nothing but helps to expand template packs.
774 */
775template<typename...Ts> static constexpr void nop(Ts...) {}
776
777/**
778 * Implementation of qt_static_metacall for W_OBJECT_IMPL which should be called with
779 * std::index_sequence for the amount of method, constructor, and properties.
780 */
781template<typename T, size_t...MethI, size_t ...ConsI, size_t...PropI>
782static void qt_static_metacall_impl(QObject *_o, QMetaObject::Call _c, int _id, void** _a,
783 std::index_sequence<MethI...>, std::index_sequence<ConsI...>, std::index_sequence<PropI...>) {
784 Q_UNUSED(_id) Q_UNUSED(_o) Q_UNUSED(_a)
785 if (_c == QMetaObject::InvokeMetaMethod) {
786 Q_ASSERT(T::staticMetaObject.cast(_o));
787 nop((invokeMethod<T, MethI>(reinterpret_cast<T*>(_o), _id, _a),0)...);
788 } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
789 nop((registerMethodArgumentType<T,MethI>(_id, _a),0)...);
790 } else if (_c == QMetaObject::IndexOfMethod) {
791 *reinterpret_cast<int *>(_a[0]) = sums((1+indexOfMethod<T,MethI>(reinterpret_cast<void **>(_a[1])))...)-1;
792 } else if (_c == QMetaObject::CreateInstance) {
793 nop((createInstance<T, ConsI>(_id, _a),0)...);
794 } else if ((_c >= QMetaObject::ReadProperty && _c <= QMetaObject::QueryPropertyUser)
795 || _c == QMetaObject::RegisterPropertyMetaType) {
796 nop((propertyOperation<T,PropI>(static_cast<T*>(_o), _c, _id, _a),0)...);
797 }
798}
799
800/** Same for W_GADGET */
801template<typename T, size_t...MethI, size_t ...ConsI, size_t...PropI>
802static void qt_static_metacall_impl(T *_o, QMetaObject::Call _c, int _id, void** _a,
803 std::index_sequence<MethI...>, std::index_sequence<ConsI...>, std::index_sequence<PropI...>) {
804 Q_UNUSED(_id) Q_UNUSED(_o) Q_UNUSED(_a)
805 if (_c == QMetaObject::InvokeMetaMethod) {
806 nop((invokeMethod<T, MethI>(_o, _id, _a),0)...);
807 } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
808 nop((registerMethodArgumentType<T,MethI>(_id, _a),0)...);
809 } else if (_c == QMetaObject::IndexOfMethod) {
810 Q_ASSERT_X(false, "qt_static_metacall", "IndexOfMethod called on a Q_GADGET");
811 } else if (_c == QMetaObject::CreateInstance) {
812 nop((createInstance<T, ConsI>(_id, _a),0)...);
813 } else if ((_c >= QMetaObject::ReadProperty && _c <= QMetaObject::QueryPropertyUser)
814 || _c == QMetaObject::RegisterPropertyMetaType) {
815 nop((propertyOperation<T,PropI>(_o, _c, _id, _a),0)...);
816 }
817}
818
819/** helper for the implementation of qt_metacast */
820template <typename Interface, typename T>
821static void interfaceMetaCast(void *&result, T *o, const char *clname) {
822 const char *iid = qobject_interface_iid<Interface>();
823 if (iid && !strcmp(clname, iid))
824 result = static_cast<Interface>(o);
825}
826
827/** implementation of qt_metacast */
828template<typename T, size_t... InterfaceI>
829static void *qt_metacast_impl(T *o, const char *_clname, std::index_sequence<InterfaceI...>) {
830 if (!_clname)
831 return nullptr;
832 const QByteArrayDataPtr sd = { const_cast<QByteArrayData*>(T::staticMetaObject.d.stringdata) };
833 if (_clname == QByteArray(sd))
834 return o;
835 void *result = nullptr;
836 nop((interfaceMetaCast<decltype(binary::get<InterfaceI>(T::W_MetaObjectCreatorHelper::objectInfo.interfaces))>(result, o, _clname),0)...);
837 return result ? result : o->T::W_BaseType::qt_metacast(_clname);
838}
839
840}; // FriendHelper
841
842// Forward to the FriendHelper
843template<typename T> constexpr auto createMetaObject() { return FriendHelper::createMetaObject<T>(); }
844template<typename T, typename... Ts> auto qt_metacall_impl(Ts &&...args)
845{ return FriendHelper::qt_metacall_impl<T>(std::forward<Ts>(args)...); }
846template<typename T> auto qt_metacast_impl(T *o, const char *_clname) {
847 using ObjI = decltype(T::W_MetaObjectCreatorHelper::objectInfo);
848 return FriendHelper::qt_metacast_impl<T>(o, _clname,
849 make_index_sequence<ObjI::interfaceCount>{});
850}
851template<typename T, typename... Ts> auto qt_static_metacall_impl(Ts &&... args) {
852 using ObjI = decltype(T::W_MetaObjectCreatorHelper::objectInfo);
853 return FriendHelper::qt_static_metacall_impl<T>(std::forward<Ts>(args)...,
854 make_index_sequence<ObjI::methodCount>{},
855 make_index_sequence<ObjI::constructorCount>{},
856 make_index_sequence<ObjI::propertyCount>{});
857}
858} // w_internal
859
860#ifndef QT_INIT_METAOBJECT // Defined in qglobal.h since Qt 5.10
861#define QT_INIT_METAOBJECT
862#endif
863
864// W_OBJECT_IMPL was designed to take advantage of the GNU extension that ... can have zero arguments.
865// So we need to work around that to extract the template stuff which may not exist or be composed
866// of several macro arguments: If the first argument has parentheses, there must be at least two
867// arguments, so just do a tail. Otherwise, there should be only one or two argument, so take the second.
868#define W_MACRO_TEMPLATE_STUFF(...) W_MACRO_CONCAT(W_MACRO_TEMPLATE_STUFF_HELPER, W_MACRO_DELAY(W_MACRO_TEMPLATE_STUFF_QUERY,W_MACRO_TEMPLATE_STUFF_HELPER __VA_ARGS__))(__VA_ARGS__)
869#define W_MACRO_TEMPLATE_STUFF_QUERY(...) W_MACRO_DELAY2(W_MACRO_FIRST, W_MACRO_TEMPLATE_STUFF_HELPER_ ## __VA_ARGS__)
870#define W_MACRO_TEMPLATE_STUFF_HELPER(...) YES
871#define W_MACRO_TEMPLATE_STUFF_HELPER_YES TAIL,
872#define W_MACRO_TEMPLATE_STUFF_HELPER_W_MACRO_TEMPLATE_STUFF_HELPER SECOND,
873#define W_MACRO_TEMPLATE_STUFF_HELPER_TAIL(...) W_MACRO_MSVC_EXPAND(W_MACRO_TAIL(__VA_ARGS__))
874#define W_MACRO_TEMPLATE_STUFF_HELPER_SECOND(...) W_MACRO_MSVC_EXPAND(W_MACRO_TEMPLATE_STUFF_HELPER_SECOND2(__VA_ARGS__,,))
875#define W_MACRO_TEMPLATE_STUFF_HELPER_SECOND2(A,B,...) B
876#define W_MACRO_FIRST_REMOVEPAREN(...) W_MACRO_REMOVEPAREN(W_MACRO_FIRST(__VA_ARGS__))
877
878#define W_OBJECT_IMPL_COMMON(...) \
879 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) struct W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::W_MetaObjectCreatorHelper { \
880 static constexpr auto objectInfo = \
881 w_internal::makeObjectInfo<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::W_ThisType>(W_MACRO_STRIGNIFY(W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__))); \
882 static constexpr auto data = w_internal::generateDataArray<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::W_ThisType>(objectInfo); \
883 static constexpr auto string_data = data.first; \
884 static constexpr auto int_data = data.second; \
885 }; \
886 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) const QT_INIT_METAOBJECT QMetaObject W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::staticMetaObject = \
887 w_internal::createMetaObject<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::W_ThisType>();
888
889/** \macro W_OBJECT_IMPL(TYPE [, TEMPLATE_STUFF])
890 * This macro expand to the code that instantiate the QMetaObject. It must be placed outside of
891 * the class, in the .cpp file. The TYPE argument must be the qualified name of the class,
892 * including the namespace, if any.
893 * Example: `W_OBJECT_IMPL(Namespace::MyClass)`
894 *
895 * If the class is a templated class, the second argument contains the template introducer.
896 * Example: `W_OBJECT_IMPL(MyTemplate<T>, template <typename T>)`
897 * Parentheses are required if there is several template arguments:
898 * `W_OBJECT_IMPL((MyTemplate2<A,B>), template<typename A, typename B>)`
899 */
900#define W_OBJECT_IMPL(...) \
901 W_OBJECT_IMPL_COMMON(__VA_ARGS__) \
902 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) void W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void** _a) \
903 { w_internal::qt_static_metacall_impl<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)>(_o, _c, _id, _a); } \
904 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) const QMetaObject *W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::metaObject() const { return &staticMetaObject; } \
905 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) void *W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::qt_metacast(const char *_clname) \
906 { return w_internal::qt_metacast_impl<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)>(this, _clname); } \
907 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) int W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::qt_metacall(QMetaObject::Call _c, int _id, void** _a) \
908 { return w_internal::qt_metacall_impl<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)>(this, _c, _id, _a); }
909
910
911/** \macro W_OBJECT_IMPL(TYPE [, TEMPLATE_STUFF])
912 * Same as W_OBJECT_IMPL, but for a W_GADGET
913 */
914#define W_GADGET_IMPL(...) \
915 W_OBJECT_IMPL_COMMON(__VA_ARGS__) \
916 W_MACRO_TEMPLATE_STUFF(__VA_ARGS__) void W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void** _a) \
917 { w_internal::qt_static_metacall_impl<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)>(reinterpret_cast<W_MACRO_FIRST_REMOVEPAREN(__VA_ARGS__)*>(_o), _c, _id, _a); }
918
919/** \macro W_NAMESPACE_IMPL(...)
920 * Same as W_OBJECT_IMPL, but for a W_NAMESPACE
921 */
922#define W_NAMESPACE_IMPL(...) \
923 W_OBJECT_IMPL_COMMON(__VA_ARGS__)
924
925