0

I am trying to figure out how to use QxOrm to persist a large, complex class structure. I've made some progress, but am stuck on what to do with pointers to abstract classes.

Here's a simple example, where Table (a concrete class) has a pointer to some kind of Shape (an abstract class):

class Table {
    Shape* top;
};

class Shape {
public:
    virtual float getWidth() = 0;
};

class Square : public Shape {
    virtual float getWidth();
};

class Circle : public Shape {
    virtual float getWidth();
};

I get error messages complaining that Shape is abstract when I try this.

It looks to me like this is impossible to do with QxOrm, I suspect because its only inheritance model is Concrete.

Does anyone know if this is possible or not? I'd rather not give up on QxOrm since it looks good in many ways (and I've put a bunch of time into it already), but it looks like I will have to.

Update1: I know about QX_REGISTER_ABSTRACT_CLASS. It does not help for this use case.

Update2: I experimented with making the comment class in the qxBlog example abstract. I made it subclass qx::IxPersistable and used the QX_REGISTER_ABSTRACT_CLASS macro. When I compile, it dies in the invocation of the macro QX_PERSISTABLE_CPP, with:

../../../QxOrm/include/QxDao/../../inl/QxDao/QxDao_Count.inl:36: error: cannot declare variable 't' to be of abstract type 'comment'

There are similar error messages from other places, too.

(BTW, I was going to ask first on the QxOrm forum, but it was unavailable at the time.)

Thanks!

Chris
  • 4,734
  • 2
  • 19
  • 26

2 Answers2

0

To register an abstract class into QxOrm context : http://www.qxorm.com/qxorm_en/faq.html#faq_140

And to work with pointers to base classes, I think that qx::IxPersistable interface could help you : http://www.qxorm.com/qxorm_en/faq.html#faq_260

Note : QxOrm forum is now available...

qxorm
  • 1
  • I know about `QX_REGISTER_ABSTRACT_CLASS`. I read about `qx::IxPersistable` but it didn't look like it would be necessary or useful for this case. I'll try it, though. And thanks for the tip about the forum. – Chris Jul 10 '12 at 16:10
0

Here is a generic solution using qx::IxPersistable interface and QxOrm triggers. The idea is to store the ID of your base class + the type associated to your ID : this way, you will be able to instantiate all derived classes.

--- MyBaseClass.h file :

class QX_DLL2_EXPORT MyBaseClass : public qx::IxPersistable
{

public:

   long id;

   MyBaseClass() : qx::IxPersistable(), id(0) { ; }
   virtual ~MyBaseClass() = 0;

};

QX_REGISTER_ABSTRACT_CLASS(MyBaseClass)
QX_REGISTER_HPP_QX_DLL2(MyBaseClass, qx::trait::no_base_class_defined, 0)

typedef boost::shared_ptr<MyBaseClass> MyBaseClass_ptr;

--- MyDerivedClass1.h file :

class QX_DLL2_EXPORT MyDerivedClass1 : public MyBaseClass
{

   QX_PERSISTABLE_HPP(MyDerivedClass1)

public:

   QString description;

   MyDerivedClass1() : MyBaseClass() { ; }
   virtual ~MyDerivedClass1() { ; }

};

QX_REGISTER_HPP_QX_DLL2(MyDerivedClass1, MyBaseClass, 0)

typedef boost::shared_ptr<MyDerivedClass1> MyDerivedClass1_ptr;

--- MyDerivedClass2.h file :

class QX_DLL2_EXPORT MyDerivedClass2 : public MyBaseClass
{

   QX_PERSISTABLE_HPP(MyDerivedClass2)

public:

   QString prop1;
   QString prop2;

   MyDerivedClass2() : MyBaseClass() { ; }
   virtual ~MyDerivedClass2() { ; }

};

QX_REGISTER_HPP_QX_DLL2(MyDerivedClass2, MyBaseClass, 0)

typedef boost::shared_ptr<MyDerivedClass2> MyDerivedClass2_ptr;

--- MyContainer.h file :

class QX_DLL2_EXPORT MyContainer
{

public:

   long id;

   long base_id;
   QString base_type;
   MyBaseClass_ptr base_ptr;

