C++ Making a Member Function a Friend With Seperate Compilation - c++

My question is very similar to a previously answered question except I'm trying to rewrite the same program using seperate compilation in order to practice proper C++ coding style. I've simplified the program to highlite the problamatic areas.
Currently this program runs fine if I declare the entire class as a friend, but this feels inherently dangerous, giving the entire class friend access.
File Foo.h
#ifndef FOO_H
#define FOO_H
class Foo{
friend class Bar;
public:
// getX defined in header for simplicity.
const int &getX() const{
return x;
}
private:
int x = 0;
};
#endif
File Bar.h
#ifndef BAR_H
#define BAR_H
#include "Foo.h"
class Bar{
public:
void mutateX(Foo &foo);
};
#endif
File Bar.cpp
#include "Bar.h"
void Bar::mutateX(Foo &foo){
foo.x = 1;
}
I can't wrap my head around how to just make the mutateX member as a friend. Any help will be greatly appreciated!
Here's a tester class main.cpp
#include "Foo.h"
#include "Bar.h"
#include <iostream>
int main(){
Foo foo;
Bar bar;
std::cout << foo.getX() << std::endl;
bar.mutateX(foo);
std::cout << foo.getX() << std::endl;
return 0;
}

Related

Splitting coupled classes into separate headers

