Thread: Circular include issue

  1. #1
    Registered User
    Join Date
    Jul 2006
    Posts
    6

    Circular include issue

    How do I solve a circular header file include, where A.h includes B.h, B.h includes C.h, and C.h includes A.h and B.h? (There is one class in each file. And no, a forward declaration of C in B.h doesn't help.)

    I am working on a scheduler and the problem involves three classes: Task, Event and Schedule. Schedules contain Events, which in turn contain Tasks, in the following way:

    Code:
    class Schedule {
    	list<const Event*> events;
    	/* ... */
    };
    
    class Event {
    	const Task task;
    	/* ... */
    };
    
    class Task {
    	/* ... */
    };
    (Both Schedule and Event invokes methods on their relevant members.)

    However, I run into problems when I try to add a method for determining whether a Task can be added to a Schedule. I think the method should be in the Task class, since it mostly uses data from that class, but since it refers to both Schedule and Event, it introduces two circular dependencies.

    Code:
    bool Task::isAppendableTo(Schedule& schedule) {
    	/* ... */
    	Event* event;
    	/* ... */
    }
    The easiest way to remove the need for circular includes would be to use a forward declaration in Event.h for the Task class, but for some reason this doesn't work. The compiler seems to object to me doing just about anything with the Task class in Event.h.

    Thankful for any suggestions,
    Einar

  2. #2
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Quote Originally Posted by einarp
    How do I solve a circular header file include, where A.h includes B.h, B.h includes C.h, and C.h includes A.h and B.h? (There is one class in each file. And no, a forward declaration of C in B.h doesn't help.)
    Are you using include guards?
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Event.h should #include "Task.h", otherwise forward declarations should be sufficient (with the include guards, of course). Can you show how you are using them and the errors you get?

  4. #4
    Registered User
    Join Date
    Jul 2006
    Posts
    6
    Quote Originally Posted by Dave_Sinkula
    Are you using include guards?
    I am indeed.

    Quote Originally Posted by Daved
    Event.h should #include "Task.h", otherwise forward declarations should be sufficient (with the include guards, of course). Can you show how you are using them and the errors you get?
    As I wrote: "A.h includes B.h, B.h includes C.h, and C.h includes A.h and B.h", where A = Schedule, B = Event and C = Task.

    Using a simple forward declaration:

    class Task;

    in Event.h, does not help, and I still get hundreds of compile errors (in VC++, yuck!).

    Any other ideas?

  5. #5
    carry on JaWiB's Avatar
    Join Date
    Feb 2003
    Location
    Seattle, WA
    Posts
    1,972
    Could you post a minimal compilable example that demonstrates the problem?
    "Think not but that I know these things; or think
    I know them not: not therefore am I short
    Of knowing what I ought."
    -John Milton, Paradise Regained (1671)

    "Work hard and it might happen."
    -XSquared

  6. #6
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Order of definition is also important. Maybe that is where the problem lies.

    Schedule uses Event.
    Event uses Task.
    Task uses nothing(correct?)

    So definitions should follow accordingly. First Task, then Event, then Schedule. This order should be reflected in the order of the includes. To simplify the process you can (and should) write another header file that only does the includes. This will be the header you should include in your program.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Code:
    bool Task::isAppendableTo(Schedule& schedule) {
    	/* ... */
    	Event* event;
    	/* ... */
    }
    Also, you have a problem here. Schedule is not a part of Task scope. You may be better of defining Schedule::CanAppend(Task& task)
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> class Task; in Event.h, does not help,

    As I said earlier, that is the only situation where you cannot use a forward declaration, you must use a #include since the object is used directly, instead of via pointer or reference. For the other two files, use forward declarations. If that doesn't compile, please post the code (or at least how you did the #includes and forward declarations and anywhere that a different class is mentioned in each header) and the compiler errors (or at least the first few).

  9. #9
    Registered User
    Join Date
    Jul 2006
    Posts
    6
    Quote Originally Posted by Daved
    >As I said earlier, that is the only situation where you cannot use a forward declaration, you must use a #include since the object is used directly, instead of via pointer or reference. For the other two files, use forward declarations. If that doesn't compile, please post the code (or at least how you did the #includes and forward declarations and anywhere that a different class is mentioned in each header) and the compiler errors (or at least the first few).
    So if I all classes use each other directly and not only via pointer or reference, the problem can't be solved?

    Quote Originally Posted by JaWiB
    Could you post a minimal compilable example that demonstrates the problem?
    A minimum compilable example would include six files. And I think we have the problem almost figured out.

    Quote Originally Posted by Mario F.
    Order of definition is also important. Maybe that is where the problem lies.

    Schedule uses Event.
    Event uses Task.
    Task uses nothing(correct?)

    So definitions should follow accordingly. First Task, then Event, then Schedule. This order should be reflected in the order of the includes. To simplify the process you can (and should) write another header file that only does the includes. This will be the header you should include in your program.
    Nope, got that covered.

    And a separate header file with includes for the whole project? Doesn't sound too appealing.

    Quote Originally Posted by Mario F.
    Also, you have a problem here. Schedule is not a part of Task scope. You may be better of defining Schedule::CanAppend(Task& task)
    That is what I resorted to earlier, but I don't like it. And I don't see why the language should prevent me from defining an operation in the correct context.

  10. #10
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> So if I all classes use each other directly and not only via pointer or reference, the problem can't be solved?
    Correct, although that's not the case in the code you posted originally, and that only applies to what is used in the header file, which is usually just the class declaration and function prototypes.

    Each class can use the other classes in the source file. This leads to one work around if you really need the circular usage - the PIMPL idiom, but my guess is that you are just implementing the forward declaration incorrectly.

    >> A minimum compilable example would include six files. And I think we have the problem almost figured out.
    Even a non-compilable example would help like what I requested in my last post. The only code we have is in the original post, and that code should work fine with forward declarations of Event in the Schedule header and Schedule in the Task header, and #include of Task.h in the Event header.

    >> And I don't see why the language should prevent me from defining an operation in the correct context.
    You should be able to do that just fine. That function prototype requires only a forward declaration of Schedule, and the implementation would be in a source file which shouldn't cause any issues. I don't see any reason to change that code.

  11. #11
    Registered User
    Join Date
    Jul 2006
    Posts
    6
    Quote Originally Posted by Daved
    ...and that only applies to what is used in the header file, which is usually just the class declaration and function prototypes. Each class can use the other classes in the source file. This leads to one work around if you really need the circular usage - the PIMPL idiom, but my guess is that you are just implementing the forward declaration incorrectly.
    That's the tie-breaker right there. Thanks a bunch. Not sure exactly what I did wrong the first time, but it works just the way I want it to now so that's it for me.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help me with function call
    By NeMewSys in forum C++ Programming
    Replies: 16
    Last Post: 05-22-2008, 01:53 PM
  2. Socket programming
    By kahad in forum C Programming
    Replies: 3
    Last Post: 12-14-2006, 04:37 PM
  3. limits.h problem!!
    By guitarist809 in forum C++ Programming
    Replies: 21
    Last Post: 04-13-2006, 10:28 PM
  4. to #include or not to #include
    By krygen in forum C++ Programming
    Replies: 9
    Last Post: 12-25-2004, 12:06 AM
  5. help with finding lowest number entered
    By volk in forum C++ Programming
    Replies: 12
    Last Post: 03-22-2003, 01:21 PM