Src\qtbase\src\corelib\kernel\qobjectdefs_impl.h QtPrivate 解读 “SignalArgs” : 此包扩展中的元素数与“II”中的元素数不匹配

69 篇文章 12 订阅

 RemoveRef去除变量的引用属性。

    template <typename T> struct RemoveRef { typedef T Type; };
    template <typename T> struct RemoveRef<T&> { typedef T Type; };
    template <typename T> struct RemoveConstRef { typedef T Type; };
    template <typename T> struct RemoveConstRef<const T&> { typedef T Type; };


void fun(T a) //假设传入的是int &,则获取到的Type为int。需要注意的是RemoveRef和RemoveConstRef不接受指针类型。
{
    removeRef<T>::Type va = a;
}

用模板实现的简单的List。List 并不占用内存,目的是用来操作可变参数的类型。模板的目的是在编译时期将一些可预见的操作完成,从而提升程序运行时的速度。 

    template <typename...> struct List {};
    template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; };
    template <typename, typename> struct List_Append;
    template <typename... L1, typename...L2> struct List_Append<List<L1...>, List<L2...>> { typedef List<L1..., L2...> Value; };
    template <typename L, int N> struct List_Left {
        typedef typename List_Append<List<typename L::Car>,typename List_Left<typename L::Cdr, N - 1>::Value>::Value Value;
    };
    template <typename L> struct List_Left<L, 0> { typedef List<> Value; };
    // List_Select<L,N> returns (via typedef Value) the Nth element of the list L
    template <typename L, int N> struct List_Select { typedef typename List_Select<typename L::Cdr, N - 1>::Value Value; };
    template <typename L> struct List_Select<L,0> { typedef typename L::Car Value; };


template<typename ...Args>
void funxx(Args... args)
{
	typedef List<Args...> L;  //用Args...的类型做成一个模板List,这个List,List并不占用实际
//大小,只是为了一些操作而设计这样的模板。
	cout << typeid(List_Select<L, 3>::Value).name() << endl; //打印第3个元素
//模板的展开过程是在编译阶段实现的。编译完成后这两行语句等价于:
    cout<<typeid(3.1).name()<<endl;

/*可以看到两者的反汇编是一样的:
	typedef List<Args...> L;
	cout << typeid(List_Select<L, 2>::Value).name() << endl;
00007FF695B2360B  lea         rcx,[double `RTTI Type Descriptor' (07FF695B3A278h)]  
00007FF695B23612  call        type_info::name (07FF695B2187Fh)  
00007FF695B23617  mov         rdx,rax  
00007FF695B2361A  mov         rcx,qword ptr [__imp_std::cout (07FF695B3D198h)]  
00007FF695B23621  call        std::operator<<<std::char_traits<char> > (07FF695B212B7h)  
00007FF695B23626  lea         rdx,[std::endl<char,std::char_traits<char> > (07FF695B21186h)]  
00007FF695B2362D  mov         rcx,rax  
00007FF695B23630  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF695B3D1C0h)]  
	cout << typeid(3.1).name() << endl;
00007FF695B23636  lea         rcx,[double `RTTI Type Descriptor' (07FF695B3A278h)]  
00007FF695B2363D  call        type_info::name (07FF695B2187Fh)  
00007FF695B23642  mov         rdx,rax  
00007FF695B23645  mov         rcx,qword ptr [__imp_std::cout (07FF695B3D198h)]  
00007FF695B2364C  call        std::operator<<<std::char_traits<char> > (07FF695B212B7h)  
00007FF695B23651  lea         rdx,[std::endl<char,std::char_traits<char> > (07FF695B21186h)]  
	cout << typeid(3.1).name() << endl;
00007FF695B23658  mov         rcx,rax  
00007FF695B2365B  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF695B3D1C0h)]  
*/

}

/*List_Select<L,3>::Value 递归展开过程
List_Select<L,3>::Value 的类型为 List_Select<L::Cdr,2>::Value
List_Select<L::Cdr,2>::Value 的类型为 List_Select<L::Cdr::Cdr,1>::Value
List_Select<L::Cdr::Cdr,1>::Value 的类型为 List_Select<L::Cdr::Cdr::Cdr,0>::Value
List_Select<L::Cdr::Cdr::Cdr::Cdr,0>::Value 的类型为 L::Cdr::Cdr::Cdr::Car
所以List_Select<L,3>::Value  的类型为L::Cdr::Cdr::Cdr::Car 
*/
//List_Left也是采用了类似的递归展开过程。

int main()
{
    funxx("ef", 1.0f, 3.1, 'a', 4);  //输出char
    getchar();
    return 0;
}

 逗号(,)表达式重载。

