Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced C++ Programming Anton Kaiser, Summer Term 2012.

Similar presentations


Presentation on theme: "Advanced C++ Programming Anton Kaiser, Summer Term 2012."— Presentation transcript:

1 Advanced C++ Programming Anton Kaiser, Summer Term 2012

2 Advanced C++ Programming Anton Kaiser, Summer Term 2012

3 1. Overview 1.Global Objects 2.Statics 3.Initialization 2. Problems 1.Ordering 2.Multithreading 3. Possible Solutions 1.Avoiding Statics 2.Singleton 3.Schwarz Counter 4.Spinlocks 4. Conclusions 5. Sources 3

4  Where do statics reside? ◦ Special part in the data segment  When do statics exist? ◦ During the whole lifetime of a program  What if they have not been initialized? ◦ Static primitive variables are initialized to NULL ◦ For objects (like strings) the standard constructor will be called 4

5  Global objects ◦ Accessible from every file and function of the program ◦ Typical examples: cin, cout, cerr, clog //Class1.cpp string line; // global int main() {…} //Class1.cpp string line; // global int main() {…} //Class2.cpp extern string line; int lineSize() { return line.size(); } //Class2.cpp extern string line; int lineSize() { return line.size(); } 5

6  Static objects ◦ Accessible only from functions in the same file, depending on the location they have been defined at  In the whole module, when defined outside all functions  Locally in a block where defined //AccessControl.cpp static string password = "coolPassword"; bool testPasswordInput(string &pw) { if (pw.compare (password) == 0) return true; else return false; } //AccessControl.cpp static string password = "coolPassword"; bool testPasswordInput(string &pw) { if (pw.compare (password) == 0) return true; else return false; } //returns the time difference since the last call long timeDiff() { static time_t seconds = 0; //0 only assigned once! long oldSeconds = seconds; //save old time time( &seconds);//get new time return (seconds - oldSeconds); //return difference } //returns the time difference since the last call long timeDiff() { static time_t seconds = 0; //0 only assigned once! long oldSeconds = seconds; //save old time time( &seconds);//get new time return (seconds - oldSeconds); //return difference } 6

