views:

176

answers:

3

Hi,

I have a problem using forward declaration and virtual functions. I got the following error message during compilation.

main.cpp:131: error: cannot allocate an object of abstract type ‘Database::MySQL’
database_mysql.h:31: note:   because the following virtual functions are pure within ‘Database::MySQL’:
database.h:28: note:    virtual void Database::Interface::query(const char*, QueryResult&)
database.h:29: note:    virtual void Database::Interface::query(std::string, QueryResult&)
database.h:30: note:    virtual bool Database::Interface::step(QueryResult&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&)

The compiler said these functions are still pure virtual functions, but I declared and defined them properly. I don't know what the problem is.

Here is my source code.

// database.h
class QueryResult;

namespace Database
{
    class Interface {
        public:
            Interface() {};
            virtual ~Interface() {};

            virtual void query(const char *sql) = 0;
            virtual void query(std::string sql) = 0;
            virtual void query(const char *sql, QueryResult &result) = 0;
            virtual void query(std::string sql, QueryResult &result) = 0;
            virtual bool step(QueryResult &result,
                              std::vector<std::string> &row) = 0;
    };
}

// database_mysql.h 
namespace Database
{
    class MySQL : public Interface {
        public:
            class QueryResult {
            public:
                QueryResult();
                ~QueryResult() ;
                void set(MYSQL_RES *result);
                MYSQL_RES *get();

            private:
                MYSQL_RES *_result;

            };

            ...

            void query(const char *sql);
            void query(std::string sql);
            void query(const char *sql, QueryResult &result);
            void query(std::string sql, QueryResult &result);
            bool step(QueryResult &result, std::vector<std::string> &row);

            ...
    };
}


// database_mysql.cpp
Database::MySQL::QueryResult::QueryResult()
    : _result(NULL)
{
}

Database::MySQL::QueryResult::~QueryResult()
{
    ...
}

void Database::MySQL::QueryResult::set(MYSQL_RES *result)
{
    ...
}

MYSQL_RES *Database::MySQL::QueryResult::get()
{
    ...
}


void Database::MySQL::query(const char *sql)
{
   ...
}

void Database::MySQL::query(std::string sql)
{
   ...
}

void Database::MySQL::query(const char *sql, QueryResult &result)
{
   ...
}

void Database::MySQL::query(std::string sql, QueryResult &result)
{
   ...
}

/* @return: false on done or true if remained rows exist */
bool Database::MySQL::step(QueryResult &result, std::vector<std::string> &row)
{
    ...
}

Thank you.

+1  A: 

move the decl "QueryResult" into the namespace Database::Interface

it seems to be an issue with name spaces.

Anders K.
Thanks for your reply. However, the problem is still remained with same error messages.
Brian
+3  A: 

Because of the forward declaration compiler is looking for the class QueryResult in the global namespace. The function you defined in class MySQL use the inner class (which is inside the namespace) QueryResult. This is treated as an overloading by the compiler and not as the implementation of the pure virtual function. My suggestion to solve this is to remove the forward declaration and make the QueryResult a inner class of interface Interface (it makes sense to put it there, otherwise there is no use of the interface). Then it will compile properly.

Naveen
Thanks. hmmm... I used forward declaration to make that there are different QueryResult classes depends on SQL APIs like a database_sqlite3.h, etc. But I think it's not possible if I put the QueryResult class into the Interface class.
Brian
Probably you can make `QueryResult` also an abstract base class so that different implementation of `Interface` can provide their own implementation of `QueryResult`?
Naveen
Thanks, I will try it now, but how can I receive different database handle in the member function parameters of abstract base class? Should I use void pointer or template class? Sorry for my poor question. I am C++ newbie. :-)
Brian
A: 

You can probably do a quick fix now by making QueryResult a class and deriving MySQLQueryResult from it. As others pointed out the issue is with compiler not being able to use a nested class instead of the forward declared class.

        // database.h
        class QueryResult {
        //make this class abstract by creating a protected default constructor
        protected:
          QueryResult(){}
        }

        //mysql.cpp
        class QueryResult : ::QueryResult {
        public:
            QueryResult();
            ~QueryResult() ;
            void set(MYSQL_RES *result);
            MYSQL_RES *get();

        private:
            MYSQL_RES *_result;

        };
Igor Zevaka
Is it possible to call functions of MySQLQueryResult using QueryResult reference? How can I declare MySQLQueryResult parameters on the Interface's member functions without forward declarations?
Brian
Your `QueryResult` base class should contain members that are applicable to all implementations of `Interface`, so `QueryResult` should contain the least common denominator functions/attributes in it.
Igor Zevaka