   MyContainer() : id(0), base_id(0) { ; }
   virtual ~MyContainer() { ; }

   void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao);
   void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao);
   void onBeforeDelete(qx::dao::detail::IxDao_Helper * dao);
   void onBeforeFetch(qx::dao::detail::IxDao_Helper * dao);
   void onAfterInsert(qx::dao::detail::IxDao_Helper * dao);
   void onAfterUpdate(qx::dao::detail::IxDao_Helper * dao);
   void onAfterDelete(qx::dao::detail::IxDao_Helper * dao);
   void onAfterFetch(qx::dao::detail::IxDao_Helper * dao);

   void insertOrUpdateBasePtr(qx::dao::detail::IxDao_Helper * dao);

};

QX_REGISTER_HPP_QX_DLL2(MyContainer, qx::trait::no_base_class_defined, 0)

typedef boost::shared_ptr<MyContainer> MyContainer_ptr;

namespace qx {
namespace dao {
namespace detail {

template <>
struct QxDao_Trigger<MyContainer>
{

   static inline void onBeforeInsert(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)   { if (t) { t->onBeforeInsert(dao); } }
   static inline void onBeforeUpdate(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)   { if (t) { t->onBeforeUpdate(dao); } }
   static inline void onBeforeDelete(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)   { if (t) { t->onBeforeDelete(dao); } }
   static inline void onBeforeFetch(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)    { if (t) { t->onBeforeFetch(dao); } }
   static inline void onAfterInsert(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)    { if (t) { t->onAfterInsert(dao); } }
   static inline void onAfterUpdate(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)    { if (t) { t->onAfterUpdate(dao); } }
   static inline void onAfterDelete(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)    { if (t) { t->onAfterDelete(dao); } }
   static inline void onAfterFetch(MyContainer * t, qx::dao::detail::IxDao_Helper * dao)     { if (t) { t->onAfterFetch(dao); } }

};

} // namespace detail
} // namespace dao
} // namespace qx

--- MyBaseClass.cpp file :

QX_REGISTER_CPP_QX_DLL2(MyBaseClass)

namespace qx {
template <> void register_class(QxClass<MyBaseClass> & t)
{
   t.id(& MyBaseClass::id, "id");
}}

MyBaseClass::~MyBaseClass() { ; }

--- MyDerivedClass1.cpp file :

QX_REGISTER_CPP_QX_DLL2(MyDerivedClass1)
QX_PERSISTABLE_CPP(MyDerivedClass1)

namespace qx {
template <> void register_class(QxClass<MyDerivedClass1> & t)
{
   t.data(& MyDerivedClass1::description, "description");
}}

--- MyDerivedClass2.cpp file :

QX_REGISTER_CPP_QX_DLL2(MyDerivedClass2)
QX_PERSISTABLE_CPP(MyDerivedClass2)

namespace qx {
template <> void register_class(QxClass<MyDerivedClass2> & t)
{
   t.data(& MyDerivedClass2::prop1, "prop1");
   t.data(& MyDerivedClass2::prop2, "prop2");
}}

--- MyContainer.cpp file :

QX_REGISTER_CPP_QX_DLL2(MyContainer)

namespace qx {
template <> void register_class(QxClass<MyContainer> & t)
{
   t.id(& MyContainer::id, "id");
   t.data(& MyContainer::base_id, "base_id");
   t.data(& MyContainer::base_type, "base_type");
}}

void MyContainer::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }
   if (! base_ptr || ! base_ptr->qxClass()) { return; }

   base_id = base_ptr->id;
   base_type = base_ptr->qxClass()->getKey();
}

void MyContainer::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }
   if (! base_ptr || ! base_ptr->qxClass()) { return; }

   base_id = base_ptr->id;
   base_type = base_ptr->qxClass()->getKey();
}

void MyContainer::onBeforeDelete(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }
   // Nothing to do here !
}

void MyContainer::onBeforeFetch(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }
   // Nothing to do here !
}

void MyContainer::onAfterInsert(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }
   insertOrUpdateBasePtr(dao);
}

void MyContainer::onAfterUpdate(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }
   insertOrUpdateBasePtr(dao);
}

