8

I searched about forward declaration and didn't see any way to make my situation work. So here it is:

1) There is a C-header file, an export interface so to speak for a large multi-component software, that contains an enum typedef

"export.h":

// This is in "C"!
typedef enum _VM_TYPE {...., ...., ...,} VM_TYPE;

2) A part of the code, in C++, uses that export.

"cpp_code.cpp":

// This is in C++
#include "export.h"
#include "cpp_header.hpp"
{ .... using VM_TYPE values to do stuffs....}

"cpp_header.hpp":

// Need to somehow forward declear VM_TYPE here but how?
Struct VM_INFO {
....
VM_TYPE VType; //I need to add this enum to the struct
....
};

So quite obvious, the problem is in cpp_head.hpp, as it doesn't know about the enum.

I tried adding to cpp_header.hpp

typedef enum _VM_TYPE VM_TYPE;

and it'll actually work. So why does THIS work? Because it has C-style syntax?! Anyway, I was told to not do that ("it's C++, not C here") by upper "management".

Is there other way to make this work at all, based on how things are linked currently? They don't want to change/add include files; "enum class" is c++ only, correct? Adding just "enum VM_TYPE" to cpp_header.hpp will get error about redefinition.

Any idea? Thanks.

Andrew
  • 127
  • 9
  • 1
    http://stackoverflow.com/questions/71416/forward-declaring-an-enum-in-c – alexbuisson Feb 18 '14 at 23:06
  • Any reason not to include export.h in cpp_header? Seems it is dependent on the enum anyway. You wouldn't be able to do that with a class/struct, unless you were using a pointer/reference. – imreal Feb 18 '14 at 23:27
  • @alexbuisson I read over that thread already. I do understand the reason why forward declaration to not work in C++ (underlying type), but wonder if there's any workaround in my situation. – Andrew Feb 19 '14 at 01:00
  • @Nick I'd say the main reason is adding/changing includes is a big "process" since we're dealing with a very large code base. The "export.h" has alot of other stuffs there. Getting this extra include will take a lot of persuasion and approval, if at all possible... – Andrew Feb 19 '14 at 01:02
  • Won't you have to include export.h in every single place you include cpp_header.hpp anyway? My point is that the 2 files have an intrinsic dependency, so they might as well be included together. – imreal Feb 19 '14 at 01:36
  • @Nick Not really. "export.h" is a big export interface for many components, whilst cpp_header.hpp is just a sub-section of 1 particular components. I understand your point tho, the easiest way to fix this is to change the include.. but let's just say if I'm NOT allowed to change includes, is there any way I can workaround this? – Andrew Feb 19 '14 at 03:43
  • If you have `#include "export.h"` in a C++ source file, then the contents are treated as C++. – Keith Thompson Feb 19 '14 at 03:49
  • 1
    Identifiers beginning with an underscore and a capital letter are always reserved in C, including in the tag namespace, so don't use things like `_VM_TYPE`. – Crowman Feb 19 '14 at 03:49
  • Why do you need to forward declare anything in the first place? You `#include "export.h"` before you `#include "cpp_code_header.cpp"`, so it'll just work, because your preprocessor is just going to dump them into the same translation unit, and the definition of `enum _VM_TYPE` will appear in there before anything in `"cpp_code_header.cpp"` does. You'll have to `#include "export.h"` if you want to get the actual enumeration members as you indicate you want, so where's the problem? – Crowman Feb 19 '14 at 04:03
  • @Paul (1)The underscore, I don't do that myself. But it's common/standard practice for this system at work. I guess they do it as it's completely closed-source and we don't use any libraries. (2) There was a typo of the filename which might have confused you. But there is no issue in cpp_code.cpp, rather, in cpp_header.hpp, that struct define needs to have the enum as a field, hence the need to somehow forward declare the enum type. – Andrew Feb 19 '14 at 04:13
  • 1
    @Andrew: (1) Those identifiers are reserved for C, not for other libraries, so tell them they're writing C wrong. (2) There's no issue in `code_header.hpp` either, because you're including `export.h` first. By the time the compiler sees anything in `code_header.hpp`, it'll have already seen the `enum` definition in `export.h`, because both files are essentially going to get copy-pasted into a single translation unit, in the order in which you `#include` them. So by the time your compiler gets to your `struct` definition, it's already seen the definition of your `enum`. – Crowman Feb 19 '14 at 04:16
  • 1
    In other words, there's basically no difference here between including `"export.h"` in `"cpp_header.hpp"`, and just including them both in `"cpp_code.cpp"` in that order. You end up with essentially the same code after preprocessing. – Crowman Feb 19 '14 at 04:25
  • @PaulGriffiths (1)Not happening :) Even compiler is modified/optimized in house, maybe that's why they started doing this way? Who knows. (2) Ok I see what you mean now. Have to figure out the actual #include order then, as I've simplified the actual #include steps alot here. Thanks – Andrew Feb 19 '14 at 04:40

