Better error reporting in case of connection failure.

Use Q_STATIC_ASSERT_X give a better error message.
If C++11 is used, you get the string in the error.
Else, clicking on the QStaticFailure error still shows you the
string in the qobject.h source code)

And report better failure if the return types do not match.
(Without the static assert, you would still have a compilation error,
but in an unrelated place, with no reference to the actual connect()
call. The error was thrown from the virtual call
QSlotObject::call, without saying where it was instantiated)

Previously the error was relying on the existence of a type inside
CheckCompatibleArguments, but the Q_STATIC_ASSERT requires a bool
(hence the introduction of CheckCompatibleArguments::value)

There also was a typo in the return value of
AreArgumentsCompatible::dummy that made that code not work, and that
error not be reported.
(Instead, the error was reported when QObjectSlot::call is instantiated)

Specialization of AreArgumentsCompatible for the void type have been
added because if the return value of a signal or slot is void, the
connection should work.

Change-Id: I5a93ec787ce2a4b94a26630ca31d5001cd294e4d
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
This commit is contained in:
Olivier Goffart 2011-12-19 13:10:40 +01:00 committed by Qt by Nokia
parent 85a77cd5c8
commit 8754bf03f5
2 changed files with 35 additions and 20 deletions

View File

@ -212,7 +212,12 @@ public:
reinterpret_cast<typename SignalType::Object *>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<typename SignalType::Object *>(0));
//compilation error if the arguments does not match.
typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments;
Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
"Return type of the slot is not compatible with the return type of the signal.");
const int *types = 0;
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
@ -234,8 +239,12 @@ public:
typedef QtPrivate::FunctionPointer<Func2> SlotType;
//compilation error if the arguments does not match.
typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments;
typedef typename QtPrivate::QEnableIf<(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount))>::Type EnsureArgumentsCount;
Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
"Return type of the slot is not compatible with the return type of the signal.");
return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0,
new QStaticSlotObject<Func2,
@ -276,7 +285,9 @@ public:
reinterpret_cast<typename SignalType::Object *>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<typename SignalType::Object *>(0));
//compilation error if the arguments does not match.
typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments;
Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
return disconnectImpl(sender, reinterpret_cast<void **>(&signal), receiver, reinterpret_cast<void **>(&slot),
&SignalType::Object::staticMetaObject);
}

View File

@ -441,36 +441,40 @@ namespace QtPrivate {
/*
Logic that check if the arguments of the slot matches the argument of the signal.
To be used like this:
CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::IncompatibleSignalSlotArguments
The IncompatibleSignalSlotArguments type do not exist if the argument are incompatible and can
then produce error message.
Q_STATIC_ASSERT(CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::value)
*/
template<typename T, bool B> struct CheckCompatibleArgumentsHelper {};
template<typename T> struct CheckCompatibleArgumentsHelper<T, true> : T {};
template<typename A1, typename A2> struct AreArgumentsCompatible {
static int test(A2);
static char test(...);
static A2 dummy();
static A1 dummy();
enum { value = sizeof(test(dummy())) == sizeof(int) };
};
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 }; };
#ifndef Q_COMPILER_VARIADIC_TEMPLATES
template <typename List1, typename List2> struct CheckCompatibleArguments{};
template <> struct CheckCompatibleArguments<void, void> { typedef bool IncompatibleSignalSlotArguments; };
template <typename List1> struct CheckCompatibleArguments<List1, void> { typedef bool IncompatibleSignalSlotArguments; };
template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
template <> struct CheckCompatibleArguments<void, void> { enum { value = true }; };
template <typename List1> struct CheckCompatibleArguments<List1, void> { enum { value = true }; };
template <typename Arg1, typename Arg2, typename Tail1, typename Tail2> struct CheckCompatibleArguments<List<Arg1, Tail1>, List<Arg2, Tail2> >
: CheckCompatibleArgumentsHelper<CheckCompatibleArguments<Tail1, Tail2>, AreArgumentsCompatible<
typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value > {};
{
enum { value = AreArgumentsCompatible<typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value
&& CheckCompatibleArguments<Tail1, Tail2>::value };
};
#else
template <typename List1, typename List2> struct CheckCompatibleArguments{};
template <> struct CheckCompatibleArguments<List<>, List<>> { typedef bool IncompatibleSignalSlotArguments; };
template <typename List1> struct CheckCompatibleArguments<List1, List<>> { typedef bool IncompatibleSignalSlotArguments; };
template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
template <> struct CheckCompatibleArguments<List<>, List<>> { enum { value = true }; };
template <typename List1> struct CheckCompatibleArguments<List1, List<>> { enum { value = true }; };
template <typename Arg1, typename Arg2, typename... Tail1, typename... Tail2>
struct CheckCompatibleArguments<List<Arg1, Tail1...>, List<Arg2, Tail2...>>
: CheckCompatibleArgumentsHelper<CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>, AreArgumentsCompatible<
typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value > {};
{
enum { value = AreArgumentsCompatible<typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value
&& CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>::value };
};
#endif