Thread: invalid use of incomplete type 'struct a'

  1. #1
    Registered User
    Join Date
    May 2010
    Posts
    4

    invalid use of incomplete type 'struct a'

    Hi to all, I m quite new to c++ and i try to compile this little program without success...
    Code:
    class b;
    class a;
    
    class b
    {
    public:
        a *aa;
        b()
        {
            aa = new a();
        }
        ~b();
    };
    class a
    {
    public:
        b *bb;
        a()
        {
            bb = new b();
        }
        ~a();
    };
    
    
    void main(int argc, char *argv[])
    {
        a my_a;
        b my_b;
    }
    What is wrong with this code ?

    Thank you for your coments and help.

  2. #2
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472

    names

    you might be able to figure it out yourself, or have a better chance at it if you name your variables a little more descriptively!
    Thought for the day:
    "Are you sure your sanity chip is fully screwed in sir?" (Kryten)
    FLTK: "The most fun you can have with your clothes on."

    Stroustrup:
    "If I had thought of it and had some marketing sense every computer and just about any gadget would have had a little 'C++ Inside' sticker on it'"

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by ajoqiqu View Post
    What is wrong with this code ?
    The return type of main is int, not void, meaning it also needs a return value.

    I don't think there is a good way to do what you want here. Why? Because of some stupidity in the design of C++: for reasons of "safety", you cannot use a void* (or any other kind of pointer) generically the way you can in C. Your forward declarations of a and b are incomplete, but if you complete them, b will contain a reference to an undefined type.

    So the trade-off for safety is dysfunction and awkwardness in this case. Sorry. You might be able to get out of it by making aa a static variable of a template function in b, since that function will only need a prototype in b's declaration (no reference to a). Actually that's not allowed either, template functions need to be defined in the declaration. Sorry again.
    Last edited by MK27; 05-15-2010 at 08:33 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by MK27
    I don't think there is a good way to do what you want here. Why? Because of some stupidity in the design of C++: for reasons of "safety", you cannot use a void* (or any other kind of pointer) generically the way you can in C. Your forward declarations of a and b are incomplete, but if you complete them, b will contain a reference to an undefined type.
    I do not know what exactly does ajoqiqu want to do here, but there may be an obvious solution: define the member functions of b after the definition of a.
    Code:
    class a;
    
    class b
    {
    public:
        a* aa;
    
        b();
    
        ~b();
    };
    
    class a
    {
    public:
        b* bb;
    
        a()
        {
            bb = new b();
        }
    
        ~a()
        {
            delete bb;
        }
    };
    
    int main()
    {
        a my_a;
        b my_b;
    }
    
    b::b()
    {
        aa = new a();
    }
    
    b::~b()
    {
        delete aa;
    }
    In fact, this solution is applicable to C: you would define functions that require the definition of a struct after the struct definition is in scope. It seems to me that once you come across something that you see as a "stupidity in the design of C++" (and as far as I can tell the premise of your rant is inaccurate to begin with), your mind goes blank and you forget even those techniques that can be carried over from C.

    EDIT:
    Beyond this, ajoqiqu: are you are of the so-called rule of 3? That is, when you need to define a destructor yourself (other than trivially to declare it virtual), you typically need to declare the copy constructor and copy assignment operator?
    Last edited by laserlight; 05-15-2010 at 01:24 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    Quote Originally Posted by MK27 View Post

    I don't think there is a good way to do what you want here. Why? Because of some stupidity in the design of C++: for reasons of "safety", you cannot use a void* (or any other kind of pointer) generically the way you can in C. Your forward declarations of a and b are incomplete, but if you complete them, b will contain a reference to an undefined type.

    So the trade-off for safety is dysfunction and awkwardness in this case. Sorry. You might be able to get out of it by making aa a static variable of a template function in b, since that function will only need a prototype in b's declaration (no reference to a). Actually that's not allowed either, template functions need to be defined in the declaration. Sorry again.
    Actually you can MK; look up static_cast<somePtrType>(void *argument)...anytime you have to interact with the pthread environment you need to do something like this and C++ likes it just fine:
    Code:
    //********************************************
    // Method:workFunc()
    //
    // Args:void *args - void ptr to data, def is NULL
    //
    // Return:void * - result code of this thread
    //
    // Desc:Wrapper fn to start a thread running
    //********************************************
    void *workFunc(void *args)
    {
       FoundationThreadBase *ptr = 
          static_cast<FoundationThreadBase *>(args);
       int nRC = ptr->Run(ptr->GetData());
       ptr->SetThreadRunningFlag(false);
       ptr->SetThreadExitedState(true);
    }

    It's the purists that it ticks off and since none of them pays my wage, they can take a long walk off of a short pier
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by laserlight View Post
    I do not know what exactly does ajoqiqu want to do here, but there may be an obvious solution: define the member functions of b after the definition of a.
    That was my first thought too, here's the code I tried which is near identical to yours (and they both compile fine but produce a segmentation fault):

    Code:
    class a;
    class b;
    
    class b {
    	public:
    		a* aa;
    		b();
    };
    
    class a {
    	public:
    		b* bb;
    		a();
    };
    
    b::b() {
    	aa = new a();
    }
    
    a::a(){
    	bb = new b();
    }
    
    int main() {
        a my_a;
        b my_b;
    }
    The segfault has an identical cause too: a sort of infinitely recursive memory corruption (backtrace for your code):

    #0 _int_malloc (av=0x7ffff7633a00, bytes=<value optimized out>)
    at malloc.c:4130
    #1 0x00007ffff7342a08 in __libc_malloc (bytes=8) at malloc.c:3551
    #2 0x00007ffff7b981ed in operator new (sz=8)
    at ../../../../libstdc++-v3/libsupc++/new_op.cc:57
    #3 0x0000000000400859 in a (this=0xa006b0) at test2.cpp:20
    #4 0x0000000000400716 in b (this=0xa00690) at test2.cpp:37
    #5 0x0000000000400866 in a (this=0xa00670) at test2.cpp:20
    #6 0x0000000000400716 in b (this=0xa00650) at test2.cpp:37
    #7 0x0000000000400866 in a (this=0xa00630) at test2.cpp:20
    #8 0x0000000000400716 in b (this=0xa00610) at test2.cpp:37
    #9 0x0000000000400866 in a (this=0xa005f0) at test2.cpp:20
    #10 0x0000000000400716 in b (this=0xa005d0) at test2.cpp:37
    #11 0x0000000000400866 in a (this=0xa005b0) at test2.cpp:20

    [...etc]

    Perhaps the reason is sort of obvious Not even a void* will save this logical flaw.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jeffcobb View Post
    Actually you can MK; look up static_cast<somePtrType>(void *argument)...
    Hmm, can you use that to cast to an object too? I presume so...FoundationThreadBase is a class, no?

    Still think that thing with C++ void* might be one of the single dumbest design choices I've ever heard of. It kind of smacks of a "business" move (to create an incompatibility with C API's).
    Last edited by MK27; 05-15-2010 at 01:41 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by MK27
    The segfault has an identical cause too: a sort of infinitely recursive memory corruption (backtrace for your code):
    Ah, but it is precisely because the members are pointers that, in a more fully developed class, one of the members could be a null pointer, thus ending this "infinitely recursive" problem, as how one might do it with a linked list implementation. As it stands, my example simulates the otherwise impossible situation of having a member of a class that has as a member an object of the current class. This is hardly a problem with the design of C++. It is a question of what ajoqiqu wants to do.

    Quote Originally Posted by MK27
    Hmm, can you use that to cast to an object too? I presume so...FoundationThreadBase is a class, no?
    Yes, you can. You can freely cast a pointer to an object to a pointer to void* and back.
    Last edited by laserlight; 05-15-2010 at 01:47 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    Quote Originally Posted by MK27 View Post
    Hmm, can you use that to cast to an object too? I presume so...FoundationThreadBase is a class, no?

    Still think that thing with void* might be one of the single dumbest design choice I've ever heard of.
    MK: FoundationThreadBase is an abstract base class I use (this is part of what will get mailed to you this weekend) to easily manage threads so basically all you need to do to create a thread is:
    Code:
    #include "foundationthread.hpp"
    
    // making a thread is as simple as deriving a class from Foundation
    // ThreadBase, providing a run() method, creating an instance of this
    // derived class and call start()
    class ThreadTest:public FoundationThreadBase
    {
    private:
    
    
    public:
        ThreadTest()
        {
            //MT
    
        }
        ~ThreadTest()
        {
            //MT
        }
    
        virtual int Run(void *ptrArgs);
    
    
    };
    And then supplying a Run() method:
    Code:
    //********************************************
    // Method:Run()
    //
    // Args:None
    //
    // Return:0 on success, -1 if fail
    //
    // Desc:Actual run routine for this thread
    //********************************************
    int ThreadTest::Run(void *ptrData)
    {
        int nResultCode = 0;
        // supply the thread work functionality here...
        return nResultCode;
    }
    Then in your main thread, to launch this it is as simple as:

    Code:
        ThreadTest myThread;
        myThread.Start(false);
        myThread.Join();
    Note: this is still a work in progress but to me this has always been the simplest way to launch threads and manage them effectively. You will see the whole code-base later this weekend.
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  10. #10
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    Quote Originally Posted by laserlight View Post
    Ah, but it is precisely because the members are pointers that, in a more fully developed class, one of the members could be a null pointer, thus ending this "infinitely recursive" problem, as how one might do it with a linked list implementation. As it stands, my example simulates the otherwise impossible situation of having a member of a class that has as a member an object of the current class. This is hardly a problem with the design of C++. It is a question of what ajoqiqu wants to do.


    Yes, you can. You can freely cast a pointer to an object to a pointer to void* and back.
    Laser: you are too fast for me
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by jeffcobb
    Laser: you are too fast for me
    Grr... now that I read your quote, I see that my original statement was correct. The edit that changed void to void* is wrong.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The real problem and one that can be solved in C++ is an incorrect use of the langugage. Had the implementation of said classes been in a separate file then the forward declare would have worked fine. You can forward declare anything you want so long as you don't actually use the class you are forward declaring. So if A needs B and B needs A and A has both a header and an impl and B has both a header and an impl the forward declare works just fine. The problem arises from this section:

    Code:
    class b;
    class a;
    
    class b
    {
    public:
        a *aa;
        b()
        {
            aa = new a();
        }
        ~b();
    };
    The pointer aa is fine b/c after all it's really just 4 bytes. The type is a compiler side feature but in the end it is still just 4 bytes. However the constructor for b is where the problem is.
    How can the compiler instantiate aa when a has not been fully declared? class a is incomplete and therefore that line of code will generate the error that you received. As well there is no reason to forward declare b since b is declared fully prior to a. The only problem here is an incorrect use of the language and it is not related to any pointer problems or fundamental problems in the design of C++.

    Had b had a header where a was forward declared and b had an impl where a was included and used this would have worked just fine. Plain and simple you cannot use (read: instantiate) a class you have not fully declared.
    Last edited by VirtualAce; 05-15-2010 at 02:14 PM.

  13. #13
    Registered User
    Join Date
    May 2010
    Posts
    4
    Thank you for all your comment, your reflexion help me to better understand the problem.

    my goal is to modelize an object !

    an object have some properties !

    a property is an object !

    and the problem start...

    thanks

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by ajoqiqu
    my goal is to modelize an object !

    an object have some properties !

    a property is an object !
    Okay, so what exactly are you trying to model?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered User
    Join Date
    May 2010
    Posts
    4
    well, i model an object by a list of property and i model a property as a type ( a type is a reference to an object ) but also i model an object without any property just his name, its a base_type !

    I have an object : object_1

    object_1 have some properties : property_1, property_2

    the type of propertry_1 is 2 that s mean property_1 refere to object_2
    the type of propertry_2 is 3 that s mean property_2 refere to object_3

    object_2 is base_type
    object_3 is base_type



    Do you understand ?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help assignment due tomorrow
    By wildiv in forum C Programming
    Replies: 6
    Last Post: 01-27-2010, 08:38 PM
  2. Global Variables
    By Taka in forum C Programming
    Replies: 34
    Last Post: 11-02-2007, 03:25 AM
  3. Problem with Visual C++ Object-Oriented Programming Book.
    By GameGenie in forum C++ Programming
    Replies: 9
    Last Post: 08-29-2005, 11:21 PM
  4. ras.h errors
    By Trent_Easton in forum Windows Programming
    Replies: 8
    Last Post: 07-15-2005, 10:52 PM
  5. Glib and file manipulation
    By unixOZ in forum Linux Programming
    Replies: 1
    Last Post: 03-22-2004, 09:39 PM