7  Initialization ◦ First phase: static initialization (at compile time) ◦ Second phase: dynamic initialization extern int someOtherModulesFunc(); namespace StaticsAbound { inline int localFunc() { return 10 }; struct Thing { Thing() : i(someOtherModulesFunc()) {} int i; } thing;// dynamic int i1 = 0; int i2; int i3 = 1; // static int i4 = localFunc();// static int i5 = ::SomeOtherModulesFunc(); // dynamic } extern int someOtherModulesFunc(); namespace StaticsAbound { inline int localFunc() { return 10 }; struct Thing { Thing() : i(someOtherModulesFunc()) {} int i; } thing;// dynamic int i1 = 0; int i2; int i3 = 1; // static int i4 = localFunc();// static int i5 = ::SomeOtherModulesFunc(); // dynamic } 7

8 1. Overview 1.Global Objects 2.Statics 3.Initialization 2. Problems 1.Ordering 2.Multithreading 3. Possible Solutions 1.Avoiding Statics 2.Singleton 3.Schwarz Counter 4.Spinlocks 4. Conclusions 5. Sources 8

9 extern someObject o2;// declaration someObject o1(o2);// undefined! someObject o1("o1");// definition someObject o2(o1); // this is fine! int main() { someObject o3("o3"); return 0; } extern someObject o2;// declaration someObject o1(o2);// undefined! someObject o1("o1");// definition someObject o2(o1); // this is fine! int main() { someObject o3("o3"); return 0; }  Objects are initialized by order of definition, not declaration! 9

10 // object.h class Object {…}; extern Object o1; extern Object o2; extern Object o3; // object.h class Object {…}; extern Object o1; extern Object o2; extern Object o3; // objects01.cpp #include "object.h" Object o0("o0"); Object o1("o1"); int main() {...} // objects01.cpp #include "object.h" Object o0("o0"); Object o1("o1"); int main() {...} // object2.cpp #include "object.h" Object o2("o2"); // object2.cpp #include "object.h" Object o2("o2"); // object3.cpp #include "object.h" Object o3("o3"); // object3.cpp #include "object.h" Object o3("o3"); Compiler/LinkerOrder Borland C/C++ 5.6o0, o1, o2, o3 Code Warrior 8o0, o1, o2, o3 Digital Mars 8.38o3, o2, o0, o1 GCC 3.2o3, o2, o0, o1 Intel C/C++ 6.0o0, o1, o2, o3 Visual C++ 6.0o0, o1, o2, o3 Watcom C/C++ 12o3, o2, o0, o1 Recommendation - Don’t rely on global object initialization ordering - Do utilize global object initialization tracing mechanisms anyway Recommendation - Don’t rely on global object initialization ordering - Do utilize global object initialization tracing mechanisms anyway 10

11 int myFunction() { static bool finished = false; static int result; if (!finished) { finished = true; result = doCalculation(); } return result; } int myFunction() { static bool finished = false; static int result; if (!finished) { finished = true; result = doCalculation(); } return result; } int myFunction() { static int result = doCalculation(); return result; } int myFunction() { static int result = doCalculation(); return result; }  Race condition ◦ Two threads could both see the uninitialized result  Race condition ◦ Two threads could read finished simultaniously, both execute doCalculation() and write to result 11

12 class MyClass {...}; int calculateResult() { static bool isConstructed = false; static MyClass myClass; // uninitialized if (!isConstructed) { myClass.isConstructed = true; new (&myClass) myClass; atexit(destruct_myClass); return myClass.doCalculation(); } class MyClass {...}; int calculateResult() { static bool isConstructed = false; static MyClass myClass; // uninitialized if (!isConstructed) { myClass.isConstructed = true; new (&myClass) myClass; atexit(destruct_myClass); return myClass.doCalculation(); } class MyClass {...}; int myFunction{...}; static MyClass myClass; return myClass.doCalculation(); } class MyClass {...}; int myFunction{...}; static MyClass myClass; return myClass.doCalculation(); }  Initialization ◦ myClass may not have been initialized before the calculation  Race condition ◦ myClass could be double-constructed and double- destructed 12

13 int calculateResult() { Lock myLock(myMutex); // only one thread gets past here static int result = doCalculation(); return result; } int calculateResult() { Lock myLock(myMutex); // only one thread gets past here static int result = doCalculation(); return result; }  Proposition ◦ Locking out other threads  Problem ◦ doCalculation() or any sub-function of it could call calculateResult()  The thread already has the lock, therefore can enter the synchronized block and again see uninitialized result! 13

14 1. Overview 1.Global Objects 2.Statics 3.Initialization 2. Problems 1.Ordering 2.Multithreading 3. Possible Solutions 1.Avoiding Statics 2.Singleton 3.Schwarz Counter 4.Spinlocks 4. Conclusions 5. Sources 14

15  Avoid using statics whenever possible  Use references as parameters instead of global variables // Using references void myFunction(MyClass &myClass) { myClass.calculate(); } int main() { MyClass myClass; myFunction(myClass); return 0; } // Using references void myFunction(MyClass &myClass) { myClass.calculate(); } int main() { MyClass myClass; myFunction(myClass); return 0; } // Using statics static MyClass myClass; void myFunction() { myClass.doCalculation(); } int main() { myFunction(); } // Using statics static MyClass myClass; void myFunction() { myClass.doCalculation(); } int main() { myFunction(); } 15

16  It‘s possible to use stack variables in main() ◦ A little bit of a hack ◦ Lifetime and ordering control // main.cpp #include "SomeClass.h" int main() { SomeClass o1; g_ptr_someClass = &o1; return g_ptr_someClass->func(); } // main.cpp #include "SomeClass.h" int main() { SomeClass o1; g_ptr_someClass = &o1; return g_ptr_someClass->func(); } // SomeClass.h class SomeClass {…}; extern SomeClass *g_ptr_someClass; // SomeClass.h class SomeClass {…}; extern SomeClass *g_ptr_someClass; ◦ Downsides  Inconvenient syntax (use global variables by pointers)  Loss in efficiency 16

17  Meyer‘s Singleton ◦ Control over creation time ◦ No control over destruction time (during process shutdown) ◦ If some destructor calls GetInstance() after the destruction of s_instance, the program will be working with an uninitialized object, which is known as „Dead Reference problem“ ◦ Using a static local object is vulnerable to race conditions in multithreaded environments 17

18  Alexandrescu Singleton ◦ Can be made thread-safe ◦ Solves the Dead Reference problem by providing the GetLongevity methods for specification of destruction order ◦ Downsides:  A lot of effort if new singletons are inserted into the system  The programmer provides the ranking values  “Easy to get it wrong” and errors are very hard to detect 18

19  Also known as „Nifty Counter“  Used by Standard C++ iostream library  Ensuring static objects to be available before mainline execution, and before they are used by any other static objects 19

20 // Stream.hpp class StreamInitializer; class Stream { friend class StreamInitializer; public: Stream() { /* Constructor must be called before use. */ } }; static class StreamInitializer { public: StreamInitializer(); ~StreamInitializer(); } initializer; // Stream.hpp class StreamInitializer; class Stream { friend class StreamInitializer; public: Stream() { /* Constructor must be called before use. */ } }; static class StreamInitializer { public: StreamInitializer(); ~StreamInitializer(); } initializer; 20

21 // Stream.cpp static int nifty_counter; // initialization of the counter StreamInitializer::StreamInitializer() { if (0 == nifty_counter++) // initialization of the stream object‘s static members } StreamInitializer::~StreamInitializer() { if (0 == --nifty_counter) // cleaning up } // Stream.cpp static int nifty_counter; // initialization of the counter StreamInitializer::StreamInitializer() { if (0 == nifty_counter++) // initialization of the stream object‘s static members } StreamInitializer::~StreamInitializer() { if (0 == --nifty_counter) // cleaning up }  The header file of the Stream class has to be included before any member function can be called on the Stream object  This ensures that the constructor of the initializer object is called before the Stream object is used. 21

22 MyClass &getInstance(); // initialization of the counter { static int guard; // initialized to zero spin_mutex(&guard); lock_scope lock(smx); static MyClass instance; return instance; } MyClass &getInstance(); // initialization of the counter { static int guard; // initialized to zero spin_mutex(&guard); lock_scope lock(smx); static MyClass instance; return instance; }  The spinlock itself is costly ◦ In most cases function-local static objects are not being accessed too often by too many threads ◦ The guarded section is very short 22

23 1. Overview 1.Global Objects 2.Statics 3.Initialization 2. Problems 1.Ordering 2.Multithreading 3. Possible Solutions 1.Avoiding Statics 2.Singleton 3.Schwarz Counter 4.Spinlocks 4. Conclusions 5. Sources 23

24  Static objects can bring along serious problems  Avoid them whenever you can!  If really needed, always be sure about ◦ Scope and lifetime ◦ Construction Order ◦ Multithreading issues  Possible solutions can be ◦ Singletons, Schwarz Counter ◦ Locking with spinlocks 24

25 1. M. Wilson, Imperfect C++: Practical Solutions for Real- Life Programming, Addison-Wesley, M. Götze, Presentation in the Seminar on Advanced C++ Programming, FAU Erlangen-Nürnberg, G. Loganathan, Presentation in the Seminar on Advanced C++ Programming, FAU Erlangen-Nürnberg, Prinz, Peter, C++ Lernen und professionell anwenden, MITP-Verlag, U. Breymann, C++ Eine Einführung, 5th edition, Hanser Verlag, S. Prata, C++: Eine Einführung in die objektorientierte Programmierung, 8th edition, te-wi Verlag, Schwarz Counter C++ Idiom https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/N ifty_Counter https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/N ifty_Counter 25


Download ppt "Advanced C++ Programming Anton Kaiser, Summer Term 2012."

Similar presentations


Ads by Google