1

I am aware how to solve basic circular dependency example, with two classes, where each needs to know that the other one exists.
However, I am now in situation, where the example is more complicated, and forward declaration is not something, that can fix that issue.

Consider these three files

// my_thread.hpp
template<typename Function> class my_thread;

template<typename Return, typename... Input>
struct my_thread<Return(Input...)>
{
    void somefunction() { thread_manager::static_function(); }
}

// thread_manager.hpp
struct thread_manager
{
    static void static_function() { }
    std::list<some_class::thread_type> threads;
}

// some_class.hpp
struct some_class
{
    using thread_type = my_thread<int(some_class*)>
}

Now, obviously my_thread.hpp requires whole thread_manager (or at least it's function?). thread_manager requires using directive from some_class and some_class is dependent on my_thread. Since STL containers require complete type template parameters, I can't forward declare my_thread. I can not even put out definition of my_thread<T>::somefunction(), since it's template function and requires to be placed in header.
My question is, how do I resolve this circular dependency?

And funny thing, MSVC does not require #include "thread_manager" inmy_thread.hppfor some reason. I don't know how it knows aboutthread_manager`.

Zereges
  • 5,139
  • 1
  • 25
  • 49

2 Answers2

1

One way to tackle this would be to inject thread manager as a template parameter:

// my_thread.hpp
template<typename ThreadManager, typename Function> class my_thread;

template<typename ThreadManager, typename Return, typename... Input>
struct my_thread<ThreadManager, Return(Input...)>
{
    void somefunction() { ThreadManager::static_function(); }
}

Your some_class has to pass the thread manager in, but it does not know anything about the thread manager, so you have to inject the thread manager into some_class too:

// some_class.hpp
template<typename ThreadManager>
struct some_class
{
    using thread_type = my_thread<ThreadManager, int(some_class*)>;
};

Finally, the thread manager is in the position to inject itself into some_class:

// thread_manager.hpp
struct thread_manager
{
    static void static_function() { }
    std::list<my_thread<thread_manager, int(int)>> threads;
}

With this structure nothing directly depends on a thread manager any more.

As an aside, you might want to use your some_class in some single threaded context. In that case, you can create a dummy ThreadManager that provides the same interface as your thread_manager but does nothing, and set this class as a default template parameter.

Maksim Solovjov
  • 3,147
  • 18
  • 28
  • Dependency injection will propagate up to the highest level class, so you'll have to inject `thread_manager` into `some_class`. Give me a sec – Maksim Solovjov Sep 06 '15 at 10:14
  • It would be nice if I could just forward declare `thread_manager::static_function` without having to do whole `#include` – Zereges Sep 06 '15 at 10:22
  • It would, but you can't. Dependency injection looks messy at first, but it is great: now your code is testable :) – Maksim Solovjov Sep 06 '15 at 10:23
0
#include <list>

// my_thread.hpp
template<typename Function> class my_thread;

// thread_manager.hpp
struct thread_manager
{
    static void static_function() { }
    std::list<my_thread<int(int)>> threads;
} ;

template<typename Return, typename... Input>
struct my_thread<Return(Input...)>
{
    void somefunction() { thread_manager::static_function(); }
} ;

Another variant: leave the declaration of somefunction in the Template and implement it in a separate file.

Heimetli
  • 23
  • 5
  • 1
    AFAIK, template functions definitions should be in same (header) file as hte class is defined. – Zereges Sep 06 '15 at 10:22
  • _"... and implement it in a separate file."_ [Nope!](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – πάντα ῥεῖ Sep 06 '15 at 10:31