I'm trying to separate the following two classes so each class can be defined in its own Header:
#include <iostream>
class Boo;
class Foo
{
public:
Foo(Boo *booPtr)
:booPtr(booPtr){};
virtual ~Foo(){};
Boo *booPtr;
};
class Boo
{
public:
Boo()
:foo(this){};
virtual ~Boo(){};
Foo foo;
int num = 32;
};
int main()
{
Boo *boo = new Boo;
std::cout << "booPtr : " << boo->foo.booPtr->num << '\n';
delete boo;
}
The Result:
booPtr : 32
Program ended with exit code: 0
And here is my failed attempt at separation:
"Foo.hpp"
#ifndef Foo_hpp
#define Foo_hpp
#include <stdio.h>
#include "Boo.hpp"
class Foo
{
public:
Foo(Boo *booPtr)
:booPtr(booPtr){};
virtual ~Foo(){};
Boo *booPtr;
};
#endif /* Foo_hpp */
"Boo.hpp"
#ifndef Boo_hpp
#define Boo_hpp
#include <stdio.h>
#include "Foo.hpp"
class Boo
{
public:
Boo()
:foo(this){};
virtual ~Boo(){};
Foo foo; // Error : Field has incomplete type 'Boo'
int num = 32;
};
#endif /* Boo_hpp */
"main.cpp"
#include <iostream>
#include "Foo.hpp"
#include "Boo.hpp"
int main()
{
Boo *boo = new Boo;
std::cout << "booPtr : " << boo->foo.booPtr->num << '\n';
delete boo;
}
But I can't build the code since it generate's the following error:
Boo.hpp -> Foo foo; -> "Field has incomplete type 'Boo'"
How can I fix my code?
You have one file that includes the other and vice versa. This creates a cycle of inclusion.
Realize that the #include macro does nothing but to replace itself (that is, it's line) with the other file. This makes it obvious why you can't have file A include file B.
The obvious solution is to put a forward declaration of Boo in Foo:
#ifndef Foo_hpp
#define Foo_hpp
#include <stdio.h>
class Boo;
class Foo
{
public:
...
Strangely, you already did this before you separated them.
Now a little bit more theory for you: A class is technically a data storage. It needs to know it's size in order to reserve memory. Therefore, it needs to know all the sizes of it's members, which it can only know when they are declared. Therefore, a class needs to include the declaring headers of each class it has as a member. However, a pointer to an object is different (same goes for references). A pointer always takes the same size, that is 32 or 64 bit, depending on your platform (probably 64 bit since we have 64 bit platforms nowadays). Therefore, the class does not need to know the class it points at, the memory it reserves for it's pointer member is always of the same size. That's why a forward declaration, that says nothing about a classes size, is fine here.
While circular inclusion by itself, proper include-guards assumed, is not an error, remember it's just textual replacement:
Make sure all the permutations are viable and have the same meaning!
In your case, replace the inclusion of "Boo.hpp" with the forward-declaration, which you used for a reason in the merged source.
Or, you know, simply abandon splitting the header:
Remember that classes are not necessarily the proper unit of organization.
Addendum on circular includes, starting with Foo.hpp:
#ifndef FOO
#define FOO // (!)
// now including BOO!
#ifndef BOO
#define BOO
// now including FOO a g a i n
// as FOO i s already defined, all that remains is:
#ifndef FOO
#endif
class Boo { }; // now lacking definition of Foo...
#endif // BOO
class Foo { };
#endif // FOO (first one)
Analogously for Boo.hpp (you'd only need a pre-declaration of Foo, but there is none as the definition again follows afterwards...).
As you see, circular includes, appropriate include guards provided, won't result in endless self inclusion. However, the outcome is not the one you intended.

Why is this error so unclear?

I have the following code:
foo.h
#ifndef FOO_H
#define FOO_H
#include "bar.h"
class Foo
{
public:
Foo(Bar bar);
};
#endif //FOO_H
bar.h
#ifndef BAR_H
#define BAR_H
#include "foo.h"
class Bar
{
public:
Bar(Foo foo);
};
#endif //BAR_H
If I compile that, I get the following error message:
expected ')' before 'foo' bar.h line 9
After looking on this website, I fixed it by using a forward declaration of Foo in bar.h, and Bar in foo.h.
My question is, why does the compiler make this error sound like a syntax error, whilst it's actually not ? I would think that catching such an error and return a proper error message would be quite simple.
You have headers with unresolved circular dependency. That is when your code somewhere includes "foo.h" first then after preprocessing it will become
class Bar // expanded from #include "bar.h"
{
public:
Bar(Foo foo); // Foo is not declared at this point
};
class Foo // rest of foo.h content
{
public:
Foo(Bar bar);
};
if your code includes "bar.h" first then after preprocessing it will become
class Foo // expanded from #include "foo.h"
{
public:
Foo(Bar bar); // Bar is not declared at this point
};
class Bar // rest of bar.h content
{
public:
Bar(Foo foo);
};
So there is an error in both cases.
To get around this issue you need to utilize proper forward declarations:
// foo.fwd.h
#ifndef FOO_FWD_H
#define FOO_FWD_H
class Foo;
#endif // FOO_FWD_H
// bar.fwd.h
#ifndef BAR_FWD_H
#define BAR_FWD_H
class Bar;
#endif // BAR_FWD_H
and include them into headers instead of header with complete class declaration:
// foo.h
#ifndef FOO_H
#define FOO_H
#include "bar.fwd.h"
class Foo
{
public:
Foo(Bar bar);
};
#endif //FOO_H
// bar.h
#ifndef BAR_H
#define BAR_H
#include "foo.fwd.h"
class Bar
{
public:
Bar(Foo foo);
};
#endif //BAR_H
and then include headers with class definition only into .cpp or implementation file.
C++ is very hard to parse. When compiler does not know that Foo is name of some type then it expects that we try to declare members with that name in Bar. Code does not parse as any of valid variants of member declarations.
Old compilers just diagnosed such cases as "syntax error". Modern compilers try to be friendlier. The diagnostic likely tries to help us to correct the code towards one of such (or some other similar) valid member declaration.
class Bar
{
public:
Bar (Foo());
Bar (*Moo);
Bar Roo();
};
Unfortunately it guessed totally wrongly since Foo was not meant as member name but as a type of parameter of constructor.

Troubles separating C++ source file into multiple files

Let me specify exactly what I'm trying to do, I need to split my foo-bar program into five separate files: main, foo.h, foo.cpp, bar.h, bar.cpp. My header files (foo.h and bar.h) are meant to contain the declarations for their corresponding classes, while the c++ files (foo.cpp and bar.cpp) are meant to define the class.
I'm using Visual Studio, and thus far the only file I have showing red flags is my main file. Here is my code thus far, and I will include the errors that are being thrown in my main file:
main.cpp
#include <iostream>
#include "foo.h"
#include "foo.cpp"
#include "bar.h"
#include "bar.cpp"
using namespace std;
int main() {
Bar b(25); /*I am getting a red flag under the 25, stating there is no constructor that can convert int to Bar*/
b.func1(); /*I'm getting a red flag under func1 and func2 stating neither of them are members of Bar*/
b.func2(34);
return 0;}
foo.h
#ifndef foo_h
#define foo_h
#include "foo.cpp"
class Foo {};
#endif
foo.cpp
#ifndef foo_c
#define foo_c
#include "foo.h"
#include "bar.cpp"
private:
int data;
public:
Foo(int d) : data(d) {}
int get_data() { return data; }
virtual void func1() = 0;
virtual int func2(int d) = 0;
#endif
bar.h
#ifndef bar_h
#define bar_h
#include "bar.cpp"
#include "foo.h"
class Bar : public Foo {};
#endif
bar.cpp
#ifndef bar_c
#define bar_c
#include "bar.h"
#include "foo.h"
#include "foo.cpp"
Bar(int d) : Foo(d) {}
void func1() {
cout << "Inside func1\n";
cout << "\tData is " << get_data() << endl;
}
int func2(int d) {
cout << "Inside func2 with " << d << endl;
cout << "\tData is " << get_data() << endl;
return d;
}
#endif
My program worked until I split it up, but now it keeps throwing this message at me when I try to compile it, and there are a couple of red flags in my main code. This is what the console tells me:
No suitable constructor exists to convert int to Bar
func1 is not a member of class Bar
func2 is not a member of class Bar
My question is: What am I doing wrong, and is there a better way to go about what I'm trying to do?
Thank you in advance.
There is more than one misconception manifested in this code. Perhaps it is easier to correct them altogether than to describe and discuss them individually.
Let us start from the bottom of the dependency tree. There at the bottom is a virtual class Foo. Here is its correct declaration.
#ifndef foo_h
#define foo_h
class Foo {
private:
int data;
public:
Foo(int);
int get_data();
virtual void func1() = 0;
virtual int func2(int) = 0;
};
#endif
Note that we include the declarations of all its methods in the header file. However the implementation of the nonvirtual methods is moved out into the foo.cpp file.
#include "foo.h"
Foo::Foo(int d) : data(d) { }
int Foo::get_data() { return data; }
Note that we do not need any special devices to protect from multiple inclusion of the .cpp file because we are not going to include it ever.
Now Foo is the parent of the class Bar that does some real work for us. Once again, all its methods are declared within the class declaration.
#ifndef bar_h
#define bar_h
#include "foo.h"
class Bar : public Foo {
public:
Bar(int);
void func1();
int func2(int);
};
#endif
And its implementation is in the corresponding compilation unit called bar.cpp. When implementing a class method we indicate which class the method belongs to by prepending the class name to the method name, e.g. Bar::func1.
#include "bar.h"
#include "foo.h"
#include <iostream>
Bar::Bar(int d) : Foo(d) {};
using namespace std;
void Bar::func1() {
cout << "Inside func1\n";
cout << "\tData is " << get_data() << endl;
}
int Bar::func2(int d) {
cout << "Inside func2 with " << d << endl;
cout << "\tData is " << get_data() << endl;
return d;
}
Finally we use it in the main.cpp where only a small change is required.
#include <iostream>
#include "foo.h"
#include "bar.h"
using namespace std;
int main() {
Bar b(25);
b.func1();
b.func2(34);
return 0;}
Let's now proceed with building our project. If you were using GCC that would've been easy to describe as a sequence of CLI commands. Since you are using Visual Studio, you would have to perform the corresponding actions through the GUI.
First compile Foo
g++ -c -Wall foo.cpp
Next compile Bar
g++ -c -Wall bar.cpp
Compile main
g++ -c -Wall main.cpp
Now link it all together
g++ -o main foo.o bar.o main.o
Finally run it and voila
Inside func1
Data is 25
Inside func2 with 34
Data is 25
You should never #include .cpp files. Instead, compile each .cpp file into an object file and link them into an executable.
During the preprocessor stage, the compiler takes all of the #included files and treats them as if they were concatenated into one large program. Sometimes, certain files may be #included multiple times. Declarations, in header files, may be repeated, but multiple definitions, from source files, cause errors. (You probably don't have this problem because you used include guards in your source files.)
When creating object files, header files are used by the compiler to check names and types, but the actual definitions are not needed. The definitions that are found are compiled into the object file. The purpose of separate object files is to separate compilation of these definitions into modular units.

Multiple definition of variable

Here's my code:
main.cpp
#include "foo.h"
int main()
{
return 0;
}
foo.h
#ifndef FOO_H
#define FOO_H
class Foo
{
public:
Foo();
int bar;
}
#endif
foo.cpp
#include "foo.h"
Foo::Foo()
{
bar = 3;
}
Compiling this gives me the following error:
multiple definition of 'bar'
But I have include guards around the header file which defines bar, therefore how can it be defined more than once?
It's down to the missing semicolon at the end of the class declaration foo in foo.h.
That's confusing the compiler (it seems to be attempting to parse your constructor definition as the name of an object of type foo).
C++ ain't Java you know!

functions that intake objects belonging to classes in other files

Why if I have
in foo.h:
class Foo
{
}
void Bar(const Foo& foo);
it works but:
in foo.h:
class Foo
{
}
in bar.cpp
#include "foo.h"
void Bar(const Foo& foo);
doesn't work (unknown type name 'Foo' is its exact words)?
I don't know what about my question isn't specific and forward declarations don't work they just create a error 'duplicate symbol' so im just going to post the code im working with
in creatures.h
#ifndef CREATURES_H_
#define CREATURES_H_
#include <string>
#include "textio.hpp"
class Creature {
private:
protected:
int statBlock[10];
public:
std::string name = "foo";
Creature ();
void ai(int);
};
class Dwarf : public Creature {
private:
public:
std::string name = "Dwarf";
Dwarf (int);
void defaultDwarfGen();
};
main.cpp
#endif
#include "creatures.hpp"
#include "textio.hpp"
#include <iostream>
int main(int argc, char const *argv[]) {
Dwarf creature_1(0);
return 0;
}
textio.hpp:
#ifndef TEXTIO_H
#define TEXTIO_H
#include <iostream>
#include "creatures.hpp"
void challenge(const Creature& param);
#endif
Your problem is that you are including textio.hpp in creatures.hpp so first time that compiler see function void challenge(const Creature& param)Creature class isn't defined.
When you include createures.hppin textio.hpp CREATURES_H_ is already defined and bypass inclusion)
You can fix it deleting this include or declaring a forward definition for Creature class
In order to answer this question properly, you must provide foo.h, foo.cpp, bar.h, and bar.cpp
In short:
To make use of Bar in foo.h, foo.h must have the declaration for Bar.
To make use of Foo in bar.h, bar.h must have the declaration for Foo.
To make use of Bar in foo.cpp, foo.h or foo.cpp must have the declaration for Bar.
To make use of Foo in bar.cpp, bar.h or bar.cpp must have the declaration for Foo.
When I say, "must have declaration for", you can #include the appropriate header.
If you are trying to use Foo in Bar and Bar in Foo, then you've got a circular reference. The way we overcome this is with a forward declaration.
You can read about forward declarations here: https://isocpp.org/wiki/faq/misc-technical-issues#forward-decl