0

I intend to provide simple wrappers to the operating system API, which throw exceptions when errors happen. These wrappers are simple, and are all defined as inline functions in a header file. Since system API is supposed to be large, the header file is supposed to be huge as well, containing a large number of tiny inline functions. The problem is, if a shared library (.so) is compiled with the header file included, will all these tiny wrappers be compiled into the resulting binary, resulting in a large binary file, even when only a small part of the wrappers are actually used? What about the case with executables, will it be different? If it is the case, will splitting the wrappers into multiple header files be the only solution? Or should I make the wrappers internal linkage by specifying static?

Here is what I think. The wrappers may be ODR-used (e.g., taking its address). And on Linux platforms, functions with external linkage are exported by default (i.e., linkable by other binary modules). So I guess it may be necessary for the linker to actually generate outline definitions for them. Please refer to bullet point 3) in the Description section here.

A simple example wrapping CloseHandle() in Windows API:

inline void close_handle(HANDLE handle) { if (!CloseHandle(handle)) { throw std::system_error(GetLastError(), std::system_category(), "CloseHandle"); } } 
8
  • My feeling is that your question is too broad (you don't show any code), unclear, and you care too much about implicit details (you did not tell which) that the implementation does handle quite well in practice. Code a bit of your library first, then ask more focused questions with some actual source code in them. Be more specific: what OS, what compiler, what compilation flags, what source code? Commented Nov 16, 2015 at 13:06
  • @BasileStarynkevitch You may be right. But what I want is an (almost) protable source-code-level solution. So, the OS, compiler, and compiler option details should not matter. I will add some simple example code. Commented Nov 16, 2015 at 13:14
  • The compiler options does matter, since some compilers (g++ probably) won't inline functions if you don't ask them to optimize. Commented Nov 16, 2015 at 13:21
  • @BasileStarynkevitch Actually, I do not care whether the functions are practically inlined or not. I do care whether binary is generated even when the functions are not used at all. And if it is, what could be an elegant and portable way to prevent it. Commented Nov 16, 2015 at 13:24
  • You should not care in practice. Trust your implementation, it is doing well enough. I don't understand why avoiding some (rare) inlined function to be outlined is so important. Most inlined functions won't be outlined (and the few that would be outlined would be outlined for some good reasons) Commented Nov 16, 2015 at 13:24

1 Answer 1

2

A (rather small) function declared static inline (or often, just inline, or even a member function defined inside some class or struct) won't (in practice) appear in the code if it not used (see this), and will probably be inlined everywhere. Of course, you need to enable the optimizations in your compilation command. So, if using GCC, compile with g++ -Wall -O2 (and you might add -fverbose-asm -S and look inside the generated assembler code to check).

Some compilers (and probably g++) won't bother inlining if not asked to optimize. And inlining is always a optimization, which the compiler might not do in some occasions (in particular when storing the address of that function somewhere)

BTW, it may look you are reinventing a framework similar to POCO or to Qt. Did you consider using them instead?

Also, recent C++11 (and C++14) implementations are already wrapping a significant part of the OS API (in particular, standard C++ IO library and C++ thread support library and more recently C++14 TS), often already using exceptions, so better leverage on them and use a recent C++ compiler (for GCC, that means GCC 5.2 in November 2015).

(In other words, code your thing for C++11 at least, not C++98)

On Linux with GCC (or Clang/LLVM), if making a library, you might be interested in link-time-optimization (compile & link the library with g++ -O2 -flto), precompiled headers, visibility function attributes.

Regarding program libraries on Linux, read Program Library HowTo. For shared libraries, read Drepper's paper: How to Write Shared Libraries and this answer.

In practice, an inlined function in some library would often not be outlined in the library, but in the application program calling it. So if your shared library Foo defines in a public header <foo.h>

 inline int maxsq(int a, int b) { // you could add some conditional throw here... if (std::abs(a) < std::abs(b)) return b*b; else return a*a; } 

then the object code of maxsq probably won't appear inside libfoo.so, but only in your program (which #include <foo.h> in its source code) if that program needs maxsq to be outlined, e.g. stores the address of maxsq somewhere (or if you did not ask for enough optimizations).

Remember that inlining is always an optimization, and some compiler may avoid it (even for good performance reasons) sometimes. In practice, trust your compiler (actually, your C++ implementation also includes the linker, which might "garbage collect" sections).

Sign up to request clarification or add additional context in comments.

7 Comments

see here for some discussion of static inline in C++
I hope to find a portable source-code-level solution that works without twiddling with compiler options. Besides, what I need is checked system API, not an entire framework. So, POCO or Qt does not meet my needs. They are both very nice, of course :)
You don't need twiddling compiler options. But IMHO, GCC is useless for production code without -Wall (to get most warnings) and without -O2 to get optimizations, without which the performance of the binary program would be poor
How about the case when the an inline function is ODR-used, e.g., when its address is taken? An outline definition should be mandatory in such cases, which is compiled into the binary. And the ODR-use may not be obvious. It may appear in another binary module.
The compiler is required to handle that case transparently.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.