Results 1 to 9 of 9

Thread: Virtual static methods alternative :D

  1. #1
    Join Date
    Dec 2006
    Posts
    160
    Thanks
    33
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Virtual static methods alternative :D

    Hello people and happy new year

    There i'm having a brainstorm on a design pattern. Let's talk in C++ and make abstraction of Qt for the moment.

    [EDIT]: Just to make the post more clear: what i am trying to achieve, is a database object which has enough functionnality to provide the developper a good help after subclassing it. i want this base object to provide search method as well. i am not trying to do a huge thing which is automatically aware of fields and linked data and data types etc, but something usefull enough to speed up the dev of a larger database based app.

    Basically here is what i have for now:
    1) A base abstract (i'll explain why later) object named DBHelper:
    It contains a string variable named "_table", and which has to be initialised in the constructor. So basically after this object is constructed, it "knows" on what database table to interact with.
    The base object contains a private variable named _uid, which is of course a mandatory database field for any object, and is an auto increment in database. That's how i know if an object is abstract, up to date, or to be deleted etc...
    This object also has 3 protected methods, each one to insert, update, or delete in a database (createObject, updateObject and deleteObject).
    DBHelper also defines 2 pure virtual methods named "fieldsList" and "makeBinding". The first method has to return a QStringList containing the fields used by the subclassed object. The second method accepts a QSqlQuery reference as parametter, and allows the objects subclassing DBHelper to bind values to a query once it's prepared...

    2) Several objects, for instance a DBEmployee which subclasses DBHelper and initialize it with the table named "employees". Let's assume DBEmployee contains one private variable "_name". It also implements the method "fieldsList" (Which in this case returns a 1-element QStringList containing "name", since the field in database is named like that). And of course, DBEmployee also implements makeBinding, so when the base DBHelper wants to create or update the object in DB, the binding are made...

    Now let's suppose i want to add search functions to my DBEmployee object. i will probably decide to make something like this:
    static QList<DBEmployee*> DBEmployee::findByName(const QString& name);
    This method would call some magic in the base DBHelper (Which is of course not "subclassed" since we are talking about a static method).
    But in this case, DBHelper can't get the table name, nor the bindings or fields list

    i'm totally aware that there is no solution for this since i'm trying to achieve something (Calling something from a "base class" from a static method) which is programatically impossible... What i'm asking here, is the best solution (To make my DBHelper object as helpfull as possible, and of course avoid having SQL or database related code in the subclassing objects).

    i'm not sure i'm clear enough. If you need more precisions just ask

    Pierre.
    Last edited by hickscorp; 3rd January 2008 at 18:10. Reason: reformatted to look better

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    How about a template method like QList< T * > DBHelper::find( const QString & table, const QString & field, const QString & value ) or something like that? This would make fundByName() implementation trivial.

  3. #3
    Join Date
    Dec 2006
    Posts
    160
    Thanks
    33
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    Hello Jacek and thank you for this answer,

    i have excluded the use of a class template from the begining, since the static "search" methods will be different from one object to another... For instance, an DBEmployee object will provide a findByName method, while another object for instance a DBConferenceRoom object has to provide a findByHalleyCode...

    My goal is not really to make the base object with all search methods, but to make some generic search methods with arguments which will "ease" the subclassing... But as soon as i use a static method in a derived class for my search i would run into so many problems, for instance (And if i dont use a class template):
    - If i make a generic findByCode method (Common to all object) in DBHelper,
    - If i use DBEmployee::findByCode(someCode), the static method from DBEmployee will have to call the DBHelper static method... So how the DBHelper will "know" it will have to instanciate a DBEmployee for each resultset?

    That's only an example of the most irritating problems

    The only solution i have found for now, is to make in DBHelper a "query builder" generic static method, so the derived classes can call it to help them build their queries (Again, the goal is to avoid using database-specific code in the derived code).

    Pierre.

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    Quote Originally Posted by hickscorp View Post
    So how the DBHelper will "know" it will have to instanciate a DBEmployee for each resultset?
    You will ask it to do so.
    Qt Code:
    1. QList< DBConferenceRoom * > DBConferenceRoom::findByHalleyCode( const QString & code )
    2. {
    3. return DBHelper::find< DBConferenceRoom * >( "conference_rooms", "halley_code", code );
    4. }
    5.  
    6. QList< DBEmployee * > DBEmployee::findByName( const QString & name )
    7. {
    8. return DBHelper::find< DBEmployee *>( "employees", "name", name );
    9. }
    To copy to clipboard, switch view to plain text mode 

  5. The following user says thank you to jacek for this useful post:

    hickscorp (3rd January 2008)

  6. #5
    Join Date
    Dec 2006
    Posts
    160
    Thanks
    33
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    Yes your solution with templates on static method sounds very nice

    i will remember this solution, for now i have found an alternative:
    In each subclass of DBHelper, i create a static method named "instanciate" which instanciates an object. For instance in DBEmployee i have a static method like this:
    Qt Code:
    1. DBEmployee* DBEmployee::instanciate (const QSqlDatabase& db, const QSqlQuery& query, QObject* parent) {
    2. DBEmployee* toRet = (DBEmployee*)getCachedObject(TableName+":"+query.value(0).toString());
    3. if (!toRet)
    4. toRet = new DBEmployee(db, query, parent);
    5. return toRet;
    6. }
    To copy to clipboard, switch view to plain text mode 

    [EDIT]Don't bother the getCachedObject thing it's a personnal caching stuff i have made to avoid querying the database if the object is already loaded under certain circumpstances.

    This for each object.

    Then in DBHelper, i have a generic "find" function (not done yet, i was writting it when i checked your answer) which takes as an argument
    * the search criterion
    * table name
    * fields list
    * etc
    * PLUS a pointer to a static instanciation function

    The only problem for now is that my DBHelper::genericFind will always return an instanciated DBxxxObj* with a DBHelper* type. Not problematic with a single find, but as soon as i will want to return a QList of DBxxxObj*, i will first have to return a QList of DBHelper* which will be iterated in the derived static method, and for each element casted as DBxxxObj* in a new QList of DBxxxObj*...

    What do you think about this solution?
    Last edited by hickscorp; 3rd January 2008 at 23:52.

  7. #6
    Join Date
    Dec 2006
    Posts
    160
    Thanks
    33
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    So here is the solution i have made for now (maybe it is ugly, i really want to get feedback from you, and what you think about it):

    In DBHelper:
    Qt Code:
    1. typedef DBHelper*(*instPtr)(const QSqlDatabase&, const QSqlQuery&, QObject*);
    2. static DBHelper* DBHelper::findUnique (const QString& table, const QStringList& fields, const QString& criterion,
    3. const QSqlDatabase& db, instPtr instanciate, QObject* parent=0) {
    4. QSqlQuery query = buildSearch(table, fields, criterion, db);
    5. if (!query.next())
    6. return 0;
    7. return (*instanciate)(db, query, parent);
    8. }
    To copy to clipboard, switch view to plain text mode 

    In my derived DBEmployee i have defined TableName and FieldList as two static variables (1st is QString, 2nd is QStringList), and these static methods:
    Qt Code:
    1. DBHelper* DBEmployee::instanciate (const QSqlDatabase& db, const QSqlQuery& query, QObject* parent) {
    2. DBEmployee* toRet = (DBEmployee*)getCachedObject(TableName+":"+query.value(0).toString());
    3. if (!toRet)
    4. toRet = new DBEmployee(db, query, parent);
    5. return toRet;
    6. }
    7. DBEmployee* DBEmployee::findByCode (const QSqlDatabase &db, int code, QObject *parent) {
    8. return (DBEmployee*)findUnique(TableName, FieldsList, "uid="+QString::number(code), db, &instanciate, parent);
    9. }
    To copy to clipboard, switch view to plain text mode 

    So... When i want to get an employee from it's code:
    Qt Code:
    1. DBEmployee* test = DBEmployee::findByExactName (_adminDb, "John Doe", this);
    To copy to clipboard, switch view to plain text mode 

    Now i have a question:
    - Imagine i create a method which returns an instance of QList<DBHelper*>.
    - Now if i call this method from another one which HAS to return a QList<DBEmployee*>, is it possible to recast the whole list with some magic, or have i to create a new list, iterate thru the old one, and fill the new one with freshly re-casted objects?

    [EDIT] In other words: Do you see a solution to avoid the casting problems? For instance, my function pointer is defined to return a DBHelper*, so in my DBEmployee if i declare the static method "instanciate" with a return of type DBEmployee*, it can't compile...

    For now, my search methods to return multiple results looks like this:
    In DBHelper:
    Qt Code:
    1. DBHelperList DBHelper::findMulti (const QString& table, const QStringList& fields, const QString& criterion,
    2. const QSqlDatabase& db, instPtr instanciate, QObject* parent) {
    3. QSqlQuery query = buildSearch(table, fields, criterion, db);
    4. DBHelperList listToRet;
    5. while (query.next())
    6. listToRet.append((*instanciate)(db, query, parent));
    7. return listToRet;
    8. }
    To copy to clipboard, switch view to plain text mode 

    In DBEmployee:
    Qt Code:
    1. QList<DBEmployee*> DBEmployee::findByName (const QSqlDatabase& db, const QString& name, QObject* parent) {
    2. DBHelperList original = findMulti(TableName, FieldsList, "name like '%"+name+"%'", db, &instanciate, parent);
    3. QList<DBEmployee*> toRet;
    4. while (!original.isEmpty())
    5. toRet.append((DBEmployee*)original.takeFirst());
    6. return toRet;
    7. }
    To copy to clipboard, switch view to plain text mode 

    What i want to avoid is the loop in DBEmployee method, to recast all objects... Which were already all casted in DBHelper findMulti method!
    Last edited by jacek; 4th January 2008 at 15:23. Reason: wrapped too long line

  8. #7
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    How about a template method like QList< T * > DBHelper::find( const QString & table, const QString & field, const QString & value ) or something like that?

    Qt Code:
    1. template< class T >
    2. QList< T * > DBHelper::find( ... )
    3. {
    4. QSqlQuery query( buildSearch( table, fields, and, stuff ) );
    5. QList< T * > result;
    6. while( query.next() ) {
    7. result.append( T::instantiate( query, parent ) );
    8. }
    9. return result;
    10. }
    To copy to clipboard, switch view to plain text mode 

  9. The following user says thank you to jacek for this useful post:

    hickscorp (4th January 2008)

  10. #8
    Join Date
    Dec 2006
    Posts
    160
    Thanks
    33
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    yes i understood this solution, but there are 2 things scaring me:

    - In DBHelper i have very important static variables (Mostly for caching the "already-instanciated-objects"), and i have read somewhere that template classes doesn't share their static variables... i really dont know if it's true, and unfortunatelly i dont have time for testing. If you know about this, please enlighten me

    - I'm not familiar with class templates at all, since i really *HATE* hpp files containing definition and declarations. i only have written very few class templates since i'm doing C++, i do this only when i dont have a choice.

    However, i really believe that your idea is very good, and i will probably rewrite my code to something similar as soon as i get a bit more time, and if i'm sure that a static variable declared in DBHelper will be the same instance for a DBHelper<DBEmployee*> and for a DBHelper<DBConferenceRoom*> for instance.

    Thanks a lot Jacek
    Pierre.

    [EDIT] Also, can you please confirm that i can use a template in a static method? You wrote:
    Qt Code:
    1. template< class T >
    2. QList< T * > DBHelper::find( ... )
    To copy to clipboard, switch view to plain text mode 

    But will it compile with:
    Qt Code:
    1. template< class T >
    2. static QList< T * > DBHelper::find( ... )
    To copy to clipboard, switch view to plain text mode 

  11. #9
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    5,372
    Thanks
    28
    Thanked 976 Times in 912 Posts
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Virtual static methods alternative :D

    Quote Originally Posted by hickscorp View Post
    i have read somewhere that template classes doesn't share their static variables...
    You aren't going to use a template class, but a class with template methods.

    Qt Code:
    1. #include <iostream>
    2.  
    3. class A
    4. {
    5. public:
    6. template< class T > static void foo( T x ) { std::cout << _x++ << " A::foo " << x << std::endl; }
    7.  
    8. private:
    9. static int _x;
    10. };
    11.  
    12. int A::_x = 0;
    13.  
    14. int main()
    15. {
    16. A::foo( 10 );
    17. A::foo( 20.5 );
    18. A::foo( "aaa" );
    19.  
    20. A::foo( 10 );
    21. A::foo( 20.5 );
    22. A::foo( "aaa" );
    23.  
    24. return 0;
    25. }
    To copy to clipboard, switch view to plain text mode 

    $ ./a.out
    0 A::foo 10
    1 A::foo 20.5
    2 A::foo aaa
    3 A::foo 10
    4 A::foo 20.5
    5 A::foo aaa
    And case of template classes, you can always move common static variables to a non-template base class.

    Quote Originally Posted by hickscorp View Post
    can you please confirm that i can use a template in a static method?
    Yes, you can.

Similar Threads

  1. Replies: 16
    Last Post: 23rd May 2008, 10:12

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.