Built-in class functions and shared library (dll)

I'm trying to move some code to a shared library (works fine when compiling offline), but getting some problems with the class's built-in functions. mingw / gcc v4.7.2.

Part of the problem is that I prefer to define my built-in functions outside the class declaration (it keeps the class declaration tidier and easier to read). I have always believed that this is acceptable and equivalent to the definition in the class declaration ... but this is not always the case. I created a simple sample to demonstrate the problems. (Obviously, dllexport is usually found in a macro to switch between import / export.)

Title:

// 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 (an implicit, inline definition inside the class), the code compiles without warning.

With INLINE_OPTION == 2 (extra-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 the embed), 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 information about its call from the first line inside Dummy :: do_something () in the .cpp file. Please note that this is an attempt to embed a function inside the library itself! For simple access functions, this can be very significant overhead.

Am I doing something wrong? Is gcc correct when considering an inline function outside a class, differently, in class function definitions? (Am I really forced to clutter the class declaration?)

Note. The problem is not only that I declare inline. It also affects all declared as constexpr and even destructors declared as "= default" when inheritance is applied.

Edit:

Just tried with mingw64 / gcc v4.8.0 with the same results. Note that this includes the fact that option 1 is NOT embedded 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 give a warning - Winline

+6
source share
6 answers

The editing that I did in another post did not seem to be perceived, and in any case it seems that some additional clarity may be appropriate, so I am posting the details that I posted to another forum. In the class C code below, this problem works - export only elements that are not part of the composition, not the entire class. As noted in the comments elsewhere, __declspec(dllexport) and __attribute__((dllexport)) 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 using the options: -std=c++11 -O2 -S -Winline (using mingw / ming64 with gcc v4.7.2 or v4.8.0), you can see that the collector created for the ga library functions is gb and gc are as follows:

ha:

 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 

ds:

 .seh_endprologue movl (%rcx), %eax addl $1, %eax ret 

and you will get 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())

Note that there were no warnings that fa is not embeddable (which, I think, was expected). But also note that ga, gb, and gc are all library functions. No matter what you might think about whether the built-in functions themselves should be exported, there are no good reasons why the built-in strings cannot be built into the library. Therefore, I consider this a bug in the compiler.

Take a look at the well-studied code and see how much you find that only explicit members are exported. For example, those few boost parts that compile into a library (such as regex) use the class A method, which means that many access functions are not built into the library.

But, all that is aside, the answer at the moment is the class C method (it is obvious that in real code this must be enclosed in a macro in order to switch between export and import, as usual at the class level).

+1
source

I don't know anything about how to create shared libraries on Windows. On linux / OSX, no special processing is required in the source code, so both common (.so) and regular (.a) libraries can be made from the same sources without special processing.

If you really need a special attribute to export characters to shared libraries, you can just break the code, for example.

 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; } 
+2
source

Perhaps my answer was a bit cryptic ... let me give you a quick example of what I mean by using snippets of code.

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 can place inline definitions just below the class declaration in the same header file. However, I believe that 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 can handle it.

+1
source

This is not a compiler error as intended. In C ++, if a function is inline, it must be declared inline in every declaration. There are 5 properties that must be met, and one of them:

  An inline function with external linkage (eg not declared static) has the following additional properties: 1) It must be declared inline in every translation unit. ... 

In your example, you first declared the function Dummy :: get_int () as a definition not inside the built-in class. This means that the function cannot be updated as inline

Source: http://en.cppreference.com/w/cpp/language/inline

BTW: the built-in specifier works differently in C, where you can declare both built-in and non-built-in versions of the same function. However, you must implement both and make sure that they do the same.

+1
source

Why don't you declare your inline function in the class declaration ( inline int get_int() const; )? Maybe there is a mistake?

0
source

The compiler cannot embed a function that must be exported to a dll. In the end, when called from an executable file associated with your DLL, the function must have an address. Most likely, the call from do_something will be built in, but in the general case, I think it is simply impossible

0
source

All Articles