Adapt to the C++ SIC introduced by P0021: noexcept overloading

C++17 adopts P0021R1[1], which makes noexcept be part of the function
pointer's type and thus be overloadable. It contains some provisions for
allowing a noexcept function pointer to cast implicitly to a non-
noexcept function pointer, but that fails in the presence of templates
and additional overloads that could match the type in question.

Fortunately, the paper proposed a test macro, so we can change our
sources now and be compatible with both C++14 and C++17 rules.

This first failed with Clang 4.0 trunk. This source incompatibility is
not our fault, it's the language's doing.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html

Task-number: QTBUG-58054
Change-Id: I2bc52f3c7a574209b213fffd14988cf0b875be63
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
Thiago Macieira 2017-01-10 15:30:42 -08:00
parent 635d0ae007
commit 5a1b4832a2
5 changed files with 104 additions and 0 deletions

View File

@ -149,6 +149,20 @@ namespace QtPrivate {
(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...)>
{
@ -187,6 +201,47 @@ namespace QtPrivate {
}
};
#if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) noexcept>
{
typedef Obj Object;
typedef List<Args...> Arguments;
typedef Ret ReturnType;
typedef Ret (Obj::*Function) (Args...) noexcept;
template <class Base> struct ChangeClass { typedef Ret (Base:: *Type)(Args...) noexcept; };
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 noexcept>
{
typedef Obj Object;
typedef List<Args...> Arguments;
typedef Ret ReturnType;
typedef Ret (Obj::*Function) (Args...) const noexcept;
template <class Base> struct ChangeClass { typedef Ret (Base:: *Type)(Args...) const noexcept; };
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...) noexcept>
{
typedef List<Args...> Arguments;
typedef Ret ReturnType;
typedef Ret (*Function) (Args...) noexcept;
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);
}
};
#endif
template<typename Function, int N> struct Functor
{
template <typename SignalArgs, typename R>

View File

@ -3,5 +3,8 @@ TARGET = ../tst_qobject
QT = core-private network testlib
SOURCES = ../tst_qobject.cpp
# Force C++17 if available (needed due to P0012R1)
contains(QT_CONFIG, c++1z): CONFIG += c++1z
!winrt: TEST_HELPER_INSTALLS = ../signalbug/signalbug
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0

View File

@ -120,6 +120,7 @@ private slots:
void connectCxx0x();
void connectToStaticCxx0x();
void connectCxx0xTypeMatching();
void connectCxx17Noexcept();
void connectConvert();
void connectWithReference();
void connectManyArguments();
@ -4757,10 +4758,13 @@ class LotsOfSignalsAndSlots: public QObject
public slots:
void slot_v() {}
void slot_v_noexcept() Q_DECL_NOTHROW {}
void slot_vi(int) {}
void slot_vi_noexcept() Q_DECL_NOTHROW {}
void slot_vii(int, int) {}
void slot_viii(int, int, int) {}
int slot_i() { return 0; }
int slot_i_noexcept() Q_DECL_NOTHROW { return 0; }
int slot_ii(int) { return 0; }
int slot_iii(int, int) { return 0; }
int slot_iiii(int, int, int) { return 0; }
@ -4774,13 +4778,18 @@ class LotsOfSignalsAndSlots: public QObject
void slot_vPFvvE(fptr) {}
void const_slot_v() const {};
void const_slot_v_noexcept() const Q_DECL_NOTHROW {}
void const_slot_vi(int) const {};
void const_slot_vi_noexcept(int) const Q_DECL_NOTHROW {}
static void static_slot_v() {}
static void static_slot_v_noexcept() Q_DECL_NOTHROW {}
static void static_slot_vi(int) {}
static void static_slot_vi_noexcept(int) Q_DECL_NOTHROW {}
static void static_slot_vii(int, int) {}
static void static_slot_viii(int, int, int) {}
static int static_slot_i() { return 0; }
static int static_slot_i_noexcept() Q_DECL_NOTHROW { return 0; }
static int static_slot_ii(int) { return 0; }
static int static_slot_iii(int, int) { return 0; }
static int static_slot_iiii(int, int, int) { return 0; }
@ -4943,6 +4952,32 @@ void tst_QObject::connectCxx0xTypeMatching()
}
void receiverFunction_noexcept() Q_DECL_NOTHROW {}
struct Functor_noexcept { void operator()() Q_DECL_NOTHROW {} };
void tst_QObject::connectCxx17Noexcept()
{
// this is about connecting signals to slots with the Q_DECL_NOTHROW qualifier
// as semantics changed due to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html
typedef LotsOfSignalsAndSlots Foo;
Foo obj;
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_v_noexcept);
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_i_noexcept);
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_vi_noexcept);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_v_noexcept);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_i_noexcept);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_vi_noexcept);
QVERIFY(QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::const_slot_vi_noexcept));
QVERIFY(QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::const_slot_v_noexcept));
QObject::connect(&obj, &Foo::signal_v, receiverFunction_noexcept);
Functor_noexcept fn;
QObject::connect(&obj, &Foo::signal_v, fn);
}
class StringVariant : public QObject
{ Q_OBJECT
signals:

View File

@ -2,3 +2,6 @@ CONFIG += testcase
TARGET = tst_qtimer
QT = core testlib
SOURCES = tst_qtimer.cpp
# Force C++17 if available
contains(QT_CONFIG, c++1z): CONFIG += c++1z

View File

@ -766,6 +766,11 @@ class StaticEventLoop
{
public:
static void quitEventLoop()
{
quitEventLoop_noexcept();
}
static void quitEventLoop_noexcept() Q_DECL_NOTHROW
{
QVERIFY(!_e.isNull());
_e->quit();
@ -787,6 +792,9 @@ void tst_QTimer::singleShotToFunctors()
QTimer::singleShot(0, &StaticEventLoop::quitEventLoop);
QCOMPARE(_e->exec(), 0);
QTimer::singleShot(0, &StaticEventLoop::quitEventLoop_noexcept);
QCOMPARE(_e->exec(), 0);
QThread t1;
QObject c1;
c1.moveToThread(&t1);