SFML community forums

General => General discussions => Topic started by: Saboba42 on February 18, 2011, 03:35:50 pm

Title: .h Files and forward Declarations
Post by: Saboba42 on February 18, 2011, 03:35:50 pm
I have been running into an issue while coding in C++ in the Code::Blocks compiler.  I declare a class in a header file and then put that header file in another header file, but the second header file compiles as if the first class had not been declared.  Codeblocks still acts like it knows about the second header (auto-completing in the second header with things from the first header and such).

In coding terms:

Header 1:
<header guard>

namespace GQE
{
    class A
    {
          public int z;
    };
};

Header 2:

<header guard>

#include "Header1.h"

namespace GQE
{
     Class B
     {
          void Foo(A* theA);
     };
};

Compiler error: in B::void Foo(A* theA): A has not been declared


The problem is fixed when I do this (-> marks the addition):

<header guard>

#include "Header1.h"

namespace GQE
{

->     Class A;

     Class B
     {
          void Foo(A* theA);
     };
};

However, this does not work:

Header_types.h:

<header guard>
namespace GQE
{
     class A;
};

Header 2:

<header guard>

#include "Header_types.h"

namespace GQE
{
     Class B
     {
          void Foo(A* theA);
     };
};

This, however, will compile:

Header 2:

<header guard>

namespace GQE
{
   Class A;
};

namespace GQE
{
     Class B
     {
          void Foo(A* theA);
     };
};

Isn't this exactly equivalent to the above example that doesn't work???  To make this even more crazy, I am building this project from the Basic Game Engine source from the wiki, and the problem is only happening in a few files I have added to the project - the rest of the project does the exact same thing as above, except it works.

The same thing happens for enums and it's driving me crazy.  Help appreciated!  If you need more information, I'll see what I can do.  The headers are both added to the same project, and are in the same file in that project, so I know that isn't the issue.
Title: .h Files and forward Declarations
Post by: Laurent on February 18, 2011, 03:44:53 pm
Make sure that you didn't define the same header guards in two different files -- happens easily with copy and paste ;)
Title: .h Files and forward Declarations
Post by: devlin on February 18, 2011, 07:13:28 pm
I take it "#pragma once" doesn't work in Code::Blocks? :)
Title: .h Files and forward Declarations
Post by: Saboba42 on February 18, 2011, 09:35:52 pm
@Laurent:
Nope, header-guards are unique

@devlin:
I don't think it does, I tried using #pragma once in addition to my guards, which didn't solve the problem, and using only #pragma once resulted in multiple includes.  Perhaps I am using it wrong - I hadn't heard of it before and only looked it up on wiki.

For the record, my header guards are of the type:

#ifndef EVENTMANAGER_H_INCLUDED
#define EVENTMANAGER_H_INCLUDED

//The code here

#endif
Title: .h Files and forward Declarations
Post by: JAssange on February 18, 2011, 10:11:32 pm
GCC (and therefore code blocks) should support #pragma once fine.
Title: .h Files and forward Declarations
Post by: Saboba42 on February 19, 2011, 08:05:36 pm
@JAssange:

Mind giving a little demonstration of how to use the #prima once command correctly?  Like I said, the way I tried it didn't make anything better.
Title: .h Files and forward Declarations
Post by: JAssange on February 19, 2011, 08:19:04 pm
Quote from: "Saboba42"
@JAssange:

Mind giving a little demonstration of how to use the #prima once command correctly?  Like I said, the way I tried it didn't make anything better.

You put #pragma once at the very top of the header file and don't include any header guards, the compiler will insure it is not included multiple times.
Title: .h Files and forward Declarations
Post by: DoctorJ on February 20, 2011, 04:18:19 am
I tried this:
for file: "header1.h"
Code: [Select]
#ifndef HEADER1_H_INCLUDED
#define HEADER1_H_INCLUDED

namespace GQE
{
class A
{
public: int z;
};
};

#endif // HEADER1_HPP_INCLUDED

and for file "header2.h"
Code: [Select]
#ifndef HEADER2_H_INCLUDED
#define HEADER2_H_INCLUDED

#include "header1.h"

namespace GQE
{
class B
{
void Foo(A* theA);
};
};

