I seem to be misunderstanding something related to build order dependencies in C++. So I have this code, which defines a class that I use as a functionoid (object whose purpose is to substitute for passing a function pointer):
#include "Imports.h"
class X: public Y
{
public:
X (T* t) { this->t= t; }
virtual ~X(){}
virtual void draw()
{
if (t->booleanReturningFunction())
{
t->someField.draw();
}
}
T* t;
};
I'm getting a compiler error that complains about the "use of undefined type T" at the line numbers where I'm using T. However, Imports.h looks like:
//The goal of this file is to have all the typcally needed imports in one place.
#if !defined(IMPORTS_H)
#define IMPORTS_H
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <assert.h>
#include <cmath>
#include <fstream>
class T;
class X;
class Y;
#include "Y.h"
#include "X.h"
#include "T.h"
#endif // if !defined(IMPORTS_H)
With each ".h" file containing a definition of that class. Now T actually does have an X object in it (not a pointer, but an X object). But, as far as I can tell, there's no circular dependency in the build order because X only has a pointer to T, right? Is there anything I'm missing that you can see just from this code? Help is much appreciated!
Edit: I solved my problem. The issue was that I was doing the above code inside a header file. The compiler understandably couldn't compile t->booleanReturningFunction() based on a forward reference (it needed to see the class declaration to know what address to bind the function call to).
Now T actually does have an X object in it (not a pointer, but an X object).
Given this order -
class T;
class X;
class Y;
#include "Y.h"
#include "T.h" // 1
#include "X.h"
T.h has a X object as you mentioned. Until this point (1) compiler doesn't know the definition of class X. For the object to instantiate compiler should see the full class definition just not the forward declaration of X. But it seems strange that the compiler is complaining about undefined type T.
I solved my problem. The issue was that I was doing the above code inside a header file. The compiler understandably couldn't compile t->booleanReturningFunction() based on a forward reference (it needed to see the class declaration to know what address to bind the function call to).
Related
For teaching purposes in my applied oriented object courses, we are asked to develop a fully featured C++ application without using the STL nor any string manipulation functions from cstring (SDL for GUI will be involved in a later stage).
While redeveloping simple String and list container hierarchy classes, I encountered a cyclical dependency issue. Previously, I fixed these kind of issues using forward declaration. However, this time, things do not go as expected and this problem kept me busy for a few evenings now.
Here is a simple UML diagram of the issue I have.
Every class has their own .cpp and .hpp files, except for BaseListItemNotFoundException I declare with a using statement above the BaseList class declaration.
class BaseListItemNotFoundException: BaseException {
using BaseException::BaseException;
};
Even if this doesn't add any added pieces of info (IMHO), let me precise BaseList and HeplList classes are actually template classes defined using .ipp and .hpp.
I omitted some other classes involved to restrict the environment to a minimal working example (iterator and Cell generic class hierarchy used as payload for the lists). Header protections using define and ifndef conditions have been removed for clarity.
Here are a snipped of the files:
BaseList.hpp:
#include <cstddef>
#include <iostream>
#include "Cell.hpp"
class HeplString; // Forward declaration
#include "BaseException.hpp"
class BaseListItemNotFoundException: BaseException {
using BaseException::BaseException;
};
template<class T>
class BaseList {
// code
};
HeplList.hpp:
#include <cstddef>
#include "BaseList.hpp"
#include "Cell.hpp"
template<class T>
class HeplList : public BaseList<T> {
// code
};
#include "HeplList.ipp"
HeplString.hpp:
#include <cstddef>
#include <iostream>
#include <ostream>
#include <fstream>
#include "HeplList.hpp"
class HeplString {
// code
};
BaseException.hpp:
#include "HeplString.hpp"
#include "BaseList.hpp"
class BaseException {
// code
};
The main issue I have with this example is errors like this one:
src/tests/../BaseException.hpp:9:20: error: field ‘msg’ has incomplete type ‘HeplString’
HeplString msg;
^~~
In file included from src/tests/../HeplList.hpp:5,
from src/tests/../HeplString.hpp:9,
from src/tests/test.cpp:2:
src/tests/../BaseList.hpp:9:7: note: forward declaration of ‘class HeplString’
class HeplString;
^~~~~~~~~~
I don't understand what I'm doing wrong here. Reading other similar issues didn't help.
My git repository with the full code is available here, if needed: https://github.com/wget/hepl-2-cpp
Add #include "BaseException.hpp" to BaseList.hpp
Add #include "HeplList.hpp" to HeplString.cpp
Add the forward declaration template<class T> class HeplList; to HeplString.hpp
Now, you may need to modify some of your other classes which weren't including the BaseList.hpp header because they were relying on the header HeplString.hpp to do this for them.
I want to avoid this case:
h1.hpp:
#include <vector>
//some code
main.cpp:
#include "h1.hpp"
//#include <vector> This include is commented.
int main(){
std::vector<int> x; //works fine!
}
I want to get an error/warning that I am using <vector> while it is not directly included in MSVS.
If <vector> is used as an implementation detail (i.e., function body), separate h1.hpp to h1_head.hpp (declarations) and h1_body.hpp (definitions). If you have structs / classes, you can hide the details by e.g. declaring a detail struct / class inside your class and only defining it in h1_body.hpp . Include h1_head.hpp before the code in main.cpp and h2_body.hpp after all the codes.
If any of the fuctions in h1.hpp take / return a vector or a related type, then maybe it's best to assume that main.cpp will have <vector> when including h1. An alternative to this is to say that h1 requires a vector class and thus not include <vector> in h1 but require it to be included in main.cpp (before h1) - this is, however very confusing as there's no programmatic way of documenting it (no, requiring a typedef std::vector vector_t; or a #define H1VECTOR std::vector is not documenting, it's false generalization).
I'm having this issue where I cannot call the object constructor in main.cpp even after it has been included in main.h. The error message is:
C:\Users\Espresso\Projects\AZRA\Debug/../src/main.cpp:7: undefined reference to `g_editor::LevelEditor::LevelEditor()'
Where main.cpp contains
#include "main.h"
g_editor::LevelEditor g_levelEditor;
and main.h contains:
#include "g_editor/g_editor.h"
g_editor.h contains all of the header files of the objects in the library, which includes the levelEditor. g_editor.h:
#ifndef G_EDITOR_H_
#define G_EDITOR_H_
#pragma once
#include "g_editor/Objects/editor_module.h"
#include "g_editor/Objects/utility_window.h"
#include "g_editor/Objects/prompt_window.h"
#include "g_editor/LevelEditor/LevelEditor.h"
extern g_editor::LevelEditor g_levelEditor;
#endif
And finally, LevelEditor.h contains the constructor and member functions of LevelEditor:
#ifndef G_LEVEL_EDITOR_H_
#define G_LEVEL_EDITOR_H_
#pragma once
#include "../Objects/editor_module.h"
#include "Modules/collisionGrid_module.h"
#include "Modules/HUD_module.h"
#include "Modules/IO_module.h"
#include "Modules/ledge_module.h"
#include "Modules/segment_module.h"
#include "g_level/g_level.h"
using namespace g_level;
namespace g_editor
{
class LevelEditor
{
private:
std::vector<editor_module*> modules;
void loadModules();
public:
static LevelEditor& get()
{
static LevelEditor sSingleton;
return sSingleton;
}
LevelEditor();
~LevelEditor() {};
I apologize for the wall of text, I've been staring at this for a few days now and I have tried reordering the static libraries by precedence (which eliminated all issues save for this one.) Is there a design flaw in my current setup? I am using sSingletons, global externs, and static libraries.
There is no definition of LevelEditor::LevelEditor.
You are either missing a source file, or you forgot to add {}.
Edit: or, if your constructor does not do anything anyway, just remove the declaration.
Either
1) This function is missing:
LevelEditor(); // So now what does this do???? That's what is missing.
or
2) it isn't missing, but you didn't add the source module or library where this function is located to your linker settings.
#ifndef _MY_OPENCLPLATFORM_
#define _MY_OPENCLPLATFORM_
#include "OpenCL.h"
namespace my
{
class OpenCLPlatform
{
cl_platform_id mplatformID;
cl_uint mnumDevices;
std::vector<OpenCLDevice> mdevices; // OpenCLDevice was not declared in this scope
public:
OpenCLPlatform(cl_platform_id platformID);
void getDevices();
void printInfo();
cl_platform_id& getPlatformID();
};
}
#endif
#ifndef _MY_OPENCLDEVICE_
#define _MY_OPENCLDEVICE_
#include "OpenCL.h"
namespace my
{
class OpenCLDevice
{
cl_device_id mdeviceID;
public:
OpenCLDevice(cl_device_id device);
void printInfo();
void printDeviceType(cl_device_type deviceType);
};
}
#endif
#ifndef _MY_OPENCL_
#define _MY_OPENCL_
#if defined(__APPLE__) || defined(MACOSX)
#include <OpenCL/opencl.h> // This works only for XCODE compiler
#else
#include <CL/cl.h>
#endif
#include <cassert>
#include <iostream>
#include <vector>
#include "Exception.h"
#include "OpenCLDevice.h"
#include "OpenCLPlatform.h"
namespace my {
class OpenCLDevice;
class OpenCLPlatform;
class OpenCL;
class OpenCL
{
cl_uint mnumPlatforms;
std::vector<OpenCLPlatform> mplatforms;
void getPlatforms();
public:
OpenCL();
~OpenCL();
void quickSetup();
void printPlatformVersions();
};
}
#endif
Does the the ordering "class OpenCLDevice; class OpenCLPlatform; class OpenCL;" matter? Sometimes, header files depend on each other which can lead to "hard to follow" or convoluted inclusions...Do you have a "one way" technique to deal with convoluted inclusions that you use all the time?
Edit:
I changed the code to match my real problem. If you look at the code above, the compiler is saying that 'OpenCLDevice was not declared in this scope'.
Edit:
I finally got the code to work, and this is what I did:
1. add #include "OpenCLDevice.h"in OpenCLPlatform.h
2. compile
3. remove #include "OpenCLDevice.h"in OpenCLPlatform.h
4. compile
It works now!
Edit:
I cleaned the project and removed all dependencies, and I'm getting the same errors again.
Edit:
I think compiler did something to the code. It may have chose to not include libraries that aren't used in the header and source file, but are used in other headers and source codes
Since you are including classa.h and classb.h where both classes are (presumably) defined, you shouldn't even need the forward declaration.
However, if you did not include them, then no, order of the declarations wouldn't matter. As long as as a class is forward declared before it is used you should be OK.
I see two potential issues:
Your #include "OpenCL.h" may not include the file you expect (yours), but instead some system file.
Forward declarations can't be used in your case. It works only when you have pointers or references to class instances. Your vector<OpenCLPlatform> requires the class declaration (i.e. inclusion of the corresponding header).
i have received in .cpp errors of invalid use of undefined struct PelephonePN, CellcomPN and so on
and also error in .h errors of forward declaration of PelephonePN,...
#ifndef PHONE_H_INCLUDED
#define PHONE_H_INCLUDED
#include <iostream>
#include <ctime>
#include <string>
#include "phone.h"
using namespace std;
class PhoneNumber;
class PelephonePN;
class CellcomPN;
class OrangePN;
class HotPN;
class BezeqPN;
class PhoneManager
{
private:
PelephonePN* mpPelephone;
CellcomPN* mpCellcom;
OrangePN* mpOrange;
HotPN* mpHot;
BezeqPN* mpBezeq;
public:
PhoneManager();
~PhoneManager();
void split_check_data(string str);
};
#endif
and .cpp
#include <iostream>
#include <ctime>
#include <string>
#include <sstream>
#include "phone_manager.h"
#include "phone.h"
using namespace std;
PhoneManager::PhoneManager()
{
srand(time(0));
mpPelephone = new PelephonePN();
mpCellcom = new CellcomPN();
mpOrange = new OrangePN();
mpHot = new HotPN();
mpBezeq = new BezeqPN();
mpPelephone->add(mpCellcom);
mpPelephone->add(mpOrange);
mpPelephone->add(mpHot);
mpPelephone->add(mpBezeq);
mpBezeq->setNext(mpPelephone);
}
To instantiate an object, forward declaration is just insufficient. Include the corresponding headers in the source file. In the body of the constructor, you are instantiating mpPelephone, ..... So, make sure that corresponding class headers can be seen in the translation unit.
In your .cpp source file you need to #include the headers that define class PelephonePN and its associates.
It is fine to forward-declare these classes in the header if you are only using them as pointers or references, but one you start using them in your implementation, you'll need to provide the compiler with the definition.
You can't use a forward declaration of a class to access its members (which includes the constructor, default or otherwise) or its size and without those two things, you can't instantiate an instance of that class.
You require its FULL implementation to do that, so in your .cpp file you need to include the header with the whole class PelephonePN {/* class body */}; section.
in the first file you have lines:
#ifndef PHONE_H_INCLUDED
#define PHONE_H_INCLUDED
I suppose you have same lines in phone.h, please check...
Are your PelephonePN and co. in a particular namespace? If so, predeclarations must all be in the same namespace. Since you added a using namespace std (sigh), my guess is that phone.h is defining your classes inside the namespace std (I hope it doesn't, but that's another thing).
If so, your predeclarations must be:
namespace std {
class PelephonePN;
}
Btw, what is a pelephone??? O.O