4

I have several classes in a project I'm working on; the first is a Solver class, originally with a function template whose full definition is in the Solver header file, like so (just showing the bare necessities):

solver.h

class Solver {
  public:
    template<typename T>
    void solve(T t);
}

template<typename T>
void Solver::solve(T t) {
  // implementation here
}

Now, class A is used as template parameter for the solve function template as follows:

A.h

#include "solver.h"

class A {
  private:
    Solver s;  //s is instantiated in constructor

  public:
    void doSomething();
}

A.cpp

void A::doSomething() {
  s.solve<A&>(*this);
}

So this is all fine and dandy as it is now, but for the purposes of the project, I need to move the definition of the solve() function template into an implementation file (solver.cpp) from the header file. As I understand it, I can do this as long as I add lines that explicitly state what types will be used with the function template, as follows:

solver.cpp

template<typename T>
void Solver::solve(T t) {
  // implementation here
}

template void Solver::solve<A&>(A& a);

However this doesn't work when I try to compile solver, because in order to specify A as a type I want to use as a template parameter in solve.cpp, I need to have A not be an incomplete type. But A requires Solver in order to even compile - so I believe I have a circular dependency. Is there any way I can get around this issue?

I'm relatively new to all this, so take it easy on me please :) Much thanks.

3 Answers3

2

Samoth is nearly right, you need class A; ("forward declaration"). But only before you use it, not before the Solver class:

Edited In response to comments, your minimal code sample was too minimal :) The real problem was Header Guards:

#ifndef SOLVER_H_INCLUDED_
#define SOLVER_H_INCLUDED_

class Solver {
  public:
    template<typename T>
    void solve(T t);
};

#endif // SOLVER_H_INCLUDED_

And

// A.h
#ifndef A_H_INCLUDED_
#define A_H_INCLUDED_

#include "Solver.h"

class A {
  private:
    Solver s;  //s is instantiated in constructor

  public:
    void doSomething();
};

#endif // A_H_INCLUDED_


// Solver.cpp
#include "Solver.h"

#include "A.h"

template<typename T>
void Solver::solve(T t) {
  // implementation here
}

// explicit instantiations    
template void Solver::solve<int>(int);
//   ... 
template void Solver::solve<A&>(A&);

This will work

// main.cpp
#include "A.h"

int main()
{
    A a;
    a.doSomething();
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • I tried doing that, but the compiler gave me a "error: invalid use of incomplete type 'struct A'" for each time A is referenced in the solve function, as well as "error: forward declaration of 'struct A'" – user2548415 Jul 05 '13 at 07:07
  • Ah well. You oversimplified the sample. Of course, you can just include `A.h` inside `Solver.cpp` **and remember always to use header guards.** (edited) – sehe Jul 05 '13 at 07:16
  • sehe, you are a wonderful human being. it worked! thank you so very much. and yes, i oversimplified the example...my mistake. =/ the header guards were there and i forgot to include them in the sample. – user2548415 Jul 05 '13 at 07:44
  • and just to clarify with some dumb questions...is the reason why we include A.h in solver.cpp is so that Solver knows how to properly use A when A's members get referenced in the solve function? and also, by the time we get around to compiling A, there's no issue of any sort of duplication of code since we've already included A.h previously? – user2548415 Jul 05 '13 at 07:49
  • Almost. Yes to the first part (obviously). By the time you get around to compiling `A`, there's no issue of any duplication _because it's a different_ **[Translation Unit](http://stackoverflow.com/questions/1106149/what-is-a-translation-unit-in-c)**. As long as you don't violate the **[ODR (one definition rule)](http://stackoverflow.com/questions/4192170/what-exactly-is-one-definition-rule-in-c)** you're fine. – sehe Jul 05 '13 at 07:54
0

The best way to pass-by circular dependencies is to do this :

class A; // before the class Solver

class Solver {
  public:
    template<typename T>
    void solve(T t);
}

template<typename T>
void Solver::solve(T t) {
  // implementation here
}
Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
0

What you can do is:

solver.h

   #ifndef SOLVER_H_INCLUDED_
   #define SOLVER_H_INCLUDED_
    class Solver {
    public:
      template<typename T>
      void solve(T t);

    };
    #include "solver.cpp"
    #endif

solver.cpp

#include "solver.h"

template<typename T>
void Solver::solve(T t) {
  // implementation here
}

and a.hpp

    #ifndef A_H_INCLUDED_
    #define A_H_INCLUDED_
    #include "solver.h"
    class A {
    private:
      Solver s;  //s is instantiated in constructor

    public:
      void doSomething()
      {
        s.solve(*this);
      }
    };
   #endif
Alexis
  • 2,149
  • 2
  • 25
  • 39
  • Hmmm. The OP specificly states he "needs to" move his template definition into a separate TU. Which is technically not what you are doing here – sehe Jul 05 '13 at 06:58
  • Alexis: I tried doing this, but I got the following errors: "redefinition of void Solver::solve(T)" and "void Solver::solve(T) previously declared here" in solve.cpp. also, i need to sleep...I will be back in the morning! much thanks for the help so far – user2548415 Jul 05 '13 at 07:11
  • Are you sure you don't have anymore template void Solver::solve(T t) { // implementation here } in your solver.h ? you just need the definition in your .h @sehe maybe he just need to move the file to have a better view of the project – Alexis Jul 05 '13 at 07:15
  • That's not what he says. Anyways, I located the source of the trouble just now :/ – sehe Jul 05 '13 at 07:19