#endif // HEADER2_HPP_INCLUDED

and I included (but do not use) these files within a simple main program. It compiles ok.

A couple of caveats... include files for cpp are often named with the "hpp" extension instead of "h" - buts thats no biggie.

Per the GCC documentation (http://gcc.gnu.org/onlinedocs/gcc-4.5.2/gcc/Pragmas.html#Pragmas) - avoid pragma commands I don't see "pragma once" in the documentation and it hasn't worked when I've tried it in the past.
Title: .h Files and forward Declarations
Post by: Laurent on February 20, 2011, 10:16:10 am
#pragma once should always be used in addition to header guards. It is just an extra optimization, not a replacement.
Title: .h Files and forward Declarations
Post by: Nexus on February 20, 2011, 01:29:36 pm
When you limit your project to a compiler that supports #pragma once, where is the point in adding include guards?

#pragma once works fine alone, it's just not very portable (though g++ and MSVC support it).
Title: .h Files and forward Declarations
Post by: DoctorJ on February 20, 2011, 03:29:57 pm
Sorry, I did find "pragma once" in the gcc documentation (http://gcc.gnu.org/onlinedocs/gcc-4.5.2/cpp/Alternatives-to-Wrapper-_0023ifndef.html#Alternatives-to-Wrapper-_0023ifndef); along with a warning that it is not always implemented.

Quote
Another way to prevent a header file from being included more than once is with the `#pragma once' directive. If `#pragma once' is seen when scanning a header file, that file will never be read again, no matter what.

`#pragma once' does not have the problems that `#import' does, but it is not recognized by all preprocessors, so you cannot rely on it in a portable program.
Title: .h Files and forward Declarations
Post by: Laurent on February 20, 2011, 05:43:57 pm
Quote
When you limit your project to a compiler that supports #pragma once, where is the point in adding include guards?

You're right. I always think in terms of portability, but sometimes you just don't care.
Title: .h Files and forward Declarations
Post by: Saboba42 on February 20, 2011, 06:15:07 pm
Ok, so I am gathering that #pragma once is out as a solution.

To further deepen the mystery, this is also giving me problems:

.H file for my event manager:

Code: [Select]
#ifndef EVENTMANAGER_H_INCLUDED
#define EVENTMANAGER_H_INCLUDED

#include "GQE/GQE_Types.h"
#include "GQE/App.h"

namespace GQE
{
    class EventManager
    {
        public:

        /**
        *  Init Functions
        */
        EventManager();
        ~EventManager();

        /**
        *  Allows access to the App and its members
        */
        void RegisterApp(App* theApp);

        /**
        *  Receives passed game events and caries out appropriate actions
        */
        void MakeHappen(const MakeEvent theEvent, double x, double y, int nPlayerNum);


        private:
    ///=====================================================================///
    ///VARIABLES:
        App* mApp;
    };//class EventManager
};//namespace GQE

#endif // EVENTMANAGER_H_INCLUDED


-----------------------------------------------------------------------------------

The relevant parts of "GQE/GQE_Types.h" are:

Code: [Select]
#ifndef   GQE_TYPES_HPP_INCLUDED
#define   GQE_TYPES_HPP_INCLUDED

#include <map>
#include <string>

// The following defines help with OS/Compiler specific calls
#ifdef WIN32
#ifndef MINGW
#define STRICMP _stricmp
#else
#define STRICMP strcasecmp
#endif
#else
#define STRICMP strcasecmp
#endif

namespace GQE
{

  /// Status Enumeration for Status Return values < A functioning enum
  enum StatusType {
    // Values from -99 to 99 are common Error and Good status responses
    StatusAppMissingAsset = -4, ///< Application failed due to missing asset file
    StatusAppStackEmpty   = -3,  ///< Application States stack is empty
    StatusAppInitFailed   = -2,  ///< Application initialization failed
    StatusError           = -1,  ///< General error status response
    StatusAppOK           =  0,  ///< Application quit without error
    StatusNoError         =  0,  ///< General no error status response
    StatusFalse           =  0,  ///< False status response
    StatusTrue            =  1,  ///< True status response
    StatusOK              =  1   ///< OK status response

    // Values from +-100 to +-199 are reserved for File status responses
  };

///More enums that actually work here

   ///Lists all possible Events < I added this enum to the file and it does not work
  enum MakeEvent
  {
    AddSpam = 0,
    LastMakeEvent
  };
};//namespace GQE

------------------------------------------------------------------------------

And Code::Blocks yells back at me:

||=== Debug ===|
C:GQE\GQE\EventManager.h|34|error: ISO C++ forbids declaration of 'MakeEvent' with no type|
C:\GQE\GQE\EventManager.h|34|error: expected ',' or '...' before 'theEvent'|
||=== Build finished: 2 errors, 0 warnings ===|
Title: .h Files and forward Declarations
Post by: devlin on February 20, 2011, 07:17:12 pm
Quote from: "Saboba42"
Ok, so I am gathering that #pragma once is out as a solution.

... because?

I'm only using pragma once (without include guards - I haven't used them for several years after getting bit by the same-name "feature" once) and compiling projects happily on windows, linux and mac os x.
Title: .h Files and forward Declarations
Post by: DoctorJ on February 20, 2011, 09:29:08 pm
Sabo, I tried out those code snippets as files (had to add #endif to the end of the GQe_Types.h code above.) I don't know what you are using for App.h so I made a test file:
Code: [Select]
#ifndef APP_H_INCLUDED
#define APP_H_INCLUDED

class App
{
    public:
    sf::RenderWindow thisApp;
};


#endif // APP_H_INCLUDED

and within my main program (which #include's these three headers) I have the lines:
Code: [Select]
  App myApp;
   myApp.thisApp.Create(sf::VideoMode(400, 200, 32), "Test Program");

It compiles and runs without a hiccup. I'm guessing that your problem is more subtle than header guards.
Title: .h Files and forward Declarations
Post by: l0calh05t on February 20, 2011, 10:11:30 pm
Quote from: "devlin"
Quote from: "Saboba42"
Ok, so I am gathering that #pragma once is out as a solution.

... because?

I'm only using pragma once (without include guards - I haven't used them for several years after getting bit by the same-name "feature" once) and compiling projects happily on windows, linux and mac os x.


You really shouldn't use only pragma once. It is a nonstandard extension, so if anything use it in addition to include guards to speed up compilation on compilers that support it.

PS: About the "limiting to one compiler" argument: even in that case, I wouldn't use only pragma once, because you risk getting into a "bad habit" which might come back to bite you when you do need to write a portable piece of software.

One solution to the whole "same name" problem (which can also occur with namespaces!) is to use a (probably) unique identifier, like a 128 bit hash (as hex prepended with a letter to create a valid identifier or some other format) of your name and the name you actually want to use.

In the namespace case renaming is then as simple as writing namespace <new> = <old>; (only in source files though, of course). In the includeguard case, no renaming is needed really. And if you have the odd macro... well... it gets a little ugly. but macros are always ugly.
Title: .h Files and forward Declarations
Post by: Silvah on February 21, 2011, 06:48:48 pm
Let's play a devil's advocate! :D
Quote from: "l0calh05t"
It is a nonstandard extension
Strictly speaking, it's not. Nonstandard extension are the things that are ill-formed according to the standard, but the implementation allows them. #pragma directives are implementation-defined, not ill-formed. Thus, they're not extensions.

Quote from: "l0calh05t"
PS: About the "limiting to one compiler" argument
It's "limiting to all but the most obscure compilers". #pragma once is very widely implemented, I've heard of one (yes, one!) compiler that doesn't support it (I think there are more compilers don't supporting this #pragma, but they're so obscure that chances are nobody of us will use them anyway).

Quote from: "l0calh05t"
One solution to the whole "same name" problem (which can also occur with namespaces!) is to use a (probably) unique identifier, like a 128 bit hash (as hex prepended with a letter to create a valid identifier or some other format) of your name and the name you actually want to use.
In order to avoid name clashes, you suggest using identifiers that are still non-unique, but additionally unwieldy long and horribly ugly. Oh well...
Title: .h Files and forward Declarations
Post by: l0calh05t on February 21, 2011, 07:07:10 pm
Quote from: "Silvah"
Quote from: "l0calh05t"
One solution to the whole "same name" problem (which can also occur with namespaces!) is to use a (probably) unique identifier, like a 128 bit hash (as hex prepended with a letter to create a valid identifier or some other format) of your name and the name you actually want to use.
In order to avoid name clashes, you suggest using identifiers that are still non-unique, but additionally unwieldy long and horribly ugly. Oh well...


They aren't that long really. And it only needs to be applied to the top level namespace. And they much, much, much more likely to be unique than any word or readable acronym. So it is definitely an improvement.
Title: .h Files and forward Declarations
Post by: devlin on February 21, 2011, 07:39:11 pm
To be honest - I won't end up in a bad habit - having used inclusion guards for years during development for multiple simultaneous platforms, including PS2 and Xbox.

The fact is that pragma once is (very) convenient, and while all my current platforms (windows, macosx and linux) have compilers that support it - if I ever come across a platform that doesn't (yet supports c++) - it's nothing a quick perl-script can't handle.

Each to his own, but the arguments against pragma once are mostly archaic. Surprised you didn't bring up actual pitfalls like erroneous symlink-handling though (although I believe it's just LCC having that problem) :)
Title: .h Files and forward Declarations
Post by: Nexus on February 21, 2011, 09:28:31 pm
Quote from: "Silvah"
#pragma directives are implementation-defined, not ill-formed. Thus, they're not extensions.
The #pragma directive itself isn't the problem, that's why you can use both #pragma once and header guards in portable code. However, abandoning header guards may render code ill-formed on a standard-compliant compiler.

