ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have a project with a lot of interdependent C++ classes, which means that I can't #include the other class's header in a header because of conflicts. So I decided to try declaring the classes using "class ClassName;" in the header and #include'ing the header in the source file, but then I get errors like this:
Code:
$ make
[ 4%] Building CXX object lib/CMakeFiles/lang.dir/lang_object.cpp.o
In file included from /home/michael/projects/lang/lib/lang_object.cpp:1:0:
/home/michael/projects/lang/lib/table.hpp:32:9: error: ‘String’ does not name a type
/home/michael/projects/lang/lib/lang_object.cpp:7:24: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:12:34: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:18:36: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:24:37: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:30:36: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:36:39: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:42:42: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:61:43: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:79:44: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:98:42: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:117:48: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
/home/michael/projects/lang/lib/lang_object.cpp:133:24: error: invalid use of incomplete type ‘struct lang::LangObject’
/home/michael/projects/lang/lib/table.hpp:11:11: error: forward declaration of ‘struct lang::LangObject’
make[2]: *** [lib/CMakeFiles/lang.dir/lang_object.cpp.o] Error 1
make[1]: *** [lib/CMakeFiles/lang.dir/all] Error 2
make: *** [all] Error 2
The class of a non-pointer, non-reference member must be defined prior to the class it's used in so the class size and member offsets are known. I can't imagine any dependency that would prevent this, other than a circular (non-pointer) dependency. You should therefore reconsider how your headers are arranged.
Kevin Barry
1) Class A has a non static member of type B and class B has a non static member of type A.
That is simply impossible to compile. There is no correct syntax, because there is no correct result from compiling it.
2) Same but with one and/or the other a static member. I'm not sure. I never wanted that circularity.
3) The common circularity: Class A and B each have inline member functions that need to use the definition of the other.
You need each class defined (including declaration of its inline functions) before the definition of the other class's inline functions. A minimal (but not recommended) sequence would be:
class B;
class A { declaring but not defining A's inline functions };
class B { defining its inline functions };
define A's inline functions;
I prefer, when a class is involved in any such circularity, to split its hpp file into two files. Normally I have A.hpp defining A and its inline functions. Instead, I would have A_declare.hpp defining class A but only declaring its inline functions, then A.hpp includes A_declare.hpp and includes other X_declare.hpp involved in the circularity, and then defines A's inline functions. Then any .cpp file needing to use A should include A.hpp but any .hpp file needing to use A includes just A_declare.hpp. It takes some extra care and some compiler option to help with warnings when you get details wrong, but it works out well.
4) There are some other circularities I wouldn't create, so I'm not sure how/whether you can sort them out: For example, each of A and B might have a member function returning by value an object of the other class. I don't know whether that could work with the approach described in (3). If the return were by pointer or reference, then it is just one of many things included in (3).
Someone I used to work with used dummy templates to resolve uglier circularities without resequencing and/or splitting the definitions. I think most of that approach is not valid C++ and won't work in newer versions of gcc. I cleaned all of that out of the code he wrote in favor of methods that took some extra effort to set up, but resulted in more maintainable code. I'm not sure whether there are some cases in which valid dummy templates can more usefully resolve circularities (there are none left in the code I maintain). I do use templates to allow some very powerful circularity between a base class and derived class:
template <class B> class A { ... };
class B : public class A<B> { ... };
The classes I'm talking about have lots of circular dependencies.
You mean like A contains B, and B contains A or has A as a base class? Those things aren't possible in C++.
Looking again, it appears your error is at the point of use in the .cpp; therefore, just include the header defining lang::LangObject in the .cpp, not in the .hpp.
Kevin Barry
PS And also fix "/home/michael/projects/lang/lib/table.hpp:32:9: error: ‘String’ does not name a type". That could keep one of your classes from being defined.
Looking again, it appears your error is at the point of use in the .cpp; therefore, just include the header defining lang::LangObject in the .cpp, not in the .hpp.
I'm not sure I understand, why would lang::LangObject's header be included in itself?
Quote:
Originally Posted by ta0kira
PS And also fix "/home/michael/projects/lang/lib/table.hpp:32:9: error: ‘String’ does not name a type". That could keep one of your classes from being defined.
/home/michael/projects/lang/lib/table.hpp:32:9: error: ‘String’ does not name a type
In lang_object.cpp, make sure that header that defines "String" is included BEFORE table.hpp.
Quote:
Originally Posted by MTK358
/home/michael/projects/lang/lib/lang_object.cpp:7:24: error: invalid use of incomplete type ‘struct lang::LangObject’
Header that declares LangObject is not included in lang_object.cpp OR lang::LangObject used in non-pointer variable before the declaration of lang::LangObject structure. Include required header, or move declaration of lang::LangObject to the beginning of file.
Quote:
Originally Posted by MTK358
What if I need the member not to be a pointer?
You move every single structure into separate header, use one header per class (and one class per header) and use forward declarations + (smart) pointers when possible. See Qt 4 sources for example.
You can also use pointer to abstract base class instead of pointers to actual class to avoid some of the circular dependencies. It is possible to declare abstract base class in *.h, extend it in cpp with "private" class, and return pointer to private class declared in cpp using builder/factory method that returns pointer to base class and is visible from *.h.
It is impossible for class A to contain B and for class B to contain A as non-pointer/non-reference at the same time, since both classes would have infinite size as a result. Aside from that there are no unsolvable circular dependencies.
Quote:
Originally Posted by MTK358
I'm not sure I understand, why would lang::LangObject's header be included in itself?
IF you forgot include guard, a header can get included into itself (in a.h #include "b.h", in b.h, #include "a.h"). Situation can be solved by adding include guard or by splitting header into several different headers. Even with include guard you can still get problems if you have circular inclusions (due to declaration order).
Compiler operates on cpp files, one file at a time, doesn't know anything about other cpp files, and inserts every #included header completely into cpp file before compiling the cpp. Keep that in mind, and all problems will be easy to solve.
I'm not sure I understand, why would lang::LangObject's header be included in itself?
Does this mean you're defining the class in the .cpp file with the error, but below where the errors are? I'm sure we'd make more progress if you posted some code that demonstrated the problem.
Kevin Barry
Be wary of storing a pointer to a string, such as what seems to be your intent with this declaration:
Code:
AssocArray<const char*, LangObject> *contents;
Unless you are referencing hard-coded strings (which live for the entirety of the program), you have a good chance of losing track of the contents of your table.
Consider the following small program where in lieu of your AssocArray, I use an STL map. I prompt the user to enter their name twice, the first time for the purpose of inserting an entry into the map, the second time to retrieve what I hope will be the same entry. If you run the program, you will see that it fails to find the entry even when using the "same" string twice.
Code:
#include <map>
#include <stdexcept>
#include <string>
#include <iostream>
class Foo
{
public:
Foo() : theMap() {}
void set(const char* name, const int value)
{
theMap[name] = value;
}
int get(const char* name)
{
std::map<const char*, int>::iterator it = theMap.find(name);
if (it != theMap.end())
{
return it->second;
}
throw std::runtime_error("Name not found.");
}
private:
std::map<const char*, int> theMap;
};
int main()
{
try {
Foo foo;
std::string name, name2;
std::cout << "Enter your first name: ";
std::getline(std::cin, name);
foo.set(name.c_str(), 10);
while (true)
{
std::cout << "Enter your first name again: ";
std::getline(std::cin, name2);
if (name == name2) break;
}
int value = foo.get(name2.c_str());
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
You need to #include "lang_object.hpp" in lang_object.cpp. Also, it's strange you posted no C++ code for lang_object.cpp, given that that's where all of the compilation errors were.
Kevin Barry
lang_object.hpp should be either included from within table.hpp or it should be #included before table.hpp in all files that use table.hpp
That fixed it. I didn't think of the LangObject template argument as an issue, but actually it makes sense now.
Quote:
Originally Posted by dwhitney67
Consider the following small program where in lieu of your AssocArray, I use an STL map. I prompt the user to enter their name twice, the first time for the purpose of inserting an entry into the map, the second time to retrieve what I hope will be the same entry. If you run the program, you will see that it fails to find the entry even when using the "same" string twice.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.