template <typename T>
struct ApplyReturnValue {
	void *data;
	explicit ApplyReturnValue(void *data_) : data(data_) {}
};
template<typename T, typename U>  //,表达式重载。如果有返回值,将返回值放到containner中,
void operator,(T &&value, const ApplyReturnValue<U> &container) {
	if (container.data)
		*reinterpret_cast<U *>(container.data) = std::forward<T>(value);
}
template<typename T>  //如果没有返回值,不做操作,
void operator,(T, const ApplyReturnValue<void> &) {}



typedef int(*funzType)(int);

int funz(int a)
{
	//cout << a << endl;
	return a + 1;
}

class KK
{
public:
	template<typename U>
	static void call(funzType f, U *arg)
	{
		f(arg[1]), ApplyReturnValue<U>(&arg[0]);
	}
};

int main()
{
    int a[2] = { 0,5};
	KK::call<int>(funz, a);
	cout << a[0] << endl;//输出6
    return 0;
}

Indexes<N>  生成有序下标序列

template<class T> using InvokeGenSeq = typename T::Type;

template<int...args> struct IndexesList {
	using Type = IndexesList;
};

template<int N, class S1, class S2> struct ConcatSeqImpl;

template<int N, int... I1, int... I2>
struct ConcatSeqImpl<N, IndexesList<I1...>, IndexesList<I2...>>
	: IndexesList<I1..., (N + I2)...> {};

template<int N, class S1, class S2>
using ConcatSeq = InvokeGenSeq<ConcatSeqImpl<N, S1, S2>>;//ConcatSeqImpl是IndexesList的子类,通过InvokeGenSeq获取到ConcatSeqImpl中的Type,也就是合成后的IndexsList。

template<int N> struct GenSeq;
template<int N> using makeIndexSequence = InvokeGenSeq<GenSeq<N>>;

template<int N>
struct GenSeq : ConcatSeq<N / 2, makeIndexSequence<N / 2>, makeIndexSequence<N - N / 2>> {};

template<> struct GenSeq<0> : IndexesList<> {};
template<> struct GenSeq<1> : IndexesList<0> {};

template<int N>
struct Indexes { using Value = makeIndexSequence<N>; };



typedef IndexesList<1, 2, 3>::Type T1; //等价于typedef IndexesList<1, 2, 3> T1
typedef IndexesList<4, 5, 6>::Type T2;//等价于typedef IndexesList<4, 5, 6> T2
typedef ConcatSeqImpl<3, T1, T2>::Type T3;//等价于typedef IndexesList<1,2,3,7,8,9> T3
typedef InvokeGenSeq<ConcatSeqImpl<3, T1, T2>>::Type T4;//等价于typedef IndexesList<1,2,3,7,8,9> T4
typedef ConcatSeq<3, T1, T2>::Type T5;//等价于typedef IndexesList<1,2,3,7,8,9> T5
typedef makeIndexSequence<4>::Type T6;//等价于typedef IndexesList<0,1,2,3> T6
typedef GenSeq<10>::Type T7;//等价于typedef IndexesList<0,1,2,3,4,5,6,7,8,9> T7
typedef Indexes<10>::Value::Type T8;//等价于typedef IndexesList<0,1,2,3,4,5,6,7,8,9> T8

参数包展开:可变模板参数_Jeff_的博客-CSDN博客_模板可变参数 

FunctionPointer和FunctorCall 模板类使用。
“编译器报错:  “SignalArgs” : 此包扩展中的元素数与“II”中的元素数不匹配”   排除。

//定义template<typename Func> struct FunctionPointer的意义是增加程序的容错能力,比如传入int
//类型数据,就会出现ArgumentCount = -1, IsPointerToMemberFunction = false 
template<typename Func> struct FunctionPointer { enum { ArgumentCount = -1, IsPointerToMemberFunction = false }; };

template <typename, typename, typename, typename> struct FunctorCall;
template <int... II, typename... SignalArgs, typename R, typename Function>
struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> {
	static void call(Function &f, void **arg) {
		f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II + 1]))...), ApplyReturnValue<R>(arg[0]);
	}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet(Obj::*)(SlotArgs...)> {
//IndexesList<II...>, List<SignalArgs...> 两者的size必须要一致。如果两者数量不一致编译时会报
// “SignalArgs” : 此包扩展中的元素数与“II”中的元素数不匹配
	static void call(SlotRet(Obj::*f)(SlotArgs...), Obj *o, void **arg) {
		//cout<<typeid(IndexesList<II...>).name()<<endl<<typeid(List<SignalArgs...>).name()<<endl;
		(o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II + 1]))...), ApplyReturnValue<R>(arg[0]);//
	}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet(Obj::*)(SlotArgs...) const> {
	static void call(SlotRet(Obj::*f)(SlotArgs...) const, Obj *o, void **arg) {
		(o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II + 1]))...), ApplyReturnValue<R>(arg[0]);
	}
};
#if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet(Obj::*)(SlotArgs...) noexcept> {
	static void call(SlotRet(Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) {
		(o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II + 1]))...), ApplyReturnValue<R>(arg[0]);
	}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet(Obj::*)(SlotArgs...) const noexcept> {
	static void call(SlotRet(Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) {
		(o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II + 1]))...), ApplyReturnValue<R>(arg[0]);
	}
};
#endif