I think #pragma once is okay for own projects that only have to work on a few compilers. But when you develop an open-source library that ought to be portable, you should use normal header guards (maybe in addition to #pragma once).
Title: .h Files and forward Declarations
Post by: Silvah on February 21, 2011, 10:20:02 pm
Quote from: "devlin"
Surprised you didn't bring up actual pitfalls like erroneous symlink-handling though (although I believe it's just LCC having that problem)
If the compiler tries to be smart, the same thing can happen with normal include guards, it doesn't apply exclusively to #pragma once.
Quote from: "Nexus"
However, abandoning header guards may render code ill-formed on a standard-compliant compiler.
I think you missed the point slightly, I just said that #pragmas aren't extensions, I didn't said anything about what might happen if a compiler doesn't support the #pragma we're talking about. So, the quote isn't really appropriate here.

You're right, of course. The lack of #pragma once support certainly may render otherwise valid code ill-formed. But how many compilers don't supporting that are you aware of? #pragma once is available on almost all compilers. If it isn't, then probably it's just one of the many things you'll have problems with.

Well, I'm still playing a devil's advocate, I actually agree that in truly portable code normal header guards should be used.
Title: .h Files and forward Declarations
Post by: Saboba42 on February 22, 2011, 06:09:11 am
*cough*

While the merits or non-merits of #pragma once are fascinating, I have already attempted to use the command with my version of Code::Blocks and noticed no noticeable change.  I then decided to attempt removing the #defif header guards, which caused "multiple include" errors, showing me that it is unlikely that my version of the gcc compiler likes #pragma once, and in either case it has not fixed the problem.

