1

I have code similar to the following:

template<class BASE_TYPE = COdbcQuery>
class CRemoteQuery : public BASE_TYPE
{
    CRemoteDatabase m_Db;

public:
    CRemoteQuery()
        : BASE_TYPE(&m_Db)
    {
    }

    ~CRemoteQuery()
    {
    }
};

My problem is that m_Db.Open() must be called before passing m_Db to the base constructor.

If I call a method as an argument to the base constructor that calls Open(), it fails because m_Db has not yet been initialized.

I tried creating a virtual method in the base class, which would be called during initialization and this class could override, but template classes cannot override virtual methods.

Restructuring my base classes so that m_Db doesn't need to be opened first raises a lot of difficult issues. Is there no way to do this?

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • While I agree with @Sam Varshavchik, I think it highlights another issue. Given separation of responsibility, should the `CRemoteQuery` class handle the opening of the database, or should _Dependency Injection_ be used to provide you with a database that is already open? I'd opt for the latter, but you could just as easily use the comma operator like so: `BASE_TYPE((static_cast(m_Db.Open()), &m_Db)) {}` – jfh Sep 19 '19 at 00:41
  • @jfh: I'm not even sure how I'd approach this using DI. This class saves a lot of typing by managing its own database for times when I just need a query. At any rate, I tried to explain in my question that I attempted your suggestion. But if fails because `m_Db` has not yet been constructed. – Jonathan Wood Sep 19 '19 at 00:42
  • @Johnathan Wood well you could try explicitly constructing `m_Db` in that very same comma operator expression ala placement `new`, but that's also a big code smell as well. IMO this probably is indicative that you should refactor and introduce factories and the like, as the idea that you get a resource whose lifetime is indeterminate smells... smelly. – jfh Sep 19 '19 at 00:46

1 Answers1

2

This sequence of events can be easily implemented by making a small design change:

class CRemoteDB {

protected:
    CRemoteDatabase m_Db;

    CRemoteDB()
    {
          m_Db.open();
    }
};

template<class BASE_TYPE = COdbcQuery>
class CRemoteQuery : private CRemoteDB, public BASE_TYPE
{

public:
    CRemoteQuery()
        : BASE_TYPE(&m_Db)
    {
    }

    ~CRemoteQuery()
    {
    }
};

Parent classes always get constructed in declaration order. The CRemoteDB parent class gets constructed first, and CRemoteDatabase::open() gets called in the parent class's constructor.

Then BASE_TYPE gets constructed, and gets a pointer to the opened m_Db.

CRemoteQuery can access m_Db from its parent class no differently than it would be if it was its own class member.

but template classes cannot override virtual methods.

P.S. Whoever told you that was wrong. Template classes can certainly override virtual methods. I've got a massive hierarchy of templates here, all overriding virtual methods of their parent classes, left and right.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Another possible solution might be to create a method, that does such initialization, and use the result of it, to pass to the base class, such as: `auto& initAndReturn {m_Db.open (); return m_db;} CRemoteQuery () : BASE_TYPE (&initAndReturn ()) {}` – Algirdas Preidžius Sep 19 '19 at 00:37
  • Thanks, although an odd fix this seems to be the best possible answer. As far as virtual methods, I tried overriding a virtual method from the base class but it never got called. And when I Googled it, I found pages like [this](https://stackoverflow.com/questions/2354210/can-a-class-member-function-template-be-virtual). – Jonathan Wood Sep 19 '19 at 00:37
  • @AlgirdasPreidžius: As described in my question, I attempted that but it failed because `m_Db` has not yet been constructed. – Jonathan Wood Sep 19 '19 at 00:38
  • @JonathanWood Hmm.. I contemplated if such code was undefined behavior, or not, for the reason, that you described. But, I thought, that I remembered seeing similar code, and it _seemed_ to work fine.. Perhaps my mind is playing tricks on me, and I am misremembering things.. I, probably need to go to sleep then :/ – Algirdas Preidžius Sep 19 '19 at 00:47
  • @AlgirdasPreidžius depending on how you use the member it may not matter-- caching a reference to something I'll use later, for example. Initialization logic for a database, that be ugly. – user4581301 Sep 19 '19 at 01:15
  • The other question explains that virtual functions cannot be templates. It says absolutely nothing about a template class overriding a virtual function. These are two completely unrelated things. In fact, with modern C++, you can ***require*** that a template class, if it defines a particular function, it must override some virtual function from some inherited class, otherwise a compilation error results. This prevents the template from being instantiated with a wrong class that the template is not designed for. – Sam Varshavchik Sep 19 '19 at 11:00