Inlining methods : drawbacks - c++

I have a question concerning inlining methods.
I am using a library developed for collision models. One header file responsible for graphic interface contains declaration and implementation of the functions but the functions are not inlined. Therefore, it is impossible to include those functions in several translation units.
As an illustration here is a dummy code I designed for illustration :
LivingBeing.h
#ifndef LIVINGBEING_H
#define LIVINGBEING_H
class LivingBeing
{
public:
LivingBeing(double _size);
void breathe();
private:
double size;
};
//////////////
LivingBeing::LivingBeing(double _size)
{
size = _size;
}
void LivingBeing::breathe()
{
// do something
}
#endif
Forest.h
#ifndef FOREST_H
#define FOREST_H
#include "LivingBeing.h"
class Forest
{
public:
Forest(int _numberLivingBeings);
private:
int numberLivingBeings;
};
#endif
Forest.cpp
#include "Forest.h"
Forest::Forest(int _numberLivingBeings)
{
numberLivingBeings = _numberLivingBeings;
// Call LivingBeing constructor, methods etc...
}
Main.cpp
#include "Forest.h"
int main()
{
Forest forest = Forest(10);
return 0;
}
This code does not compile unless I add the inline keyword in front of the constructor LivingBeing and the method breathe. The error message is :
1>main_test.obj : error LNK2005: "public: __thiscall LivingBeing::LivingBeing(double)" (??0LivingBeing##QAE#N#Z) already defined in Forest.obj
1>main_test.obj : error LNK2005: "public: void __thiscall LivingBeing::breathe(void)" (?breathe#LivingBeing##QAEXXZ) already defined in Forest.obj
1>C:\Users\******\Documents\Visual Studio 2010\Projects\TutorialChronoEngine\Debug\Test_3.exe : fatal error LNK1169: one or more multiply defined symbols found
My question is : what is the drawbacks of inlining methods ? The real library I am using is pretty large, I would like to inline methods from a specific file (in my example it would be LivingBeing.h) so that it is possible to use those methods in several .cpp files. What am I risking by changing the source file as such ?
Thanks a lot

You are defining functions (LivingBeing::LivingBeing and LivingBeing::breathe) in a header, which means there will be a definition in each translation unit that includes that header. This breaks the One Definition Rule (ODR), hence the link error.
You have three options:
Move the function definitions into a source file so they are only defined once; or
Declare them inline to allow multiple identical definitions; or
Move the definitions inside the class definition, which implicitly makes them inline.
what is the drawbacks of inlining methods ?
Possibly increased compilation time and executable size; but you'd need to measure to see whether there's any noticable difference.
A less stable API - client code will need recompiling whenever any inline function changes.
The possibility of accidentally breaking the ODR, for example if a function contains macros which might have different expansions in different translation units.

Just because a method is defined in a "source" file (.c/.cpp) doesn't mean it won't be inlined... link-time operations may perform that optimization. Oppositely, just because a method is declared and implemented as inline in a header file doesn't mean it will be inlined. In general, I define methods in a header file if they are very simple, e.g. int getWidth() const { return width; }, or MyObj* getNext() { return internal_itor ? internal_itor->next : 0; }

Related

MFC projects cause "multiple definition" linker errors?

Is there something special about how an MFC project handles includes?
Here's the scenario. I like to define my class member functions in the h file, instead of splitting up a class between two files.
In Visual Studio I can create an empty Win32 project and do something like this:
main.cpp:
#include "doubleDef.h"
int main()
{
doubleDef t;
t.func();
return 0;
}
doubleDef.h:
#pragma once
class doubleDef
{
public:
int func();
};
int doubleDef::func()
{
return 4;
}
This builds just fine.
If I take doubleDef.h into an MFC dialog project, and add #include "doubleDef.h" to the h file of the main dialog, I get LNK2005, saying that func is already defined, making it seem as if the #pragma once is being ignored.
If I instead include doubleDef.h in the main dialog's cpp file, everything is fine. But in the empty Win32 I can include doubleDef.h "multiple times" by doing this:
Header.h
#pragma once
#include "doubleDef.h"
Header1.h
#pragma once
#include "doubleDef.h"
main.cpp:
#include "Header.h"
#include "Header1.h"
int main()
{
doubleDef t;
t.func();
return 0;
}
That is, it appears #pragma once works as expected (prevents multiple definitions of doubleDef::func()).
If I turn doubleDef into a template class, then the function definition must be in the h file. Likewise, I can make func inline, either by adding the keyword or implicitly by defining it next to the declaration in the class (as in int func() {return 4;}), and then, again the definition must be in the h file.
According to the documentation, the compiler treats inline as more or less optional, so it seems like if I just want to keep everything in the h file, I can just make everything inline.
What gives?
The #pragma once means the file will only be included once per source file. If you have many source files including it, you will still get a copy in each source file.
By declaring a function inline, you tell the compiler it's OK to have multiple copies - as long as those copies are identical.
The usual way of working is to have the declarations in the header file, and the definitions (implementation) in another source file.
P.S. MFC has nothing to do with your problems.
In your simple Win32 project you have one main file that keeps including the same item, basically a no-op. To have multiple of the same include in referenced in the same file does not create a new link.
However with your MFC project you put your header file into mainfrm.h. That file is included in several other files in that project not just the mainfrm.cpp. Essentially creating a new link for each of those other files the main header was included in.
1>MainFrm.obj : error LNK2005: "public: int __thiscall
doubleDef::func(void)" (?func#doubleDef##QAEHXZ) already defined in
MfcTest.obj 1>FileView.obj : error LNK2005: "public: int __thiscall
doubleDef::func(void)" (?func#doubleDef##QAEHXZ) already defined in
MfcTest.obj 1>ClassView.obj : error LNK2005: "public: int __thiscall
doubleDef::func(void)" (?func#doubleDef##QAEHXZ) already defined in
MfcTest.obj 1>OutputWnd.obj : error LNK2005: "public: int __thiscall
doubleDef::func(void)" (?func#doubleDef##QAEHXZ) already defined in
MfcTest.obj 1>PropertiesWnd.obj : error LNK2005: "public: int
__thiscall doubleDef::func(void)" (?func#doubleDef##QAEHXZ) already defined in MfcTest.obj
Take a look at that output. You can see each of the other components that thinks it has that object.
To put it another way your original statement of why does it work for one way and not the other is because the complexity of the second example (MFC) actually includes that header all over the place. If you only want it used by the main form then include it in the cpp for it.
This was already answered before, here is more detailed explanation:
You cannot define a function more than once, unless it is inline
You cannot declare a function more than once in the same file.
You do need to declare functions and classes multiple times if they are referenced in multiple .cpp files.
#pragma once prevents multiple declarations in the same file. It will not prevent multiple declarations in different files. Multiple declarations is exactly what you want and it is why you are including the files in multiple .cpp files in the first place.
class N1 {
public:
//Declaration
//Multiple .cpp files need to know about this class and its members
int foo();
};
//Definition
//should be done once. This should go to .cpp file
int N1::foo() {
return 1;
}
In the above example, compile will work fine in multiple .cpp file. The compile routine does not notice multiple definitions. But the linker notices multiple definitions and complains. You have to move the definition to .cpp file or use inline functions.
class N2
{
public:
int foo();
};
inline int N2::foo()
{ //valid, even if it is repeated in different files
return 1;
}
class N3
{
public:
int foo() //always valid
{
return 1;
}
};

Compilation error while including cpp file in main: Inline versus non-inline functions

I have a conceptual doubt which i'll try to put across using an example:
main.cpp
#include "array_list.cpp"
int main()
{
array_list list1;
return 0;
}
Scenario1:
array_list.cpp->
class array_list
{
private:
int list[10];
public:
array_list () {};
~array_list () {};
void set_element (int,int);
};
void array_list::set_element (int i,int a) {
list[i] = a;
}
Error:
main.obj : error LNK2005: "public: void __thiscall array_list::set_element(int,int)" (?set_element#array_list##QAEXHH#Z) already defined in array_list.obj
1>C:\Users\vrastog\Documents\Visual Studio 2012\Projects\C++ learning\Debug\list using arrays.exe : fatal error LNK1169: one or more multiply defined symbols found
Scenario 2:
array_list.cpp->
class array_list
{
private:
int list[10];
public:
array_list () {};
~array_list () {};
void set_element (int i,int a) {
list[i] = a;
}
};
Error: No error!!
Question: I understand the reason for error. The same method has been defined twice, once in main.obj and second in array_list.obj and hence, it should be an error.
My question is why does the second scenario work? Here also, since we have includes array_list.cpp in the main file, 'set_element' should have been defined twice here as well. What am I missing here?
Please don't include .cpp files.
In the first example, the function is defined out of class, you need to add inline, otherwise it's a multiple definition.
In the second example, the function is defined in the class definition, so it's an implicit inline function (it's like the compiler added the inline for you), that's why it's not causing multiple definitions.
In-class definition makes a method inline, and therefore it does not cause a multiple definition error in the object file.
An inline method should be implemented in every translation unit it is used in, so the inline method compiles into both object files.
C++ Standard Draft (n3797) 3.2.4: An inline function shall be defined in every translation unit in which it is odr-used.
Also 3.2.6 requires that these function should be exactly the same.
g++ implements this using weak symbols: inline functions are special exported functions that do not cause multiple definition error when linking.
Use a tool like nm under Linux and see for yourself. It emits a weak symbol into the object file:
$ nm arraylist.o
00000000 W _ZN10array_list11set_elementEii
(... ctors, dtors ...)
00000000 T main
Also, if you do not use the function, or the compiler inlines all occurrences, it may get optimized out.

When to use 'inline' keyword in class methods with header and object files?

I have been just tweaking around my game (putting class declaration in *.h files with their definition in the corresponding .cpp object files), but I don't quite understand when I have to use 'inline' keyword next to the method definitions and when I don't. Let me show you:
//shape.h
//header guards and includes in here
class shape
{
private:
char type;
int verticies[4][2];
int centre[2];
int radius;
public:
shape(int coordinates[4][2]);
shape(int coords[2], int r);
void Change(int coordinates[4][2]);
void Change(int coords[2], int r);
//more functions...
};
//shape.cpp
#include "shape.h"
inline shape::shape(int coordinates[4][2])//Constructor for a rectangle shape
{
for (int i=0; i<8; i++) //copy arguments to class
verticies[i/2][i%2]=coordinates[i/2][i%2];
//calculate centre of the rectangle
centre[0]=(coordinates[0][0]+coordinates[2][0])/2;
centre[1]=(coordinates[0][1]+coordinates[2][1])/2;
}
inline shape::shape(int coords[2], int r)//Constructor for a circle shape
{
type='C';
centre[0]=coords[0];
centre[1]=coords[1];
radius=r;
}
inline void shape::Change(int coordinates[4][2])//change coordinates of a rectangle shape
{
if (type=='C') return;//do nothing if a shape was a circle
for (int i=0; i<8; i++)
verticies[i/2][i%2]=coordinates[i/2][i%2];
centre[0]=(coordinates[0][0]+coordinates[2][0])/2;
centre[1]=(coordinates[0][1]+coordinates[2][1])/2;
if(verticies[0][0]-verticies[1][0]==0 || verticies[0][1]-verticies[1][1]==0) type='S'; else type='R';
}
inline void shape::Change(int coords[2], int r)//change coordinates for a circle shape
{
if (type=='R' || type=='S') return; //do nothing if a shape was not a circle
centre[0]=coords[0];
centre[1]=coords[1];
radius=r;
}
//and so on...
Not having an inline keyword would result in: "multiple definition of `shape::shape(int (*) [2])' " error. However on other occasions with other classes use of 'inline' was unneccesary. So my question is: when do I have to use 'inline' keyword and why is it important anyway?
Edit: So I have been informed that using inline in this situation is a bad Idea. Therefore what is a proper way to implement source and header files?
The inline keyword is important when you define a non-template function in a header file, but outside a class declaration. It avoids the multiple definition issue when a header is included in multiple places.
Other than that, it's not much use these days. It's technically still supposed to indicate that you want a function to use inline expansion -- i.e. instead of actually calling it (which presents a small overhead), the compiler just drops copies of the entire function body into whatever location it gets called from. (It's an invaluable optimisation for very small functions, such as accessor methods.) In reality though, compilers tend to figure out what should and shouldn't be inlined on their own, so it takes the keyword as little more than a hint.
In the version of the code you posted, inline key is completely unnecessary and actually harmful. In this version you have to remove all inline keywords from the code you posted to be able to properly compile and link it. What you posted will typically cause linker errors due to missing function definitions.
inline keyword becomes necessary if you move all your function definitions into the header file.
So, this is the simple rule you have to follow with inline keyword and member functions: If you define your member function in the header (.h) file, use inline keyword. If you define your member function in implementation (.cpp) file, don't use inline keyword.

LNK 2005 link errors for functions but not for class in Visual Studio 2010

I am now building a C++ DLL library. Today I have met a confusing problem: in this library I can define class but not functions. To be more specific, I give the following codes to illustrate my problem:
namespace fundamental
{
class Tree
{
public:
Tree() {};
~Tree() {};
int x;
};
/*int anyfunction()
{
return 1;
}*/
}
The above definition is in the header file, and this file will be invoked by other files. My problem is that if I commented the function part (int anyfunction()) everything was fine, but if I added this function, I would get the following errors:
page_analysis.obj : error LNK2005: "int __cdecl fundamental::anyfunction(void)" (?anyfunction#fundamental##YAHXZ) already defined in geo_box.obj
1>pa_region_properties.obj : error LNK2005: "int __cdecl fundamental::anyfunction(void)" (?anyfunction#fundamental##YAHXZ) already defined in geo_box.obj
My question is why I will get LNK2005 error only for functions but not for classes. Any ideas?
If you define something in a header file, then that definition will be duplicated in any translation unit (roughly speaking, every source file) that includes that header. Sometimes, multiple definitions are an error.
Classes can be defined in multiple translation units, as long as the definitions are identical; indeed, they must be defined in any translation unit that uses them.
Functions usually can't, but you can allow it by declaring it inline:
inline int anyfunction() {return 1;}
or you could move the definition to a single source file, and only declare it in the header:
// header
namespace fundamental {
int anyfunction();
}
// source file
int fundamental::anyfunction() {return 1;}
Most likely you have included that function via a header into different translation units (aka cpp-file). If you really need that function to be inlined, use "inline":
inline int anyfunction()
{
return 1;
}
HTH Torsten

gtest problem with inline function

hello i have which include inline function, when i try testing this class with google test, i have error like:
error LNK2019: unresolved external symbol "public: double __thiscall Math::returnPi(void)" (?returnPi#Math##QAENXZ) referenced in function "private: virtual void __thiscall Speed_Math_Test::TestBody(void)" (?TestBody#Speed_Math_Test##EAEXXZ)
for example my class(header file)
class Math
{
public:
Math(void);
inline double returnPi();
~Math(void);
};
my class(cpp file)
Math::Math(void)
{}
Math::~Math(void)
{}
double Math::returnPi()
{ return 3.14;}
test:
TEST(EQ, Math)
{
Math *m=new Math();
EXPECT_EQ(3.14,m->returnPi());
}
what i need to do? i read manual but dont see how i can resolved this error.
An inline function should be in your header file, not in your source file so it can actually be inlined by the callers (which don't have access to the source file).
Moreover, you don't need to specify inline in your class declaration if you give the definition of the function.
So your header should become:
class Math
{
public:
Math(void);
double returnPi() { return 3.14; } // no need to specify inline here
~Math(void);
};
And remove the definition for returnPi() from your source file.
Note that you could also have done:
class Math
{
public:
Math(void);
double returnPi();
~Math(void);
};
inline double Math::returnPi() { return 3.14; } // inline is mandatory here to avoid respecting the "One Definition Rule"
The second solution is good if you want to keep the class declaration separate from the function definition.
Also note that inline does not guarantees that the actual function calls will be inlined: the only thing it enforces is that you don't have to respect the "One Definition Rule": the inline function must have the same definition in all translation units.
Are you sure you are compiling the class' CPP file as part of the project? This should be fine.