Are there any other ides as to the cause of my problem?  Like I said, I am writing the problematic files in the exact same format used by the rest of the engine.  The rest of the engine works fine, and the example code seems to compile fine on other peoples' compilers, but my compiler still gives me errors which tell me the compiler recognizes that I forward-declared an object in a header, but when I try to use that object in another header with the #include command, it still tells me <object name> has no type as if it were not declared.

*steals the above discussion for a small introductory pamphlet on #pragma once and uses the proceeds to hire a 3rd or 4th rate debugging specialist*
Title: .h Files and forward Declarations
Post by: DoctorJ on February 22, 2011, 06:34:14 am
For the sake of testing, can you move or copy that "enum MakeEvent ..." into EventManager.h and confirm that everything would work if it could find it? Perhaps add a fake enum into GQE_Types.h and see if it that can be referenced somewhere else within EventManager.h?
Title: .h Files and forward Declarations
Post by: devlin on February 22, 2011, 08:40:22 am
Sorry Saboba. :)

Just a few thoughts:
Code: [Select]
const MakeEvent theEvent

Did you intend to make it a const reference? (and thus forgot the &)

Also, namespaces shouldn't have a semicolon ';' after them. Usually this isn't much of an issue as it would be stripped out by the compiler as a noop - but I'm not sure how well C::B functions.
Title: .h Files and forward Declarations
Post by: Silvah on February 22, 2011, 09:26:25 am
Quote from: "Saboba42"
I then decided to attempt removing the #defif header guards, which caused "multiple include" errors, showing me that it is unlikely that my version of the gcc compiler likes #pragma once
GCC supports #pragma once since 2.95, I think. It was released in 1999. Now we have 2011, GCC 4.6.0 will be released soon. It's update time!

