views:

870

answers:

5

Currently I have a frustrating problem with forward declaration and template function. I have been trying to googling and do some modification but nothing has worked so far. Below is the snippet of the code:

class TaskScheduler; --> //forward declaration of ‘struct TaskScheduler’
//
//

class TaskEvent {
//
//
};

class HostTask {
//
//
};

template<class T> inline HostTask*
findT(TaskScheduler* tss, T* e)
{
    map<int, HostTask*>::iterator it;
    bool bEq = false;
    for(it = tss->tasks_.begin(); it != tss->tasks_.end(); it++) { --> //error: invalid use of incomplete type ‘struct TaskScheduler’
    if(dynamic_cast<TaskEvent*>(e))
        bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
    else if(dynamic_cast<HostTask*>(e))
        bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
    if(bEq) {
        return it->second;
    }
}
return NULL;
}
//

//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
friend HostTask* findT<TaskEvent>(TaskScheduler* tss, TaskEvent* e); //findT function is used here
//
//
};

Here is the error message that I've got which is shown in the code as well: ./bt-taskscheduler.h:159: error: forward declaration of ‘struct TaskScheduler’ ./bt-taskscheduler.h:229: error: invalid use of incomplete type ‘struct TaskScheduler’

Could anybody show me what is going wrong in this code? Any help is appreciated..

+4  A: 

In the definition of findT you are using tss->tasks_ which dereferences a pointer to an object of type TaskScheduler so you need a full definition of the struct, not just a forward declaration visible at this point in the program.

The definition of struct TaskScheduler needs to appear before the definition of the findT function template.

Charles Bailey
+2  A: 

You are using the TaskScheduler class in your for-loop header "tss->tasks_.begin()". Compiler does not know, whether this class have "tasks_" member or not.

It is not the problem with your templates, any function, inlined in the header file will cause the same error. Forward declaration of the class only allows you to declare pointers (or references) to that class or pass this class objects as a parameters. You cannot "use" the class (call its methods or get the member data), until you fully define your class.

SadSido
+1  A: 

Because you use the definition of TaskScheduler in the findT functions, you have two options:

  • Move the definition of TaskScheduler above the findT template function
  • Make TaskScheduler a second template of of the findT function

Like this:

template< class U, class T> 
inline HostTask* findT( U* tss, T* e)
{
   //...
}
TimW
A: 

As other posters have mentioned, you are dereferencing a pointer to TaskScheduler without a definition of the type, which will cause an error just as it would in any definition.

What you are probably confused about is that your code likely works on some compilers, even modern ones (I know MSVC is incorrect in this regard, but I do not know if it will accept the above code*). These compilers do not properly implement what is known as two-phase name lookup.

Two-phase name loopkup is a more predictable method of name lookup used in templates than the simpler form used by some compilers. In the simpler form, the template definition is parsed and stored for use only when it's instantiated, and name lookup is performed on all names in the template from the point at which you instantiate the template. With two-phase name lookup, names used within a template are sorted into dependent names and non-dependent names. Non-dependent names are names that the compiler can resolve immediately - any name that doesn't rely on a template parameter, directly or indirectly. These names are processed immediately when you define the template. Dependent names, on the other hand, cannot be resolved immediately; they are stored and then, when instantiation is performed, looked up in the template's context, but also in the context in which the template was instantiated for argument-dependent lookup only.

Here's an example:

 void foo (int);
 template <typename T> void bar(T t) {
   foo(1.0);
   foo(t);
 }
 void foo (double);

 struct qux {};
 void foo (qux);

 void baz () {
   bar (1.0);
   qux q;
   bar (q);
 }

N.B. I know I got the metasyntactic names in the wrong order. I apologize, but I added qux last and couldn't be bothered to rewrite my comment.

The instantiations of the bar template each call foo twice. The first call is non-dependent, so the compiler resolves it as soon as it sees it. The result is that it calls foo (int), applying a conversion, even though it will later find a better definition. This is no different from any other function call in C++. The tricky bit comes with the second call, which is dependent. The first call in baz calls bar<double>, the latter calls bar<qux>. The instantiated bar attempts to call foo with an object of type T. In the double scenario, since primitives never use argument-depedent lookup, the result is once again looked up only from bar, and foo(int) is found. When called with qux, however, argument-dependent lookup is applied both in the the definition and instantiation context**, so foo(qux) is called.

It can be a tad stupid, but it tends to Do The Right Thing. Also, I hope you actually understood that; it can be rather confusing. You'll need to read that Wikipedia link to understand fully.

* MSVC may implement a lesser form of two-phase name lookup where it does resolve non-dependent names correctly, but it takes into account definitions after the template for dependent names. I forget whether it does this or omits two-phase lookup entirely and I don't have a copy of the program to check.

** In nearly every case, the instantiation context includes every declaration the definition context does. There is, however, the export keyword which can cause this not to be the case. That keyword is only implemented in one compiler frontend - I wonder why nobody else has implemented it? [/sarcasm]

coppro
indeed this code works with some compilers.. anyway, thank you everyone's answer.. basically I need to forward declare the findT template function and work around on my code structure.. thanks guys..
bayu
+1  A: 

Next to trouble with the forward declaration, it looks as if your findT function should actually be a member function of the scheduler class: it makes extensive use of the scheduler's data members.

These members are private, so you need a way to publish them, and fall back onto the friend declaration.

So either you make the members public, or, better, you refactor the findT function into a member function.

There's no problem in making it a templated member function, either. And you will automatically get rid of the friend declaration.

//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
 public:
  template<class T> inline HostTask* findT(T* e) const
  {
    map<int, HostTask*>::iterator it;
    bool bEq = false;
    for(it = tasks_.begin(); it != tasks_.end(); it++) { 
       if(dynamic_cast<TaskEvent*>(e))
          bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
      else if(dynamic_cast<HostTask*>(e))
          bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
      if(bEq) {
          return it->second;
      }
    }
    return NULL;
  }


};
xtofl