3

I checked SO for duplicates of this, but was unable to find an exact solution for my problem.

I have a header file NvCommon.h where I use an enum NV_DATA_TYPE. This enum is defined in another header NvDefs.h where I use a number of structs and enums from NvCommon.h. I can't build this due to circular dependency. I know that forwrd declaring enum is not possible.

In this situation what could be done? Is this a problem with my design? Do I have to introduce another header file to solve this?

I am no C expert. Please help me. My design may have issues and I know can fix this circular dependency by introducing another header file. What I would like to know is "is that the only way around". Looking for alternate solutions if available.

I will post the full code if it is helpful.

Community
  • 1
  • 1
NeonGlow
  • 1,659
  • 4
  • 18
  • 36

5 Answers5

10

It can be useful to define enums in their own file(s), and if you do that here, your problem will disappear.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
6

If I understand your question correctly, you've got something like this:

derpfoo.h:

#ifndef DERPFOO_H
#define DERPFOO_H

#include "derpbar.h"

typedef struct {
        char *nothing;
} FOO;

BAR foo (BAR derp) {
        return derp;
}

#endif

derpbar.h:

#ifndef DERPBAR_H
#define DERPBAR_H

#include "derpfoo.h"

typedef struct {
        char *nothing;
} BAR;

FOO bar (FOO derp) {
        return derp;
}

#endif

and then a simple derp.c:

#include "derpfoo.h"
#include "derpbar.h"

int main (void) {
        return 0;
}

A friend of mine presented me with this problem in some SDL code a while ago, and it took me a while to understand both why he was doing what he was doing and how to fix it reasonably.

The intention of separating the code like this is that derpfoo and derpbar are logically separate, but they are also, sadly, mutually dependent. The simplest solution I've found to such a thing is to combine them and split them according to anatomy rather than logic like so:

derpstructs.h:

#ifndef DERPSTRUCTS_H
#define DERPSTRUCTS_H

typedef struct {
        char *nothing;
} FOO;

typedef struct {
        char *nothing;
} BAR;

#include "derpfunctions.h"

#endif

derpfunctions.h:

#ifndef DERPFUNCTIONS_H
#define DERPFUNCTIONS_H

#include "derpstructs.h"

BAR foo (BAR derp) {
        return derp;
}

FOO bar (FOO derp) {
        return derp;
}

#endif

and still a simple derp.c:

#include "derpstructs.h"
#include "derpfunctions.h"

int main (void) {
        return 0;
}

Note that derpstructs.h includes derpfunctions.h at the end rather than at the beginning. This is not, strictly speaking, necessary, but if you do intend to have them include each other, you must include the function definitions after the struct definitions that they depend on, in all possible inclusion paths. Moving on...

This solution works, but it's not exactly sticking with the original philosophy that caused the problem to begin with -- that the two parts are logically separate, and should be kept so in the code.

The answer to both is to split everything up further, and tweak the inclusion paths even more.

derpfoostructs.h includes derpbarstructs.h first and then defines struct FOO and then at the end optionally includes derpfoofunctions.h and derpbarfunctions.h.

derpbarstructs.h includes derpfoostructs.h first and then defines struct BAR and then at the end optionally includes derpfoofunctions.h and derpbarfunctions.h.

derpfoofunctions.h includes derpfoostructs.h and derpbarstructs.h first, then includes derpbarfunctions.h, and then defines its functions.

derpbarfunctions.h includes derpfoostructs.h and derpbarstructs.h first, then includes derpfoofunctions.h, and then defines its functions.

derp.c includes derpfoostructs.h and derpbarstructs.h and then includes derpfoofunctions.h and derpbarfunctions.h, and then goes on to do whatever else it needs to do.

This satisfies both of the desired requirements. It eliminates circular dependencies, and still maintains logical separation of the two logically separate code units. You get two files to edit when you change from project to project instead of one, but it at least keeps the mutable code apart from the immutable. That, and it's the only solution I've found.

Hope this helped. Good luck on your projects.

  • @NeonGlow Let me know if you need more details or just a code dump of the four-include solution. The key, really, is to think about how it would flow if it was all one file, and realize that the C preprocessor makes it literally that, and then the compiler gets hold of it. So you can split your code up completely insanely, so long as the include path stitches it all back together into something legible for the compiler. – Pegasus Epsilon Feb 27 '13 at 09:59
  • This is OK. Enough inputs here. I may do the homework reordering things.:) – NeonGlow Feb 27 '13 at 10:56
2

Circular dependencies might mean that you're overdesigning your interface. Merge both files into Nv.h. If this trivially solves the problem, this means you were incorrectly designing the interface.

hdante
  • 7,685
  • 3
  • 31
  • 36
  • The file NvCommon.h contains defintions that will not change across projects and NvDefs.h contains stuff that will/may change across projects. That is how this division comes. – NeonGlow Feb 26 '13 at 03:58
  • 2
    in that case you probably want to order things so NvDefs.h includes NvCommon.h Why do you want to include stuff from a file that won't change into one that will? – camelccc Feb 26 '13 at 09:21
  • If `NV_DATA_TYPE` changes on a project base, it should not be used in defintions that shall be constant over projects. So `NV_DATA_TYPE` shall then **not** be used in `NvCommon.h`. – alk Feb 26 '13 at 11:27
1

Check this out

How to properly declare and define variables, libraries, functions , etc. This might be relevant.

Community
  • 1
  • 1
1

I tried modeling this problem based on your description. In my implementation, I tested three things (1) declaring but not defining NV_DATA_TYPE in NvDefs.h, (2) declaring and defining NV_DATA_TYPE in NvDefs.h, and (3) defining NV_DATA_TYPE in NvDefs.h, but declaring it NvCommon.h. Also, as your description provided, I created some structs in NvCommon.h and accessed those objects in NvDefs.h. In every case--with and without guards in the header files--the code compiled and executed with correct results.

Is it possible that your circular dependency is somewhere else in your header files besides the NV_DATA_TYPE enum?

THV
  • 77
  • 8
  • The problem is in these two headers. Moving the enum itself will solve the problem. The only thing that is holding me from doing this is the enum wont logically fit there. – NeonGlow Feb 27 '13 at 04:51