But... thank you for proving that I was wrong: not only very obscure compilers don't support it. Severely outdated ones don't, either.
Title: .h Files and forward Declarations
Post by: l0calh05t on February 22, 2011, 05:38:08 pm
Quote from: "Silvah"

But... thank you for proving that I was wrong: not only very obscure compilers don't support it. Severely outdated ones don't, either.


You are wrong again, as some versions of GCC didn't/still do not support pragma once (like mingw, not sure if that's still true, but it was for a long time)

And in fact it was declared deprecated after a few versions in GCC. soo.. yeah. avoid it, imo...
Title: .h Files and forward Declarations
Post by: devlin on February 22, 2011, 05:47:27 pm
Quote from: "l0calh05t"
And in fact it was declared deprecated after a few versions in GCC. soo.. yeah. avoid it, imo...

Now you're just grasping at straws :)

Quote
However, with the 3.4 release of GCC, the #pragma once handling code was fixed to behave correctly with symbolic and hard links. The feature was "un-deprecated" and the warning removed.
Title: .h Files and forward Declarations
Post by: l0calh05t on February 22, 2011, 05:52:45 pm
Quote from: "devlin"
Quote from: "l0calh05t"
And in fact it was declared deprecated after a few versions in GCC. soo.. yeah. avoid it, imo...

Now you're just grasping at straws :)

Quote
However, with the 3.4 release of GCC, the #pragma once handling code was fixed to behave correctly with symbolic and hard links. The feature was "un-deprecated" and the warning removed.


So? It was still deprecated for a few versions. Just pointing out that such extensions cannot be relied on.
Title: .h Files and forward Declarations
Post by: Silvah on February 22, 2011, 06:38:37 pm
Quote from: "l0calh05t"
some versions of GCC didn't/still do not support pragma once (like mingw, not sure if that's still true, but it was for a long time)
[citation needed]

Quote from: "l0calh05t"
it was declared deprecated after a few versions in GCC
It was. It's not since GCC 3.4.0, which was released in 2004.

Quote from: "l0calh05t"
Just pointing out that such extensions cannot be relied on.
#pragmas are not extensions. Read my post again (http://sfml-dev.org/forum/viewtopic.php?p=27762#27762).
Title: .h Files and forward Declarations
Post by: l0calh05t on February 22, 2011, 07:00:49 pm
Quote from: "Silvah"
It was. It's not since GCC 3.4.0, which was released in 2004.

I said was. ;)

Quote
#pragmas are not extensions. Read my post again (http://sfml-dev.org/forum/viewtopic.php?p=27762#27762).


"#pragma" in and of itself is not an extension, but "#pragma once" is not part of any C or C++ standard. #pragma is specifically designed for compiler/os specific options and therefore non-portable.

About mingw: The current version is ok with pragma once, although I did find a pragma once related bug in it's tracker (for the current version)
Title: .h Files and forward Declarations
Post by: Silvah on February 22, 2011, 07:58:49 pm
Quote from: "l0calh05t"

I said was. ;)
Of course you did. The point is that, well, it was but it is not anymore.

Quote from: "l0calh05t"

"#pragma" in and of itself is not an extension, but "#pragma once" is not part of any C or C++ standard.
Now you're right. :P

Quote from: "l0calh05t"
#pragma is specifically designed for compiler/os specific options and therefore non-portable.
In theory, yes. In practice, no. Vast majority of modern compilers do implement #pragma once. Compare that with export: a standard feature that is implemented by one or two compilers.

