c++wtwt-dbo

Circular dependency in Wt::Dbo


Wt recommends to use forward declarations to avoid circular dependencies.

// Settings.h
#include <Wt/Dbo/Dbo.h>
#include <string>

class User; // Forward declaration of User Wt::Dbo object

class Settings 
{
public:
  Wt::Dbo::ptr<User> user;

  template<class Action>
  void persist(Action& a)
  {
    Wt::Dbo::belongsTo(a, user);
  }
};

 

// User.h
#include <Wt/Dbo/Dbo.h>
#include <string>

#include "Settings.h"

class User
{
public:
  Wt::Dbo::weak_ptr<Settings> settings;

  template<class Action>
  void persist(Action& a)
  {
    Wt::Dbo::hasOne(a, settings);
  }
};

However, when I use this Settings class in another cpp file, the program doesn't compile:

// test.cpp
#include "Settings.h"

error: C2079: 'dummy' uses undefined class 'User'

Possible solutions (which I do not like)

  1. A solution is to include in User.h in every cpp file that includes Settings.h, i.e.:

    // test.cpp
    #include "User.h"
    #include "Settings.h"
    

    I do not prefer this solution, because I have to remember to include User.h every time I include Settings.h.

  2. Another solution is to use the non-recommended DBO_EXTERN_TEMPLATES macro, i.e.

    // Settings.h
    ...
    class Settings
    {
    public:
       ....
    };
    
    DBO_EXTERN_TEMPLATES(Settings)
    

    I do not prefer this solution as this macro is not recommend, nor documented. DBO_EXTERN_TEMPLATES doesn't work with all compilers.

Question

a. What is the best/preferred methodology to overcome circular dependencies between Wt::Dbo objects avoiding the mentioned undefined class error?

b. Why does solution 1. works?

I created a new (general - not Wt::Dbo specific) question (with an MCVE), to clarify the specific situation: When are member functions of a templated class instantiated?

References


Solution

  • Based on the answer of ChrisMM, another solution is to forward declare your class at the top of its header file:

    Settings.h:

    // include guard
    class Settings;
    #include "User.h"
    
    class Settings { ... };
    

    Users.h:

    // include guard
    class User;
    #include "Settings.h"
    
    class User { ... };
    

    The advantage of this approach is that you only have to forward declare the class in its own header file and are allowed to just include the file in any other (header) file that need it.