template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret(Obj::*) (Args...)>  
//<Ret(Obj::*) (Args...)>描述一个类成员函数指针,当代码中使用FunctionPointer<FunPtr>时,
//编译器就会自动推导匹配Ret 、Obj、Args...,而不用手动去写出。FunctionPointer<Ret 、Obj、Args...>
//所以r<Ret(Obj::*) (Args...)> 的意义虽然是非必须的,但是可以辅助编译器进行类型推导。
{
	typedef Obj Object;
	typedef List<Args...>  Arguments;
	typedef Ret ReturnType;
	typedef Ret(Obj::*Function) (Args...);
	enum { ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true };
	template <typename SignalArgs, typename R>
	static void call(Function f, Obj *o, void **arg) {
		FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
	}
};
template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret(Obj::*) (Args...) const>
{
	typedef Obj Object;
	typedef List<Args...>  Arguments;
	typedef Ret ReturnType;
	typedef Ret(Obj::*Function) (Args...) const;
	enum { ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true };
	template <typename SignalArgs, typename R>
	static void call(Function f, Obj *o, void **arg) {
		FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
	}
};

template<typename Ret, typename... Args> struct FunctionPointer<Ret(*) (Args...)>
{
	typedef List<Args...> Arguments;
	typedef Ret ReturnType;
	typedef Ret(*Function) (Args...);
	enum { ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false };
	template <typename SignalArgs, typename R>
	static void call(Function f, void *, void **arg) {
		FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg);
	}
};


//测试代码
//为了方便简化调试,理解代码逻辑,模仿了一个类。不像上面定义的FunctionPointer类,调用
//FunctionPointer1时需要手动写出所有模板参数。而不能只传入一个函数指针参数。
template<class Obj, typename Ret, typename... Args> struct FunctionPointer1 
{
	typedef Obj Object;
	typedef List<Args...>  Arguments;
	typedef Ret ReturnType;
	typedef Ret(Obj::*Function) (Args...);
	enum { ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true };
	template <typename SignalArgs, typename R>
	static void call(Function f, Obj *o, void **arg) {
		FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
	}
};

class FBase
{
public:
	template<typename T ,typename... Args>
	static void connect(FBase *sender, T a,Args ...args)
	{
		int ret = 0;
		int *empty_args[2] = {&ret,0};
		typedef FunctionPointer1<FBase, T, Args ...> FunP;
		//cout << typeid(FunP::ReturnType).name() << endl;
		typedef FunP::Function FunType;
		//cout << typeid(FunType).name() << endl;
		FunType tt = &FBase::test;
		//cout << FunP::ArgumentCount << endl;
		//SignalArgs 从FunctionPointer的代码中可以看到不是定义为一个可变参数包,但使用的时候需要与args的数量一致,
		//否则编译器会报错“SignalArgs” : 此包扩展中的元素数与“II”中的元素数不匹配
		//我在一开始使用的时候直接将Args...传给call,一直导致报错。后来发现调用无参函数的时候可以传入List<>。
		//还需要注意,无参传入的时候,不能将FunctionPointer1模板中的Args ...替换成void或者List<>,Args...能传入为空,
		//计算sizeof...(Args)的时候大小能为0,而传入void或者List<>,sizeof...(Args)大小会变成1,这又会导致
		//编译器报错: “SignalArgs” : 此包扩展中的元素数与“II”中的元素数不匹配
        //需要注意的是SignalArgs不是可变参数,二就是一个普通类型参数,在代码中它的类型为List<...>。
		//List<Args ...>获取Args中的变量类型并将整体作为一个List类型
		//调用模板类中的模板函数需要加template关键字,否则可能会识别不出来。
		FunctionPointer1<FBase,T, Args ...>::template call<List<Args ...>, T>(tt, sender, (void**)&empty_args);
		cout << ret << endl;
	}

	template<typename Func,typename ...Args>//,typename FF=List<> 
	static void connenct1(FBase*sender, Func func, Args ...args)
	{
		int ret = 0;
		int *targs[] = { &ret,&args... };
		typedef FunctionPointer<Func>::ReturnType T;
		//模板会自动匹配到template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret(Obj::*) (Args...)>
		//区分两个概念,模板类中的成员函数 与 模板类中的模板函数(模板中的模板)。
		//调用模板类中的模板函数需要加template关键字,否则可能会识别不出来。
		//FunctionPointer<Func>::call<Args, T>(func, sender, (void**)&empty_args);
		FunctionPointer<Func>::template call<List<Args ...>, T>(func, sender, (void**)&targs);//Args...
		cout << ret << endl;
	}

