diff --git a/autotests/libs/itemhydratest.cpp b/autotests/libs/itemhydratest.cpp index eff9e503d..fa3c521c4 100644 --- a/autotests/libs/itemhydratest.cpp +++ b/autotests/libs/itemhydratest.cpp @@ -1,362 +1,400 @@ /* Copyright (c) 2006 Till Adam This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "itemhydratest.h" #include #include "item.h" #include #include #include #include using namespace Akonadi; struct Volker { bool operator==(const Volker &f) const { return f.who == who; } virtual ~Volker() { } virtual Volker *clone() const = 0; QString who; }; typedef std::shared_ptr VolkerPtr; typedef QSharedPointer VolkerQPtr; struct Rudi: public Volker { Rudi() { who = QStringLiteral("Rudi"); } virtual ~Rudi() { } Rudi *clone() const override { return new Rudi(*this); } }; typedef std::shared_ptr RudiPtr; typedef QSharedPointer RudiQPtr; struct Gerd: public Volker { Gerd() { who = QStringLiteral("Gerd"); } Gerd *clone() const override { return new Gerd(*this); } + + typedef Volker SuperClass; }; typedef std::shared_ptr GerdPtr; typedef QSharedPointer GerdQPtr; Q_DECLARE_METATYPE(Volker *) Q_DECLARE_METATYPE(Rudi *) Q_DECLARE_METATYPE(Gerd *) Q_DECLARE_METATYPE(Rudi) Q_DECLARE_METATYPE(Gerd) namespace Akonadi { template <> struct SuperClass : public SuperClassTrait {}; -template <> struct SuperClass : public SuperClassTrait {}; } QTEST_MAIN(ItemHydra) ItemHydra::ItemHydra() { } void ItemHydra::initTestCase() { } void ItemHydra::testItemValuePayload() { Item f; Rudi rudi; f.setPayload(rudi); QVERIFY(f.hasPayload()); Item b; Gerd gerd; b.setPayload(gerd); QVERIFY(b.hasPayload()); QCOMPARE(f.payload(), rudi); QVERIFY(!(f.payload() == gerd)); QCOMPARE(b.payload(), gerd); QVERIFY(!(b.payload() == rudi)); } void ItemHydra::testItemPointerPayload() { Item f; Rudi *rudi = new Rudi; // the below should not compile //f.setPayload( rudi ); // std::auto_ptr is not copyconstructable and assignable, therefore this will fail as well //f.setPayload( std::auto_ptr( rudi ) ); //QVERIFY( f.hasPayload() ); //QCOMPARE( f.payload< std::auto_ptr >()->who, rudi->who ); // below doesn't compile, hopefully //QCOMPARE( f.payload< Rudi* >()->who, rudi->who ); delete rudi; } void ItemHydra::testItemCopy() { Item f; Rudi rudi; f.setPayload(rudi); Item r = f; QCOMPARE(r.payload(), rudi); Item s; s = f; QVERIFY(s.hasPayload()); QCOMPARE(s.payload(), rudi); } void ItemHydra::testEmptyPayload() { Item i1; Item i2; i1 = i2; // should not crash QVERIFY(!i1.hasPayload()); QVERIFY(!i2.hasPayload()); QVERIFY(!i1.hasPayload()); QVERIFY(!i1.hasPayload()); bool caughtException = false; bool caughtRightException = true; try { Rudi r = i1.payload(); } catch (const Akonadi::PayloadException &e) { qDebug() << e.what(); caughtException = true; caughtRightException = true; } catch (const Akonadi::Exception &e) { qDebug() << "Caught Akonadi exception of type " << typeid(e).name() << ": " << e.what() << ", expected type" << typeid(Akonadi::PayloadException).name(); caughtException = true; caughtRightException = false; } catch (const std::exception &e) { qDebug() << "Caught exception of type " << typeid(e).name() << ": " << e.what() << ", expected type" << typeid(Akonadi::PayloadException).name(); caughtException = true; caughtRightException = false; } catch (...) { qDebug() << "Caught unknown exception"; caughtException = true; caughtRightException = false; } QVERIFY(caughtException); QVERIFY(caughtRightException); } void ItemHydra::testPointerPayload() { Rudi *r = new Rudi; RudiPtr p(r); std::weak_ptr w(p); QCOMPARE(p.use_count(), (long)1); { Item i1; i1.setPayload(p); QVERIFY(i1.hasPayload()); QCOMPARE(p.use_count(), (long)2); { QVERIFY(i1.hasPayload< RudiPtr >()); RudiPtr p2 = i1.payload< RudiPtr >(); QCOMPARE(p.use_count(), (long)3); } { QVERIFY(i1.hasPayload< VolkerPtr >()); VolkerPtr p2 = i1.payload< VolkerPtr >(); QCOMPARE(p.use_count(), (long)3); } QCOMPARE(p.use_count(), (long)2); } QCOMPARE(p.use_count(), (long)1); QCOMPARE(w.use_count(), (long)1); p.reset(); QCOMPARE(w.use_count(), (long)0); } -void ItemHydra::testPolymorphicPayload() +void ItemHydra::testPolymorphicPayloadWithTrait() { VolkerPtr p(new Rudi); { Item i1; i1.setPayload(p); QVERIFY(i1.hasPayload()); QVERIFY(i1.hasPayload()); QVERIFY(i1.hasPayload()); QVERIFY(!i1.hasPayload()); QCOMPARE(p.use_count(), (long)2); { RudiPtr p2 = std::dynamic_pointer_cast(i1.payload< VolkerPtr >()); QCOMPARE(p.use_count(), (long)3); QCOMPARE(p2->who, QStringLiteral("Rudi")); } { RudiPtr p2 = i1.payload< RudiPtr >(); QCOMPARE(p.use_count(), (long)3); QCOMPARE(p2->who, QStringLiteral("Rudi")); } bool caughtException = false; try { GerdPtr p3 = i1.payload(); } catch (const Akonadi::PayloadException &e) { qDebug() << e.what(); caughtException = true; } QVERIFY(caughtException); QCOMPARE(p.use_count(), (long)2); } } +void ItemHydra::testPolymorphicPayloadWithTypedef() +{ + VolkerPtr p(new Gerd); + + { + Item i1; + i1.setPayload(p); + QVERIFY(i1.hasPayload()); + QVERIFY(i1.hasPayload()); + QVERIFY(!i1.hasPayload()); + QVERIFY(i1.hasPayload()); + QCOMPARE(p.use_count(), (long)2); + { + auto p2 = std::dynamic_pointer_cast(i1.payload< VolkerPtr >()); + QCOMPARE(p.use_count(), (long)3); + QCOMPARE(p2->who, QStringLiteral("Gerd")); + } + + { + auto p2 = i1.payload< GerdPtr >(); + QCOMPARE(p.use_count(), (long)3); + QCOMPARE(p2->who, QStringLiteral("Gerd")); + } + + bool caughtException = false; + try { + auto p3 = i1.payload(); + } catch (const Akonadi::PayloadException &e) { + qDebug() << e.what(); + caughtException = true; + } + QVERIFY(caughtException); + + QCOMPARE(p.use_count(), (long)2); + } +} + void ItemHydra::testNullPointerPayload() { RudiPtr p((Rudi *)nullptr); Item i; i.setPayload(p); QVERIFY(i.hasPayload()); QVERIFY(i.hasPayload()); QVERIFY(i.hasPayload()); // Fails, because GerdQPtr is QSharedPointer, while RudiPtr is std::shared_ptr // and we cannot do sharedptr casting for null pointers QVERIFY(!i.hasPayload()); QCOMPARE(i.payload().get(), (Rudi *)nullptr); QCOMPARE(i.payload().get(), (Volker *)nullptr); } void ItemHydra::testQSharedPointerPayload() { RudiQPtr p(new Rudi); Item i; i.setPayload(p); QVERIFY(i.hasPayload()); QVERIFY(i.hasPayload()); QVERIFY(i.hasPayload()); QVERIFY(!i.hasPayload()); { VolkerQPtr p2 = i.payload< VolkerQPtr >(); QCOMPARE(p2->who, QStringLiteral("Rudi")); } { RudiQPtr p2 = i.payload< RudiQPtr >(); QCOMPARE(p2->who, QStringLiteral("Rudi")); } bool caughtException = false; try { GerdQPtr p3 = i.payload(); } catch (const Akonadi::PayloadException &e) { qDebug() << e.what(); caughtException = true; } QVERIFY(caughtException); } void ItemHydra::testHasPayload() { Item i1; QVERIFY(!i1.hasPayload()); QVERIFY(!i1.hasPayload()); Rudi r; i1.setPayload(r); QVERIFY(i1.hasPayload()); QVERIFY(!i1.hasPayload()); } void ItemHydra::testSharedPointerConversions() { Item a; RudiQPtr rudi(new Rudi); a.setPayload(rudi); // only the root base classes should show up with their metatype ids: QVERIFY(a.availablePayloadMetaTypeIds().contains(qMetaTypeId())); QVERIFY(a.hasPayload()); QVERIFY(a.hasPayload()); QVERIFY(a.hasPayload()); QVERIFY(!a.hasPayload()); QVERIFY(a.payload().get()); QVERIFY(a.payload().get()); bool thrown = false, thrownCorrectly = true; try { QVERIFY(!a.payload()); } catch (const Akonadi::PayloadException &e) { thrown = thrownCorrectly = true; } catch (...) { thrown = true; thrownCorrectly = false; } QVERIFY(thrown); QVERIFY(thrownCorrectly); } void ItemHydra::testForeignPayload() { QTemporaryFile file; QVERIFY(file.open()); file.write("123456789"); file.close(); Item a(QStringLiteral("application/octet-stream")); a.setPayloadPath(file.fileName()); QVERIFY(a.hasPayload()); QCOMPARE(a.payload(), QByteArray("123456789")); Item b(QStringLiteral("application/octet-stream")); b.apply(a); QVERIFY(b.hasPayload()); QCOMPARE(b.payload(), QByteArray("123456789")); QCOMPARE(b.payloadPath(), file.fileName()); Item c = b; QVERIFY(c.hasPayload()); QCOMPARE(c.payload(), QByteArray("123456789")); QCOMPARE(c.payloadPath(), file.fileName()); } diff --git a/autotests/libs/itemhydratest.h b/autotests/libs/itemhydratest.h index 79e088dfe..fd056d8fb 100644 --- a/autotests/libs/itemhydratest.h +++ b/autotests/libs/itemhydratest.h @@ -1,48 +1,49 @@ /* Copyright (c) 2007 Till Adam This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ITEMHYDRA_H #define ITEMHYDRA_H #include class ItemHydra : public QObject { Q_OBJECT public: ItemHydra(); virtual ~ItemHydra() { } private Q_SLOTS: void initTestCase(); void testItemValuePayload(); void testItemPointerPayload(); void testItemCopy(); void testEmptyPayload(); void testPointerPayload(); - void testPolymorphicPayload(); + void testPolymorphicPayloadWithTrait(); + void testPolymorphicPayloadWithTypedef(); void testNullPointerPayload(); void testQSharedPointerPayload(); void testHasPayload(); void testSharedPointerConversions(); void testForeignPayload(); }; #endif diff --git a/src/core/supertrait.h b/src/core/supertrait.h index 661d07a2c..efd5ab574 100644 --- a/src/core/supertrait.h +++ b/src/core/supertrait.h @@ -1,50 +1,63 @@ /* Copyright (c) 2009 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_SUPERTRAIT_H #define AKONADI_SUPERTRAIT_H namespace Akonadi { +namespace Internal { +template struct check_type{ typedef void type; }; +} + /** @internal - @see super_class + @see SuperClass */ -template +template struct SuperClassTrait { typedef Super Type; }; +template +struct SuperClassTrait::type> { + typedef typename Class::SuperClass Type; +}; + /** Type trait to provide information about a base class for a given class. Used eg. for the Akonadi payload mechanism. To provide base class introspection for own types, extend this trait as follows: @code namespace Akonadi { template <> struct SuperClass : public SuperClassTrait{}; } @endcode + + Alternatively, define a typedef "SuperClass" in your type, pointing to the base class. + This avoids having to include this header file if that's inconvenient from a dependency + point of view. */ template struct SuperClass : public SuperClassTrait {}; } #endif