void MyContainer::onAfterDelete(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }

   if (base_ptr)
   {
      QVariant idEmpty;
      QSqlError daoError = base_ptr->qxDeleteById(idEmpty, (& dao->database()));
      dao->updateError(daoError);
   }
}

void MyContainer::onAfterFetch(qx::dao::detail::IxDao_Helper * dao)
{
   if (! dao) { qAssert(false); return; }
   if (dao->error().isValid()) { return; }

   if ((base_id > 0) && (! base_type.isEmpty()))
   {
      MyBaseClass * pBase = qx::create_nude_ptr<MyBaseClass>(base_type);
      if (! pBase) { qAssert(false); return; }
      base_ptr.reset(pBase);
      QVariant vId = base_id; QStringList lstEmpty;
      QSqlError daoError = base_ptr->qxFetchById(vId, lstEmpty, lstEmpty, (& dao->database()));
      dao->updateError(daoError);
   }
}

void MyContainer::insertOrUpdateBasePtr(qx::dao::detail::IxDao_Helper * dao)
{
   static bool bInEvent = false;
   if (! base_ptr || bInEvent) { return; }
   bInEvent = true;

   QStringList lstEmpty; QVariant idEmpty;
   QSqlError daoError; qx::QxSqlQuery queryEmpty;
   qx_bool bExist = base_ptr->qxExist(idEmpty, (& dao->database()));
   if (bExist)
   {
      daoError = base_ptr->qxUpdate(queryEmpty, lstEmpty, lstEmpty, (& dao->database()));
      dao->updateError(daoError);
   }
   else
   {
      daoError = base_ptr->qxInsert(lstEmpty, (& dao->database()));
      if (! daoError.isValid()) { daoError = qx::dao::update((* this), (& dao->database()), QStringList() << "base_id" << "base_type"); }
      dao->updateError(daoError);
   }

   bInEvent = false;
}

--- main.cpp file to do some tests :

   daoError = qx::dao::create_table<MyDerivedClass1>(); qAssert(! daoError.isValid());
   daoError = qx::dao::create_table<MyDerivedClass2>(); qAssert(! daoError.isValid());
   daoError = qx::dao::create_table<MyContainer>(); qAssert(! daoError.isValid());

   MyContainer b1;
   MyDerivedClass1_ptr pD1 = MyDerivedClass1_ptr(new MyDerivedClass1());
   pD1->description = "my desc";
   b1.base_ptr = pD1;
   daoError = qx::dao::insert(b1); qAssert(! daoError.isValid());

   MyContainer b2;
   MyDerivedClass2_ptr pD2 = MyDerivedClass2_ptr(new MyDerivedClass2());
   pD2->prop1 = "my prop1";
   pD2->prop2 = "my prop2";
   b2.base_ptr = pD2;
   daoError = qx::dao::insert(b2); qAssert(! daoError.isValid());

   MyContainer b3;
   b3.base_ptr = pD1;
   pD1->description = "my desc modified";
   daoError = qx::dao::insert(b3); qAssert(! daoError.isValid());

   QList<MyContainer_ptr> all;
   daoError = qx::dao::fetch_all(all);
   qx::dump(all);

And all output traces :

