3 ms·
Things that are normally valid C++ code that would get you into trouble if you are library (some of them you are static one): - Globally constructed objects, c
by malkia 13y ago
Things that are normally valid C++ code that would get you into trouble if you are library (some of them you are static one):
- Globally constructed objects, called before main(). Yes you might say, don't use them, but once you are in C++ land you can't be safe that everything else you've decided to use does not use them.
- Throwing exceptions. This is mostly debugger pain. If you've used (again) without knowledge underlying code/library that uses exceptions (and note, you don't) then you might be inadvertently throwing them to the user.
- RAII does not work with longjmp/setjmp. If you have callback mechanisms, and your client does longjmp on you, this won't work with any RAII done objects.
- C++ runtime. You would be requiring this to be loaded, which might become nuisance in situations where your main application never used C++, you wrote a plugin/shared lib using C++ code, and now there is the requirement. Not a big deal after all.
- C++ runtime again but for MSVC (although it's for the C runtime too to a point). Now while you can make code that is more independent from the "C" runtime for MSVC, it's harder to maintain this for C++ - you might run into issue where mixing dynamically (shared) libraries would have to use different (and even more different through WinSxS) MSVCPxxx.DLL libs (C++ runtime). In all fairness, this is more of a Microsoft mishap, and not caring about backward binary compatibility, but hey it's there.
- Most of all, no matter what you do - you can't expose the really useful C++ things - like stl or qt5 or something else's objects/vectors/strings/maps/etc. Well that's obvious - off course you can, but then you are basically forcing the person using your library to go that route. If your interface is in "C" not only he longer has this pressure, he can use it easier through FFI in plethora of other language/runtimes without writing wrappers, and minimizing the number of precompiled binaries that he has to provide (no linking statically to C++ runtime would turn out bad at the end).
- Case in point - STL's ITERATOR_DEBUG_LEVEL in MSVC. By default it's set to 2, which means make extensive checks of every map<>, vector<>, etc. once they are released. This severely slows down the debug. It's useful, but it's not feature that can be turned on or off. Most of 3rd party libraries, are playing safe and don't do anything special, e.g. they expect ITERATOR_DEBUG_LEVEL=2. But in your production code you might want even your Debug versions to be somewhat faster. Most of your day won't be spent tracking down map<>, vector<>, but other things. As such you go ahead and compile ITERATOR_DEBUG_LEVEL=0, and then .... well you can not link anymore directly to the said 3rd party libs. You either have to precompile them, which might be fine, until you are given Middleware that is not compiled this way. And in all cases it's very important that all these libs use at the end just one runtime, not several (as it happens when you have tons of MSVCRxxx.dll/MSVCPxxx.dll in your running image).
- And there is more. For example for statically build
libraries, global constructors might be lost, if there is no symbol referring to them used by the main application. So you might lose some important initialization step.
- etc., etc., etc.
- vinkelhake 13y agoI agree with your points, but the setjmp/longjmp issue is largely orthogonal to RAII. If you've got code in your library that you need to run after invoking a user callback (like free or mutex unlock) and the user does a longjmp on you, it doesn't really matter if the code you want executed is done automatically from a destructor (as in RAII) or manually. If you want to allow the user to do a longjmp from a callback you have to write the code carefully to handle this, be it C or C++.
- krichman 13y agoI just saw someone use a globally constructed object to instantiate some global variables and it seemed like a neat solution, but I don't really know C++. What causes it to go wrong?
- malkia 13y agoIt depends on compiler / librarian / linker. I'm talking from experience here, not from deep knowledge or understanding the problem. So basically what happens, is that if you have a lone variable globally instantiated, but no one refers to it, it might be stripped from the final linking, and as such any constructors/destructors associated with it would not be called. Now if you are linking directly to the .obj file where the variable is declared it most likely would be put into account, but if the .obj is in a .lib (.a) then it might not. Again, do some experiments, try it out. There is another nastiness with globally initialized variables, that one should not expet some kind of order when the things are initialized. We've had code in the past (Dreamcast) where globally initialized variables, which were assigned the values of other globally initialized variables and then some operation on them would not always work: e.g. this at the top level: float x = 10; float y = x + 20; It worked most of the time, and then if someone started moving variables to other object files, it stopped. Check the http://yosefk.com/c++fqa/ http://yosefk.com/c++fqa/ for more gotchas.