Quote from: "l0calh05t"
About mingw: The current version is ok with pragma once, although I did find a pragma once related bug in it's tracker (for the current version)
It'll be very kind of you if you post a link.
Title: .h Files and forward Declarations
Post by: l0calh05t on February 22, 2011, 08:06:34 pm
Quote from: "Silvah"
Quote from: "l0calh05t"
About mingw: The current version is ok with pragma once, although I did find a pragma once related bug in it's tracker (for the current version)
It'll be very kind of you if you post a link.

Sure, here you go: http://permalink.gmane.org/gmane.comp.gnu.mingw.announce/2771 (tracker link is in the message)
Title: .h Files and forward Declarations
Post by: Saboba42 on February 23, 2011, 03:19:49 am
So... could we please move the (now proven to not be a fix) #pragma once discussion to another thread please?  Thank you.

@DoctorJ

Quote
For the sake of testing, can you move or copy that "enum MakeEvent ..." into EventManager.h and confirm that everything would work if it could find it? Perhaps add a fake enum into GQE_Types.h and see if it that can be referenced somewhere else within EventManager.h?


Yes, I just did and it compiles completely fine if the enum statement is placed in EventManager.h inside the namespace GQE brackets.  This follows the original pattern - the compiler recognizes the included headers, but not the forward declarations.  However, if the forward declarations are made in the files they are used in, they work just fine.  Other header files that originally came with the source code can use declarations in the GQE/GQE_Types.h perfectly fine - it's just a few headers that I have added that can't find anything there.

Oh wow... just did more testing and the plot thickens.  There is another enum in the GQE_Types file that has been there since I got the original source code called StatusType.  When I replace "MakeEvent" with "StatusType" in EventManager.h, the program compiles normally.  However, when I use MakeEvent, the program will only compile if my new enum (MakeEvent) is declared in the EventManager.h header.

Summery: the enums and class declarations that came with GQE_Types are completely usable in all header files that GQE_Types is included in.  All of the class declarations and enums that I have added to the header will only work if I make the declaration in the header that I use them in (However, if I include EventManager.h in EventManager.cpp, then the .cpp file will use MakeEvent just fine - it just doesn't like my enums/forward declarations added to GQE_Types.h)

@devlin:
Quote
Did you intend to make it a const reference? (and thus forgot the &)

Also, namespaces shouldn't have a semicolon ';' after them. Usually this isn't much of an issue as it would be stripped out by the compiler as a noop - but I'm not sure how well C::B functions.


No, just meant to pass the MakeEvent to the function.  Thanks for the namespace tip.  I stripped out a few semi-colons from the namespace brackets and it didn't do anything good or bad.  I'm too lazy to go through and take them out, so they'll probably stay for a bit  :P
Title: .h Files and forward Declarations
Post by: DoctorJ on February 24, 2011, 12:38:31 am
try moving your "MakeEvent" enum to preceed the "StatusType" enum. (Just in case there is some mismatch of braces that errantly signal the end of the namespace.)
Title: .h Files and forward Declarations
Post by: Saboba42 on February 24, 2011, 06:03:17 pm
Nope, problem remains :x .

<UPDATE> The problem is solved when I put the problem enums in their own header file.  For some reason, the compiler didn't recognize the enums in GQE_Types.h, but it is fine with them in their own header file.  I guess this solves my problem, but I would like to know what makes this happen for future reference</UPDATE>

It looks like the compiler is rebuilding everything when I change the GQE_Types.h file, but it just feels like the compiler is skipping over everything I add...  Anything that could cause it to see only the stuff that was in the file on the first compile?  Fixated on an out of date object file?  I cleared them all, though and got a fresh compile, so maybe that's out too.
Title: .h Files and forward Declarations
Post by: DoctorJ on February 24, 2011, 06:53:36 pm
This may sound silly, but double check that the current GQE_Types.h file is actually the one being used by CB for the project. CB will happily keep files in the editor without them being part of the project. I've been caught by that before - it may reference a file in a different directory with the same name but obviously, it doesn't get updated as you edit the file with the same name in another directory.
Title: .h Files and forward Declarations
Post by: Saboba42 on February 25, 2011, 01:33:41 am
@doctorJ:
I think that's what was wrong.  I found another version of the file in another folder and moved it.  The whole project rebuilt and it all works now.  Thanks for your help (and not talking about #pragma once =P)