[QxOrm] sql query (0 ms) : CREATE TABLE MyDerivedClass1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, description TEXT)
[QxOrm] sql query (0 ms) : CREATE TABLE MyDerivedClass2 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, prop1 TEXT, prop2 TEXT)
[QxOrm] sql query (16 ms) : CREATE TABLE MyContainer (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, base_id INTEGER, base_type TEXT)
[QxOrm] sql query (0 ms) : SELECT MyDerivedClass1.id AS MyDerivedClass1_id_0 FROM MyDerivedClass1 WHERE MyDerivedClass1.id = :id
[QxOrm] sql query (0 ms) : INSERT INTO MyDerivedClass1 (description) VALUES (:description)
[QxOrm] sql query (0 ms) : UPDATE MyContainer SET id = :id, base_id = :base_id, base_type = :base_type WHERE id = :id_bis
[QxOrm] sql query (3245 ms) : INSERT INTO MyContainer (base_id, base_type) VALUES (:base_id, :base_type)
[QxOrm] sql query (0 ms) : SELECT MyDerivedClass2.id AS MyDerivedClass2_id_0 FROM MyDerivedClass2 WHERE MyDerivedClass2.id = :id
[QxOrm] sql query (0 ms) : INSERT INTO MyDerivedClass2 (prop1, prop2) VALUES (:prop1, :prop2)
[QxOrm] sql query (0 ms) : UPDATE MyContainer SET id = :id, base_id = :base_id, base_type = :base_type WHERE id = :id_bis
[QxOrm] sql query (0 ms) : INSERT INTO MyContainer (base_id, base_type) VALUES (:base_id, :base_type)
[QxOrm] sql query (0 ms) : SELECT MyDerivedClass1.id AS MyDerivedClass1_id_0 FROM MyDerivedClass1 WHERE MyDerivedClass1.id = :id
[QxOrm] sql query (0 ms) : UPDATE MyDerivedClass1 SET id = :id, description = :description WHERE id = :id_bis
[QxOrm] sql query (15 ms) : INSERT INTO MyContainer (base_id, base_type) VALUES (:base_id, :base_type)
[QxOrm] sql query (0 ms) : SELECT MyDerivedClass1.id AS MyDerivedClass1_id_0, MyDerivedClass1.description AS MyDerivedClass1_description_0 FROM MyDerivedClass1 WHERE MyDerivedClass1.id = :id
[QxOrm] sql query (0 ms) : SELECT MyDerivedClass2.id AS MyDerivedClass2_id_0, MyDerivedClass2.prop1 AS MyDerivedClass2_prop1_0, MyDerivedClass2.prop2 AS MyDerivedClass2_prop2_0 FROM MyDerivedClass2 WHERE MyDerivedClass2.id = :id
[QxOrm] sql query (0 ms) : SELECT MyDerivedClass1.id AS MyDerivedClass1_id_0, MyDerivedClass1.description AS MyDerivedClass1_description_0 FROM MyDerivedClass1 WHERE MyDerivedClass1.id = :id
[QxOrm] sql query (0 ms) : SELECT MyContainer.id AS MyContainer_id_0, MyContainer.base_id AS MyContainer_base_id_0, MyContainer.base_type AS MyContainer_base_type_0 FROM MyContainer
[QxOrm] start dump 'QList<boost::shared_ptr<MyContainer>>'
<QList-boost.shared_ptr-MyContainer-- class_id="0" tracking_level="0" version="0">
   <count>3</count>
   <item class_id="1" tracking_level="0" version="1">
      <px class_id="2" tracking_level="1" version="0" object_id="_0">
         <id>1</id>
         <base_id>1</base_id>
         <base_type class_id="3" tracking_level="0" version="0">MyDerivedClass1</base_type>
         <base_ptr class_id="4" tracking_level="0" version="1">
            <px class_id="6" class_name="MyDerivedClass1" tracking_level="1" version="0" object_id="_1">
               <MyBaseClass class_id="5" tracking_level="1" version="0" object_id="_2">
                  <id>1</id>
               </MyBaseClass>
               <description>my desc modified</description>
            </px>
         </base_ptr>
      </px>
   </item>
   <item>
      <px class_id_reference="2" object_id="_3">
         <id>2</id>
         <base_id>1</base_id>
         <base_type>MyDerivedClass2</base_type>
         <base_ptr>
            <px class_id="7" class_name="MyDerivedClass2" tracking_level="1" version="0" object_id="_4">
               <MyBaseClass object_id="_5">
                  <id>1</id>
               </MyBaseClass>
               <prop1>my prop1</prop1>
               <prop2>my prop2</prop2>
            </px>
         </base_ptr>
      </px>
   </item>
   <item>
      <px class_id_reference="2" object_id="_6">
         <id>3</id>
         <base_id>1</base_id>
         <base_type>MyDerivedClass1</base_type>
         <base_ptr>
            <px class_id_reference="6" object_id="_7">
               <MyBaseClass object_id="_8">
                  <id>1</id>
               </MyBaseClass>
               <description>my desc modified</description>
            </px>
         </base_ptr>
      </px>
   </item>
</QList-boost.shared_ptr-MyContainer-->
[QxOrm] end dump 'QList<boost::shared_ptr<MyContainer>>'
qxorm
  • 1