Click here to Skip to main content
Click here to Skip to main content

Saving a variable temporarily

By , 16 Nov 1999
 
  • Download source - 1 Kb
  • On several occassions I've had to execute a piece of code which used a global variable that needed to be temporarily reassigned. The problem was that I wanted the variable to return to its original value after the code had been executed. So I would end up assigning it to some local variable, executing the code, and then setting the global variable back to its original value held by the local variable. Here's a sample of what I mean:

         {
             // Hold value in local variable 
             double* pTemp = g_pValue; 
             
             // Reassign it to another value (temporarily) 
             g_pValue = NULL; 
             
             // Use it in some code 
             FunctionWhichUsesGlobalValue( ); 
             
             // Restore it back to what it was originally 
             g_pValue = pTemp;
         } 
    

    As may be evident, this implementation has a couple of problems. For one, if the function throws an exception the global variable never regains its original value. Another problem is that you have to remember to restore the value. For a simple case like this one it's not difficult, but if you have multiple return points, it means having to restore the value on all of them. This results in duplicate code which is more difficult to maintain.

    So how do we solve this problem? With a class, of course! If we store the variable's original value inside a class, we can then make the destructor restore the variable's value automatically. This would give us two key advantages:

    • The variable would always regain its original value.
    • We would not have to worry about explicitly restoring it.

    Now, you probably think that the way to do this is with a template class... and you're right. A template class accomplishes this task and turns the above code into something like this:

    {
        CTemp<double*> temp = g_pValue; 
        
        g_pValue = NULL; 
        FunctionWhichUsesGlobalValue( );
    } 
    

    That's quite an improvement, isn't it?! Well, it still has one minor problem which was also prevalent in the first example: the fact that we have to know and specify the variable's type. And while that is not too big of a deal, wouldn't it be better to not have to do it? In other words, wouldn't it be nice to just say: CTemp temp = g_pValue and the object would then somehow know that g_pValue is a pointer to a double? Well, with template member functions it can be done!

    Template member functions work just like regular template functions. The compiler generates them based on the type of the arguments on which they operate. So what is basically needed is a class with a template constructor and a way for the constructor to store the value in a generic member variable which the destructor can then properly restore. It's a bit tough to explain the exact mechanism here but I think you'll understand it once you see its implementation. The class is called CTemp and applying it to the code above would make it look like this:

    {
        CTemp temp = g_pValue; 
        
        g_pValue = NULL; 
        FunctionWhichUsesGlobalValue( );
    } 
    

    Much nicer, don't you think?! Not only does this class do what the template version did but it does it without you having to determine and then specify the variable's type in the code. That's what I call putting the compiler to work for the programmer!

    To use the class simply download the temp.h file into your C++ project, include it where you need it, and enjoy!

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    About the Author

    Alvaro Mendez
    Web Developer
    United States United States
    Member
    I've done extensive work with C++, MFC, COM, and ATL on the Windows side. On the Web side, I've worked with VB, ASP, JavaScript, and COM+. I've also been involved with server-side Java, which includes JSP, Servlets, and EJB, and more recently with ASP.NET/C#.

    Sign Up to vote   Poor Excellent
    Add a reason or comment to your vote: x
    Votes of 3 or less require a comment

    Comments and Discussions

     
    You must Sign In to use this message board.
    Search this forum  
        Spacing  Noise  Layout  Per page   
    GeneralMy vote of 4memberCIDev23 Sep '10 - 3:22 
    As others have said globals used in this way is a sign of poorly designed code. However, it is a useful technique for resource cleanup.
    GeneralNicememberStephen Hewitt26 Jan '06 - 15:20 
    I like it. Implementation is interesting, like Boost.Any.
     
    Steve
    Generalcode in saving vbsussprincess marla sarmiento3 Feb '04 - 16:02 
    pls. tel us the code in saving vb
    Questionwhy?sussJeff Reed4 Feb '00 - 12:14 
    Pass the value to the function instead. If you need to keep changing a global, maybe it shouldn't be global
    AnswerRe: why?sussAlvaro Mendez8 Feb '00 - 9:31 
    I agree with you but I think you're missing the point of the class. The class helps you ensure that a variable's value (whether it's global, a member, local, or whatever) stays the same after a certain operation has completed. I used the example of a global variable because I thought it'd be the most common scenario for using this class. However I've used this class quite a few times for non-global variables. Also, you gotta remember that there are times when "change the function instead" isn't the most desired solution.
     
    Regards,
    Alvaro

    GeneralRe: why?sussMike Junkin3 May '00 - 10:50 
    Alvaro,
     
    I think Jeffs' point is most of the times you could use such a class it's usually an indication that there might be a problem with how the code is designed.
     
    Could be I'm not, and aparently Jeff wasn't, thinking of the right examples. But sticking to your example.
     
    A. globals are bad in general but having a global that is changed as a side effect to using some function is REALLY bad.
     
    B if it's a local variable that your are passing to a function, and you don't want that variable to change, then either the function shouldn't be changing it, or you should just be passing a copy of the variable.
     
    C. If a function is being passed a parameter that it modifies, it should not be because it's an un-intended side effect. It should be because part of the functions purpose is to change that parameter. Which means the results of the change are important and you wouldn't want CTemp to have any chance of rolling back the changes before you got a look at them. (Which really takes you back to B, as the most common scenerio would entail you copying the variable, sending the copy to the function and comparing the changes to the original to see what happened.)
     
    Anyway, I'm glad you posted the article as it raises some issues we all need to think about from time to time.
     
    Mike
    GeneralRe: why?sussAlvaro Mendez3 May '00 - 11:46 
    Mike,
     
    Thanks for your comments Mike. Let me address each of your points:
     
    A. I agree with you. However the example in my article does not show a function that changes a global variable as a side effect. Instead I show a case where I'm about to execute a piece of code which READS the value of a global variable and I purposely want the variable to be set to some specific value just for that. Once that code has executed, the global variable should go back to its original value.
     
    Hasn't that ever happened to you? You're about to execute a piece of code which you know reads the value of some global or member variable. However, in this case you want that piece of code to work with the variable set to, say 0. Then when that's over you want the variable to maintain its original value. Now, think how you'd solve that problem if you couldn't rely on a class like CTemp.
     
    B. Yes I agree that for local variables it's just as easy, and more efficient, to make a copy of it before passing it down.
     
    C. Again, I never intended this to be used for functions that change variables as side-effects. Rather, it's meant for setting a variable's value temporarily before a piece of code is executed and then restoring it after it's done.
     

    I'd like to illustrate this class's reason for being with a real-world example where it came in really handy for me:
     
    I don't know if you this but you can override CWinApp's DoMessageBox virtual function in case you need to provide special handling for calls to AfxMessageBox. Well, I had the misfortune of being handed a poorly written C++ DLL which used AfxMessageBoxes to display any error messages it encountered. My calling program would need to call that code iteratively and it could not be stopped for any error messages. It would simply report them and go on. Well, I solved the problem by overriding DoMessageBox and adding a boolean member variable to my CWinApp-derived class.
     
    Just before entering the iterative process I would create a CTemp variable, set the variable to false (no message boxes) and viola the process would run with no interruptions. Here's how it looked:
     
    {
    CMyApp* pApp = (CMyApp*)AfxGetApp();
    CTemp tMB = pApp->m_bMessageBoxes;
    pApp->m_bMessageBoxes = false;
     
    // Iterate through code (with no interruptions)
    ...
    }
     
    The beauty of this is that as soon as the code had finished running whether successfully or not, message boxes would again be enabled automatically. I didn't have to worry about resetting the variable inside exception-catch blocks, or keeping track of what the value was in some temporary variable of the same type.
     
    Now, you could argue that the DLL was poorly written to begin with and this could have all been avoided otherwise. I agree, but at that time changing the DLL was out of the question. There simply was too much code already depending on it and the project manager would not have allowed it. So this little class solved the problem cleanly and with minimum fuss.
     
    Regards,
    Alvar
    GeneralRe: why?sussSoeren18 May '00 - 6:11 
    Hey, I like this class. I won't use it for global variables,
    because I hardly ever use such things. But I often do
    something like this:
     

    void paintSomething(CDC dc)
    {
     
    int style = ; //some style
    int width = ; //some width
    COLORREF color = ; // some color
     
    CPen someOtherObject(style, width, color);
    CGDIObject *tmp = dc.SelectObject(someOtherObject);
     
    // ... do the drawing
     
    dc.SelectObject(tmp); // here's the problem!
    }
     

    You always have to restore the dc's objects, and CTemp
    might come in quite handy here.
     
    regards,
    Soere
    GeneralRe: why?memberBobbby5 Feb '01 - 21:55 
    This whole concept is a subset of a more general concept. If you "acquire a resource", how do you ensure that the resource will be released if something unexpected (like an exception) happens? Well, the answer, as shown here is to make a class whose destructor cleans up. Stroustrup calls this the "resource acquisition is initialization" idiom.
     
    The example given in this thread is modifying a global variable, but it is also an issue every time you allocate (with "new") 2 items in a constructor. Sure you delete both in the destructor, but what happens if an exception is thrown in the constructor? Then the destructor doesn't even get called. If you put a try/catch around the whole constructor, how will you know if 1 or 2 items were allocated?
     
    Andrei Alexandrescu, writing at http://cuj.com/experts/1812/alexandr.html , creates a class similar to the one created here, called "ScopeGuard", with a detailed explanation of the issues.

    AnswerRe: why?memberStephen Hewitt26 Jan '06 - 15:22 
    You haven't always got that option - For example the global might be part of a 3rd party library.
     
    Steve

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Permalink | Advertise | Privacy | Mobile
    Web03 | 2.6.130523.1 | Last Updated 17 Nov 1999
    Article Copyright 1999 by Alvaro Mendez
    Everything else Copyright © CodeProject, 1999-2013
    Terms of Use
    Layout: fixed | fluid