	int test()
	{
		cout << "TT::test()" << endl;
		return 22;
	}

	int test1(int t)
	{
		cout << "TT::test()" << endl;
		return t+22;
	}
};


class FBase;
typedef int(FBase::*FunType)(int);//定义FBase类的成员函数指针类型

int main()
{
	FBase t;
	FunType tt = &FBase::test1;
	//调用模板类中的模板函数需要加template关键字,否则可能会识别不出来。
	FBase::template connect(&t,1);
	FBase::template connenct1(&t, tt,55);
    getchar();
    return 0;
}

Functor

template<typename Function, int N> struct Functor
{
	template <typename SignalArgs, typename R>
	static void call(Function &f, void *, void **arg) {
		FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg);
	}
};

//与FunctionPointer使用类似。
void tun(int a, int b)
{
	cout << a << "  " << b << endl;
}

typedef void(*tunType)(int, int);

template<typename T,typename Func,typename ...Args>
void myCall(T &ret,Func &f,Args... args) //ret 是用于接收存放f返回的内容的,所以类型一般要与f
//的返回值类型一致,但是当f的返回值类型为void时,在这里使用上就会出现问题。为了解决这个问题,让外
//部传入ret来保证ret不是void类型。
{
	typedef FunctionPointer<Func>::ReturnType R;
    
    //typedef std::conditional<std::is_void<T>::value, int, R>::type T1;
    //T1 ret;  //利用std库中的conditional消除void类型的可能。
    
    //R ret; ret不能为void类型。而函数返回值可能为void,程序中的argv要求必须预留一个返回值空
//间。所以就让外部来给出ret的空间。T&保证ret不是右值引用。
	void *argv[] = { &ret,&args...};
	typedef List<Args...> IArgsTypes;
	Functor<Func, sizeof...(args)>::template call<IArgsTypes, R>(f, NULL, argv);
}

class BaseH{};

int main()
{

	int a = 10;
	int b = 10;
	int ret;
	void *args[] = { &ret,&a,&b };
	typedef List<int, int> IArgsTypes;  //传入List中的参数类型和数量要与tunType中的类型数量一致。
	tunType tt = &tun;
	Functor1<tunType, 2>::template call<IArgsTypes, void>(tt, NULL, args);
    myCall(ret,tt, a, b); //因为tunType的返回值为void,并不会用到返回值的空间,但是程序强制要
//求需要一个返回值空间,所以此时随便传入一个变量都没有关系。
	int *ptr = nullptr;
	myCall(ptr, tt, a, b); //传入指针引用,函数中的指针的地址与&ptr的是一样的,所以C++中存在指针引用。
	BaseH ba;
	myCall(ba, tt, a, b);
}

AreArgumentsCompatible 判断参数是否兼容

    template<typename A1, typename A2> struct AreArgumentsCompatible {
        static int test(const typename RemoveRef<A2>::Type&);
        static char test(...);
        static const typename RemoveRef<A1>::Type &dummy();
        enum { value = sizeof(test(dummy())) == sizeof(int) };
#ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
        using AreArgumentsNarrowed = AreArgumentsNarrowedBase<typename RemoveRef<A1>::Type, typename RemoveRef<A2>::Type>;
        Q_STATIC_ASSERT_X(!AreArgumentsNarrowed::value, "Signal and slot arguments are not compatible (narrowing)");
#endif
    };
    template<typename A1, typename A2> struct AreArgumentsCompatible<A1, A2&> { enum { value = false }; };
    template<typename A> struct AreArgumentsCompatible<A&, A&> { enum { value = true }; };
    // void as a return value
    template<typename A> struct AreArgumentsCompatible<void, A> { enum { value = true }; };
    template<typename A> struct AreArgumentsCompatible<A, void> { enum { value = true }; };
    template<> struct AreArgumentsCompatible<void, void> { enum { value = true }; };


/*解说: template<typename A1, typename A2> struct AreArgumentsCompatible
下面三个函数都是没有函数实体的,只有声明,但是sizeof依然可以对其返回值进行计算。其目的是判断类型A1
和类型A2是否兼容,比如基本数据类型,int,float,double,char等都是兼容可以互转的,编译器会匹配到int 
test()返回int。但是int和自定义类Base不能互转,编译器会匹配到char test()返回char。sizeof()表达
式的计算过程发生在编译阶段,没有实际的函数调用,所以没有对函数声明实体照样可以运行。
static int test(const typename RemoveRef<A2>::Type&);
static char test(...);
static const typename RemoveRef<A1>::Type &dummy();

 template<typename A1, typename A2> struct AreArgumentsCompatible<> 则是描述了几种特化的情
况,使用时会先匹配特化的情况,如果没有满足的才会去匹配非特化的模板。
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值