2 Answers2

1

In the particular situation described in your question, you don't need to forward declare at all. All the files you #include are going to essentially get copy-pasted into a single translation unit before compilation proper begins, and since you #include "export.h" before you #include "cpp_header.hpp", then it'll just work, because by the time the compiler sees the definition of struct VM_INFO, it'll already have seen the definition of enum _VM_TYPE, so you've got no problem. There's basically no difference here between including "export.h" in "cpp_header.hpp", and including them both in "cpp_code.cpp" in that order, since you end up with essentially the same code after preprocessing. So all you have to do here is make sure you get your includes in the correct order.

If you ever wanted to #include "cpp_header.hpp" without including "export.h" in a translation unit where you need to access the members of struct VM_INFO (so that leaving it as an incomplete type isn't an option) then "export.h" is just badly designed, and you should break out the definition of anything you might need separately into a new header. If, as the comments suggest, you absolutely cannot do this and are required to have a suboptimal design, then your next best alternative would be to have two versions of "cpp_header.hpp", one which just repeats the definition of enum _VM_TYPE, and one which does not. You'd #include the first version in any translation unit where you do not also #include "export.h", and #include the second version in any translation unit where you do. Obviously any code duplication of this type is inviting problems in the future.

Also, names beginning with an underscore and a capital letter are always reserved in C, so you really shouldn't use them. If a future version of C ever decides to make use of _VM_TYPE, then you'll be stuck with either using an outdated version of C, or having all this code break.

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • Ok so it appears the #includes order is not what I thought/posted. The less simplified code is `#include "someother.hpp" \\which include "export.h"` and then `#include "cpp_header.hpp"`. Problem is that `"someother.hpp"` also include `cpp_header.hpp` BEFORE `"export.h"`... so order is reversed. Have to see if can be altered without breaking any existing code... – Andrew Feb 19 '14 at 05:08
  • Your idea should work from what I initially posted. Unfortunately there are other parts failing.... namely, other files within that component, which only #include `cpp_header.hpp`, but NOT `export.h`. So `VM_TYPE` is undefined when compiling all these files. – Andrew Feb 19 '14 at 15:37
  • Then you need to redesign. The simple and obvious answer is just to write/modify your headers in a way that reflects how you need to use their contents, either by including `"export.h"` or removing the `struct` definition from `"cpp_header.hpp"` and putting it in a separate header. If for some mysterious reason known only to your employer you are not allowed to write C properly, then either you'll need some kind of duplicative workaround along the lines given in the answer, or you'll need to abandon your plan as something your employer clearly does not want based on their design. – Crowman Feb 19 '14 at 15:52
  • Paul, Moving the struct from `cpp_header.hpp` is out of the question as alot of code depends on it. I think the easier solution now is to define a component-specific enum type (of the same thing essentially) and does a conversion of the enum from the `export.h` to this component-specific enum type in 1 location. Then everyone else within the component can use this instead. – Andrew Feb 19 '14 at 16:43
  • Marking this as the correct answer, as it solves the question based on my initial post. Needed a minor re-design to solve my particular problem as mentioned in my previous comment. Compiled ok now. Thanks Paul. – Andrew Feb 19 '14 at 17:22
0

A enum can not be forward declarations because the compiler needs to know the size of the enum. The underlying a enumerator is compiler specific, but usually a int. Can you just cast the enum as an int?

"I could be and often am wrong"

Burnt Toast
  • 909
  • 5
  • 2