I'm trying to move some code into a shared library (works fine when compiled stand-alone) but getting some issues with class inline functions. mingw/gcc v4.7.2.
Part of the problem appears to be because I prefer to define my inline functions outside the class declaration (it keeps the class declaration neater and easier to read). I always thought this was acceptable and equivalent to defining within the class declaration ... but that doesn't appear to always be the case. I've created a simple sample to demonstrate the problems. (Obviously the dllexport would normally be in a macro to switch between import/export.)
Header:
// Uncomment one at a time to see how it compiles with: -O2 -Winline
//#define INLINE_OPTION 1 // implicit - builds without inline warnings
#define INLINE_OPTION 2 // simple external inline - gives inline warnings
//#define INLINE_OPTION 3 // external forced inline - gives inline errors
class __attribute__((dllexport)) Dummy {
public:
Dummy() : m_int{0} {}
~Dummy() {}
#if INLINE_OPTION == 1
int get_int() const { return m_int; }
#else
int get_int() const;
#endif
int do_something();
private:
int m_int;
};
#if INLINE_OPTION == 2
inline int Dummy::get_int() const
{ return m_int; }
#endif
#if INLINE_OPTION == 3
inline __attribute__((always_inline)) int Dummy::get_int() const
{ return m_int; }
#endif
.cpp file:
int Dummy::do_something()
{
int i = get_int();
i *= 2;
return i;
}
As noted above, with INLINE_OPTION == 1 (implicit, in-class inline definition) the code compiles with out warning.
With INLINE_OPTION == 2 (out-of-class inline definition) I get this warning: int Dummy::get_int() const' can never be inlined because it uses attributes conflicting with inlining [-Winline]
With INLINE_OPTION == 3 (trying to force inline), I get the same warning as above, AND I get this error: error: inlining failed in call to always_inline 'int Dummy::get_int() const': function not inlinable
, with the information about it being called from the first line inside Dummy::do_something() in the .cpp file. Notice this is about trying to inline the function within the library itself! For simple accessor functions this could be very a very significant overhead.
Am I doing something wrong? Is it gcc right in treating the out-of-class-definition inline function differently to in-class function definitions? (Am I really forced to clutter the class declaration?)
Note: The problem doesn't just effect things that I declare inline. It also effects anything declared as constexpr and even destructors declared as "= default" when inheritance is involved.
Edit:
Just tried with mingw64 / gcc v4.8.0 with the same results. Note that this includes the fact that option 1 does NOT inline in do_something (I checked the assembler output), so apparently the only difference between option 1 and option 2 is that only option 2 will gives the -Winline warning.
Inline functions behave like macros. When an inline function gets called, instead of transferring the control to the function, the call gets substituted with the function code. Thus this saves time and improves performance.
Inline function is a function that is expanded in line when it is called. When the inline function is called whole code of the inline function gets inserted or substituted at the point of inline function call. This substitution is performed by the C++ compiler at compile time.
There are two kinds of memory usage that inline functions will affect: code size — in general, inlining code will increase how much memory is used to load your program. This is because there will be multiple copies of the generated code scattered around your program.
Inline function expansion can speed up execution by eliminating function call overhead. This is particularly beneficial for very small functions that are called frequently. Function inlining involves a tradeoff between execution speed and code size, because the code is duplicated at each function call site.
I don't know nothing about how to make shared libraries on Windows. In linux/OSX no special treatment is required in the source code, so that both shared (.so) and ordinary (.a) libraries can be made from the same sources without special treatment.
If you really do need a special attribute for symbols to be exported into shared libraries, then you may simply split the code, e.g.
namespace implementation_details {
class __attribute__((dllexport)) DummyBase
{
protected:
DummyBase() : m_int{0} {}
~DummyBase() {}
int do_something();
int m_int;
};
}
struct Dummy: private implementation_details::DummyBase
{
using implementation_details::DummyBase::do_something;
int get_int() const noexcept;
};
inline __attribute__((always_inline)) int Dummy::get_int() const noexcept
{ return m_int; }
Ok maybe my answer was a little cryptic... let me give you a quick example of what I mean using your code snippets.
dummy.h:
#ifndef _DUMMY_H_
#define _DUMMY_H_
class __attribute__((dllexport)) Dummy {
public:
Dummy() : m_int{0} {}
~Dummy() {}
int get_int() const;
int do_something();
private:
int m_int;
};
// here goes the include of the implementation header file
#include "dummy.h.impl"
#endif // _DUMMY_H_
dummy.h.impl:
// there will be no symbol for Dummy::get_int() in the dll.
// Only its contents are copied to the places where it
// is used. Placing this in the header gives other binaries
// you build with this lib the chance to do the same.
inline int Dummy::get_int() const
{ return m_int; }
Of course you could place the inline definitions just below your class declaration in the same header file. However, I find this still violates the separation of declaration and definition.
dummy.cpp:
// this method will become a symbol in the library because
// it is a C++ source file.
int Dummy::do_something()
{
// i would if i knew what to do...
return 0;
}
Hope I could be of help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With