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.
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.
The edit I did on another post didn't seem to take, and anyway it seems some additional clarity may be appropriate, so I am posting details I've sent to another forum. In the code below class C is the work around to this problem - export only the non-inline members, not the whole class. As noted in the comments elsewhere, __declspec(dllexport) and __attribute__((dllexport)) are equivalent.
test.hpp
class __declspec(dllexport) A {
public:
int fa() { return m; }
int ga();
private:
int m{0};
};
class __declspec(dllexport) B {
public:
int fb();
int gb();
private:
int m{0};
};
inline int B::fb() { return m; }
class C {
public:
int fc() { return m; }
__declspec(dllexport) int gc();
private:
int m{0};
};
test.cpp
#include "test.hpp"
int A::ga() { return (fa() + 1); }
int B::gb() { return (fb() + 1); }
int C::gc() { return (fc() + 1); }
If you compile this with options: -std=c++11 -O2 -S -Winline (using mingw/ming64 with gcc v4.7.2 or v4.8.0) you can see the assembler produced for the library functions ga, gb and gc look like this:
ga:
subq $40, %rsp
.seh_stackalloc 40
.seh_endprologue
call _ZN1A2faEv
addl $1, %eax
addq $40, %rsp
ret
gb:
subq $40, %rsp
.seh_stackalloc 40
.seh_endprologue
call _ZN1B2fbEv
addl $1, %eax
addq $40, %rsp
ret
gc:
.seh_endprologue
movl (%rcx), %eax
addl $1, %eax
ret
and you get the warnings:
warning: function 'int B::fb()' can never be inlined because it uses attributes conflicting with inlining [-Winline]
warning: inlining failed in call to 'int B::fb()': function not inlinable [-Winline] (called from B::gb())
Notice that there were no warnings about fa not inlining (which is, I think, expected). But also notice that ga, gb and gc are all library functions. Whatever you may think about whether the inline functions themselves should be exported, there is no good reason why the inlines cannot be inlined inside the library. Hence I consider this a bug in the compiler.
Take a look around at well regarded code and see how much you find that exports only explicit members. For example those few parts of boost that get compiled into a library (eg: regex), use the class A technique, which means the many accessor functions are not being inlined inside the library.
But, all that aside, the answer for now is the class C technique (obviously in real code this has to be enclosed in a macro to switch between export and import as you would normally at the class level).
This is not a compiler bug, as some suggested. In C++ if function is inline, it has to be declared inline in every declaration. There are 5 properties that have to be met and one of them is:
An inline function with external linkage (e.g. not declared static) has the following additional properties:
1) It must be declared inline in every translation unit.
...
In your example, you first declared function Dummy::get_int() as non-inline inside class definition. It means that function cannot be redeclared as inline
Source: http://en.cppreference.com/w/cpp/language/inline
BTW: inline specifier works differently in C, where you can declare both inline and non-inline versions of the same function. Still, you have to implement both and ensure that they do the same thing.
Why don't you declare your function inline in class declaration (inline int get_int() const;)? Maybe error is there?
The compiler cannot inline a function which has to be exported in a dll. After all when called from executable linked with your dll the function should have an address. Most probably the call from do_something will be inlined but in the general case i think it's just impossible
Related
I have the following piece of code:
#include <stdio.h>
typedef struct {
bool some_var;
} model_t;
const model_t model = {
true
};
void bla(const model_t *m) {
if (m->some_var) {
printf("Some var is true!\n");
}
else {
printf("Some var is false!\n");
}
}
int main() {
bla(&model);
}
I'd imagine that the compiler has all the information required to eliminate the else clause in the bla() function. The only code path that calls the function comes from main, and it takes in const model_t, so it should be able to figure out that that code path is not being used. However:
With GCC 12.2 we see that the second part is linked in.
If I inline the function this goes away though:
What am I missing here? And is there some way I can make the compiler do some smarter work? This happens in both C and C++ with -O3 and -Os.
The compiler does eliminate the else path in the inlined function in main. You're confusing the global function that is not called anyway and will be discarded by the linker eventually.
If you use the -fwhole-program flag to let the compiler know that no other file is going to be linked, that unused segment is discarded:
[See online]
Additionally, you use static or inline keywords to achieve something similar.
The compiler cannot optimize the else path away as the object file might be linked against any other code. This would be different if the function would be static or you use whole program optimization.
The only code path that calls the function comes from main
GCC can't know that unless you tell it so with -fwhole-program or maybe -flto (link-time optimization). Otherwise it has to assume that some static constructor in another compilation unit could call it. (Including possibly in a shared library, but another .cpp that you link with could do it.) e.g.
// another .cpp
typedef struct { bool some_var; } model_t;
void bla(const model_t *m); // declare the things from the other .cpp
int foo() {
model_t model = {false};
bla(&model);
return 1;
}
int some_global = foo(); // C++ only: non-constant static initializer.
Example on Godbolt with these lines in the same compilation unit as main, showing that it outputs both Some var is false! and then Some var is true!, without having changed the code for main.
ISO C doesn't have easy ways to get init code executed, but GNU C (and GCC specifically) have ways to get code run at startup, not called by main. This works even for shared libraries.
With -fwhole-program, the appropriate optimization would be simply not emitting a definition for it at all, as it's already inlined into the call-site in main. Like with inline (In C++, a promise that any other caller in another compilation unit can see its own definition of the function) or static (private to this compilation unit).
Inside main, it has optimized away the branch after constant propagation. If you ran the program, no branch would actually execute; nothing calls the stand-alone definition of the function.
The stand-alone definition of the function doesn't know that the only possible value for m is &model. If you put that inside the function, then it could optimize like you're expecting.
Only -fPIC would force the compiler to consider the possibility of symbol-interposition so the definition of const model_t model isn't the one that is in effect after (dynamic) linking. But you're compiling code for an executable not a library. (You can disable symbol-interposition for a global variable by giving it "hidden" visibility, __attribute__((visibility("hidden"))), or use -fvisibility=hidden to make that the default).
I compiled the following code with VS2017 version 15.8.3. Its Warning Level is set to /W4. The code contains two simple getters, one of them GetM() is inline.
The GetM() inline getter does not have a return statement. However, VS2017 happily compiled the code without any warning or error.
The GetN() method will result in error C4716: 'Simple::GetN': must return a value if its return n; statement is commented out.
class Simple
{
int m = 0;
int n = 0;
public:
int GetM() const { /* No return here. */ }
int GetN() const;
};
int Simple::GetN() const
{
return n;
// No return here results in compiler error below.
// error C4716: 'Simple::GetN': must return a value
}
int main()
{
Simple obj;
}
Question: Should the compiler also generate error C4716 for the inline method GetM()?
A method fully defined inside the class definition is sort-of inlined. If it isn't expanded directly inline where it's used, it is compiled outside of the class body. This is the magic sauce that allows a method to see any members that were defined after it in the class.
If it is not used, maybe the compiler doesn't look at it deeply enough to spot the mistake. Maybe it doesn't look at it at all. Maybe it generates a warning and maybe not. That's up to the compiler. Visual Studio seems to have elected report a missing return statement as an error, but to not inspect an unused inline (or sort-of inlined) function.
By changing main to
int main()
{
Simple obj;
obj.GetM();
}
I can make Visual Studio produce error C4716 for gGetM as the function now must be compiled, inlined or not.
I can also
inline int Simple::GetN() const
{
}
to explicitly make GetN inline and "eliminate" the error.
This is all highly compiler, and possibly even compiler option, specific.
I have a program that dynamically links against a library.
The program passes a function pointer to that library, to execute.
But the ubsan (Undefined Behavior Sanitizer) specified that the pointer is on an incorrect function type. And that occurs only
if the callback function has a class as parameter
if the callback function has a class as parameter, but only forward declared
if I specify the compilation flags: -fvisibility=hidden.
I use clang to compile my project.
Is it a bug in clang undefined behavior sanitizer?
The following code is reduced to a simple test case. Check the comments to see where we can act to remove some warnings
The code of the application:
Main.cxx
#include "Caller.h"
#include "Param.h"
static void FctVoid()
{
}
static void FctInt(int _param)
{
static_cast<void>(&_param);
}
static void FctCaller(Caller &_caller)
{
static_cast<void>(&_caller);
}
static void FctParam(Param const &_param)
{
static_cast<void>(&_param);
}
int main()
{
Param param;
Caller::CallVoid(&FctVoid);
Caller::CallInt(&FctInt);
Caller::CallThis(&FctCaller);
Caller::CallParam(&FctParam, param);
return 0;
}
The code of the library's files are:
Caller.cxx:
#include "Caller.h"
// To uncomment to fix one warning
//#include "Param.h"
void Caller::CallVoid(FctVoidT _fct)
{
_fct();
}
void Caller::CallInt(FctIntT _fct)
{
_fct(32);
}
void Caller::CallThis(FctThisT _fct)
{
Caller caller;
_fct(caller);
}
void Caller::CallParam(FctParamT const &_fct, Param const &_param)
{
_fct(_param);
}
Caller.h
#ifndef __Caller_h_
#define __Caller_h_
#include "owExport.h"
class Param;
class EXPORT_Library Caller
{
public:
typedef void(*FctVoidT)();
static void CallVoid(FctVoidT _fct);
typedef void(*FctIntT)(int);
static void CallInt(FctIntT _fct);
typedef void(*FctThisT)(Caller &);
static void CallThis(FctThisT _fct);
typedef void(*FctParamT)(Param const &);
static void CallParam(FctParamT const &_fct, Param const &_param);
};
#endif
Param.h
#ifndef __Param_h_
#define __Param_h_
#include "owExport.h"
class EXPORT_Library Param
{
public:
};
#endif
owExport.h
#ifndef __owExport_h_
#define __owExport_h_
#define OW_EXPORT __attribute__ ((visibility("default")))
#define OW_IMPORT
// Use this one to fix one warning
#define OW_IMPORT __attribute__ ((visibility("default")))
#ifdef Library_EXPORTS
# define EXPORT_Library OW_EXPORT
#else
# define EXPORT_Library OW_IMPORT
#endif
#endif
CMakeLists.txt that configures the project:
cmake_minimum_required(VERSION 3.0.0)
project(TestFunction)
set(BUILD_SHARED_LIBS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fsanitize=undefined ")
# Act here to for the call of function through pointer to incorrect function type
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
add_library(Library Caller.cxx Param.cxx)
add_executable(TestWithLib Main.cxx)
target_link_libraries(TestWithLib Library)
First: It is not good if you edit the question to add fixes already. This makes it hard to answer.
For your problem: You basically have 2 issues: First with the Ssymbol Caller, second withParam`, both are basically the same.
For the source of the issue: UBSAN compares the typeinfo of the pointer to the expected typeinfo. If the typeinfo differs, it shows the error. typeinfo comparison is done by pointer comparison. This is great for speed but introduces a subtle issue: Even when the actual types are literally the same, they might not share the same typeinfo. This is also important when you throw a type from a shared library and want to catch it in the executable (or vice-versa): Catching is done by typeinfo comparison and if the two types are not exactly the same (share the same typeinfo) you won't catch it.
So your first issue is class EXPORT_Library Caller: You conditionally define EXPORT_Library to either be "exported" or not. If it is exported from multiple DSOs then the typeinfos will be merged. In your case you export it in the shared library but not in the executable which prevents merging them. You can use BOOST_SYMBOL_EXPORT or OW_EXPORT for this.
Second issue is the other way round (assuming EXPORT_Library==OW_EXPORT): Param is exported when the Param.h header is included which is only done by the executable not by the shared library. Again typeinfos not merged -> different types to the RTTI system.
Bottom line: Export all your classes you want to use over DSO boundaries.
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.
// SomeCls.h
class SomeCls
{
static const int PERIOD_ALARM_NORMAL = 5;
static const int PERIOD_ALARM_THRESH = 1;
void method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
} obj;
It is going to build ok. Now take out the method() implementation and place it in a cpp file:
//SomeCls.cpp
#include "SomeCls.h"
void SomeCls::method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
Why does mr. linker say
undefined reference to SomeCls::PERIOD_ALARM_NORMAL' undefined
reference toSomeCls::PERIOD_ALARM_THRESH'
?
Thanks
EDIT:
It seems to me that that inside .h, the ternary operator takes static const ints it as rvalues but ... outside the decalrative .h, it regards them as lvalue and needs definition.
This is what I have managed to understand from the answers below. Kudos to Bada compiler (some eabi linux thinggie)
If the compiler can't see all the static class constants' values, then you have to provide definitions for them so that they'll actually be stored somewhere. Add the following to your cpp file:
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
This is a GCC limitation, but it's completely standard comforming. Technically a static const int is still an lvalue. You've provided the value inline so compiler will almost always use it as an rvalue. There is one exception. The abstract instructions emitted by the compiler for ternary operators queries the address of lvalues. Hence the error you're seeing.
You can work around this by using enum instead. Or if you're using a new version of GCC constexpr was added to standard to fix this exact problem (named and typed rvalues).
Alternatively you can provide the linker with a definition for the constants. E.g. in your classes cpp file add the a line like
// I wish I had constexpr
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
As a side note: I was a staunch proponent of static const for class scope constants. Then I found out that MSVC doesn't allow for static const float with the value inline. So the only values you can portably put in a static const are integers, in which case enums provide all the same features plus the guarantee that they'll never silently convert to an lvalue.
If, for whatever reason, you compiler simply refuses to link the code (like GCC 4.4.5 does), here's a simple fix: Replace the static const ints with an enum.
// someclass.h
// include guards, blabla
class SomeClass
{
enum AlarmPeriod{
PERIOD_ALARM_NORMAL = 5,
PERIOD_ALARM_THRESH = 1
};
public:
void method();
};
// someclass.cpp
#include "someclass.h"
void SomeClass::method(){
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
// main.cpp
#include "someclass.h"
int main(){
someclass sc;
sc.method();
}
This links cleanly with GCC 4.4.5, which wouldn't link the former version, even though both are technically the same.
Note that you cannot, amongst other things, take the address of PERIOD_ALARM_NORMAL and PERIOD_ALARM_TRESH anymore, because both names are just aliases for their respective values.