Does gcc hate Forward declarations?

This is a discussion on Does gcc hate Forward declarations? within the C++ Programming forums, part of the General Programming Boards category; I have a frustrating task of porting some highly templated code I wrote using MS visual studio to compile and ...

  1. #1
    Registered User
    Join Date
    Apr 2007
    Posts
    129

    Does gcc hate Forward declarations?

    I have a frustrating task of porting some highly templated code I wrote using MS visual studio to compile and run via gcc. In my complex header file I have several classes that reference each other. That's no problem in C++ in theory since you are supposed to be able to use 'forward declarations' within your header.

    I took an example from this site,
    http://www.devx.com/tips/Tip/12583

    Well his example didn't compile, so I tweaked it a little until it compiled on MS Visual Studio. Here is the result:

    Code:
    //file: bank.h
    class Account; //forward declaration
    
    class Report{
    public:
    //fine: Account is known to be a not-yet defined class
    void Output(const Account& account)
    {} ; 
    };
    
    class Account {
    Report rep;
    public:
    void Show() {
    	rep.Output(*this);
    }
    };
    
    
    int main(int argc, char *argv[])
    {
    
    Account A ;
    Report R ;
    
    R.Output(A) ;
    
    }
    So vis. studio 2005 compiles this but gcc 4.3.0 gives the error:
    test2.cpp:13: error: cannot call member function \u2018void Report::Output(const Account&)\u2019 without object


    In the actual code I'm trying to compile with gcc I have something like this:
    Code:
    // some forward declarations
    class dynmatrix ;
    template <int nrows, int ncols, int ld = nrows>
    class cmatrix  ;
    
    template <int nrows, int ncols, int ld = nrows>
    class rmatrix  ;
    
    template<int nrows, int ncols, int ld>
    protected:
    int issub ;
    
    public:
    float *val ;
    
    /* construct from a dynamic matrix */
    rmatrix(dynmatrix &dm)
    {
    
    // construction code
    
    }
    
    // lots more stuff
    } ;  // end rmatrix class
    
    // actual dynmatrix code
    class dynmatrix {
    protected:
    int issub ;
    
    public:
    float *val ;
     
    // ...
    
    // some code that references rmatrix
    template<int nr, int nc, int uld>
    	dynmatrix(rmatrix<nr,nc,uld> &cm) : nrows(nr), ncols(nc), ld(uld)
    	{
    	mtype = Realmatrix ;
    	val = cm.tohost() ;
    
    
    	}
    
    } ;  // end class dymmatrix
    This bit of code is giving me errors on the forward declarations, namely

    matrix.h: In constructor \u2018rmatrix<nrows, ncols, ld>::rmatrix(dynmatrix&)\u2019:
    matrix.h:406: error: invalid use of incomplete type \u2018struct dynmatrix\u2019
    matrix.h:80: error: forward declaration of \u2018struct dynmatrix\u2019


    The first error is in the rmatrix constructor that takes a dynmatrix argument and the second is in the forward declaration itself.

    Now if you can't do forward declarations in gcc 4.3, that's going to break a lot of code. Is there some kind of work around for this?
    Last edited by SevenThunders; 03-16-2009 at 12:49 PM. Reason: Pasted the wrong example code as per laserlight comments

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,647
    Quote Originally Posted by SevenThunders
    So vis. studio 2005 compiles this but gcc 4.3.0 gives the error:
    test2.cpp:13: error: cannot call member function \u2018void Report::Output(const Account&)\u2019 without object
    That error has nothing to do with forward declarations, but with the fact that you are trying to call a non-static member function without an object of the class. MSVC8 gives me a similiar error: "illegal call of non-static member function", as does the Comeau online compiler: "error: a nonstatic member reference must be relative to a specific object".
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The templated real code is too fragmented to tell anything. It looks like the problem is that you are indeed trying to use some forward declared type where full type is required. Declare all classes and implement them later when all declarations are available.

    E.g
    Code:
    rmatrix(dynmatrix &dm)
    {
    
    // construction code
    
    }
    If the full definition of dynmatrix is not available at this point, you can't really do anything with dm in the constructor body. Implement the constructor later after the full declaration of dynmatrix.

    Another issue is probably the default arguments. I think you can only specify the defaults at one place (otherwise they might get out of sync) and I guess forward declaration is not the right place to do it. (Well, it doesn't seem to matter but things would still get hairy if you had to forward declare the same class in more than one places.)
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  4. #4
    Registered User
    Join Date
    Apr 2007
    Posts
    129
    Ahhh yeah, you are right anon. I do need to show a bit more code. I need, in particular to show that I am accessing members of the dynamic matrix in that constructor. Here is some missing code.


    Code:
    template<int nrows, int ncols, int ld>
    class rmatrix {
    
    
    /* construct from a dynamic matrix */
    rmatrix(dynmatrix &dm)
    {
    assert(nrows == dm.nrows) ;
    assert(ncols == dm.ncols) ;
    assert(dm.mtype == Realmatrix) ;
    if (dm.ld != nrows) {
    	dynmatrix dt(nrows, ncols, Realmatrix) ;
    	dt = dm ;
    	fromhost(dt.val) ;
    
    }  else {
    
    fromhost(dm.val) ;
    
    
    }
    
    }
    
    } ; // end of rmatrix
    I think once you start actually accessing members of a forward declaration, gcc 4.3 gets unhappy. A web search is suggesting replacing the forward declarations with typedefs, which seems really odd to me.

    Oh and for laserlight, for some reason I pasted in older buggy code for my example. I fixed that and it still doesn't compile under gcc, but does using ms visual C 2005.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,647
    Quote Originally Posted by SevenThunders
    I think once you start actually accessing members of a forward declaration, gcc 4.3 gets unhappy.
    That is normal, and the solution is as anon described: move the function definition to after the forward declared class' definition.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,548
    You cannot access members or construct objects with incomplete types (ie forward declarations)!
    Visual C++ should certainly not compile and I am not sure how you get it to do that, but it certainly is wrong! How is the compiler supposed to know the existence of these members or functions, if it cannot see the class definition?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,239
    Quote Originally Posted by Elysia View Post
    You cannot access members or construct objects with incomplete types (ie forward declarations)!
    Visual C++ should certainly not compile and I am not sure how you get it to do that, but it certainly is wrong! How is the compiler supposed to know the existence of these members or functions, if it cannot see the class definition?
    It happens... My coworker just got done cleaning up some heavy template code which built fine on MSVC and GCC barked at. Yet another reason to never trust a compiler to tell you what the standard is.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #8
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,548
    True, I know that VC++ may not be the strictest compiler. It tend to be lax on some things.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    Registered User
    Join Date
    Apr 2007
    Posts
    129
    Quote Originally Posted by Elysia View Post
    You cannot access members or construct objects with incomplete types (ie forward declarations)!
    Visual C++ should certainly not compile and I am not sure how you get it to do that, but it certainly is wrong! How is the compiler supposed to know the existence of these members or functions, if it cannot see the class definition?
    It's perfectly reasonable actually. There are a few languages that allow you to use functions, for example that are declared later in the same source file. It requires that the compiler run more than 1 pass through the code. As it so happens the compiler has to run through the code multiple times in most cases just to implement some common optimizations such as for example register assignment.

    I think in D they have a module system so you can use any function declared in a given module in any order. Any good assembler will do it as well. You can jump or call a label that exists past the code that calls the jump. I wrote a byte code assembler that does this. You simply save the location of the unreferenced symbols and then apply a second processing stage to fill in the undefined references once you have more information.

    Thanks anon for reminding me about defining the function later. I avoid that so much that I forgot it was even available! I hate all that :: syntax clutter and the endless forward declarations.

  10. #10
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,548
    That sounds very well and all, but unfortunately, C++ does not have such a feature.
    So I find it very unreasonable for a language as type strict as C++ to allow the use of an incomplete type. That's something to keep in mind.
    I also figure that such a system would create headaches for compiler vendors (I certainly know templates does) or it might have been implemented, or suggested, already. Only a guess, though.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,239
    Quote Originally Posted by Elysia View Post
    That sounds very well and all, but unfortunately, C++ does not have such a feature.
    So I find it very unreasonable for a language as type strict as C++ to allow the use of an incomplete type. That's something to keep in mind.
    I also figure that such a system would create headaches for compiler vendors (I certainly know templates does) or it might have been implemented, or suggested, already. Only a guess, though.
    The situation gets a bit less obvious when templates are involved. Looking at template code, it might "appear" like a type is not complete at the time of template declaration, but that doesn't matter. It only matters that the type is complete at the time of template INSTANTIATION. So you can get away with stuff like this:

    Code:
    class Bar;
    
    template <typename T>
    void foo(T dummy, Bar &baz)
    {
        baz.quuz();
    }
    
    class Bar
    {
    public:
        void quuz();
    };
    It looks like Bar is not fully defined at the time of use, but it's not actually being "used" until the template foo<T> gets instantiated, which could very well be AFTER the point where Bar is fully defined.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  12. #12
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    What brewbuck says is exactly the reason why VC++ compiles the code, but it's not standard C++.

    The thing is, VC++ doesn't do semantic analysis of templates at all before they're instantiated. It pretty much tokenizes the template body and then stores the tokens away, to be reparsed when the template is instantiated, at the place (and in the context) of the instantiation. The consequence? A type that is incomplete at the template definition site can be used if it's complete at the instantiation site.

    But that's non-compliant behavior. The standard requires the compiler to do all semantic analysis except for a few things that involve actual template parameters. The call to baz.quuz() in the example above does not involve the template parameters at all, and thus the compiler is actually required to refuse to parse the template body.

    (More correctly, the compiler may delay diagnostics until the instantiation, but it must emit the same diagnostics as it would if it didn't.)
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  13. #13
    Registered User
    Join Date
    Apr 2007
    Posts
    129
    Quote Originally Posted by brewbuck View Post

    It looks like Bar is not fully defined at the time of use, but it's not actually being "used" until the template foo<T> gets instantiated, which could very well be AFTER the point where Bar is fully defined.
    Very good point.

    Also apparently incomplete forward declarations are not too hard for some compiler vendors, since Microsoft appears to support it. I personally don't see why it shouldn't be allowed, especially if it's in the same source file, but then I'm not writing the standards.

    Anyway this doesn't bother me as much as the stupid arcane rules for explicit template class member function instantiation, but I think that one deserves it's own thread. It's another area where MS is a bit more liberal than gcc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. forward class declarations
    By manzoor in forum C++ Programming
    Replies: 17
    Last Post: 12-05-2008, 02:55 AM
  2. how do you resolve this error?
    By -EquinoX- in forum C Programming
    Replies: 32
    Last Post: 11-05-2008, 03:35 PM
  3. Tabbed Windows with MDI?
    By willc0de4food in forum Windows Programming
    Replies: 25
    Last Post: 05-19-2005, 10:58 PM
  4. Forward Declarations in .net
    By Cornpops in forum C++ Programming
    Replies: 2
    Last Post: 04-01-2003, 01:22 PM
  5. Pet Peeves
    By Srg Pepper in forum A Brief History of Cprogramming.com
    Replies: 29
    Last Post: 10-03-2002, 11:34 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21