0

Lately, I have been playing with this idiom:

Changing a private member variable into a local static variable when:

  1. member to singleton class used in only one function
  2. member is mutex for a shared resource used in only one function.
  3. member is constant expression used in only one function.

Looking through my code, I picked up 3 instances where I did this:

/* On Singleton Classes */
void MainWindow::on_lineEdit_Filter_textChanged( const QString &text )
{
    static QString s;

    if ( text == s ) { return; }
    s = text;
    this->filter(text);
}
/* On Mutexes */
void PdfScraper::onFindKeywordFrequency( Tag t, QString designation, int n )
{
    static QMutex m;
    m.lock();
    db_Pdf.setValue( t, designation, n ); // db = database
    m.unlock();
}
/* On Constants applicable to one function */
void QPixmapCrop::evaluateImage()
{
    QString pictureText = MainWindow::getPictureText( f_TempCrop.fileName() );
    
    s_SaveEvent->setTitle( QPixmapCrop::findTitle( pictureText ) );
    s_SaveEvent->setDate ( QPixmapCrop::findDate ( pictureText ) );
    s_SaveEvent->setTime ( QPixmapCrop::findTime ( pictureText ) );
    s_SaveEvent->setTheme( QPixmapCrop::findType ( pictureText ) );
    s_SaveEvent->setTags ( MainWindow::getTagList( pictureText ) );
    
    /* Find Weekly Day   */
    {
        const static QRegularExpression re( R"((?is)^.*(sundays|mondays|tuesdays|wednesdays|thursdays|fridays|saturdays).*$)" );
        if ( pictureText.contains(re) ) {
            QString s = QString( pictureText ).replace(re, "\\1");
            const static QRegularExpression re_1( R"((?is)^sundays$)"    );
            const static QRegularExpression re_2( R"((?is)^mondays$)"    );
            const static QRegularExpression re_3( R"((?is)^tuesdays$)"   );
            const static QRegularExpression re_4( R"((?is)^wednesdays$)" );
            const static QRegularExpression re_5( R"((?is)^thursdays$)"  );
            const static QRegularExpression re_6( R"((?is)^fridays$)"    );
            const static QRegularExpression re_7( R"((?is)^saturdays$)"  );
            s.replace(re_1, "Sundays"   );
            s.replace(re_2, "Mondays"   );
            s.replace(re_3, "Tuesdays"  );
            s.replace(re_4, "Wednesdays");
            s.replace(re_5, "Thursdays" );
            s.replace(re_6, "Fridays"   );
            s.replace(re_7, "Saturdays" );
            s_SaveEvent->setDay(s);
        }
    }
    /* ... etc ... */

Given all this, are my principles thus far, sound? Are there any more that can be added?

I would also like to touch upon:

  1. Interpretation: how does one typically interpret local static variables when coming across them in the wild?
  2. Performance & Memory Footprint: can or will this make a difference at all?
  3. Readability: when does it help or hinder the readability of code?
1
  • Oh boy, you’re in for so much pain, I’m not even sure where to start. Don’t do this.
    – Alexander
    Commented Nov 25, 2023 at 16:44

5 Answers 5

4

1.member to singleton class used in only one function

The need for "true" singletons (classes which only have one instance, and completely prohibit new instances from being created) is actually pretty rare.

Even if you're modelling a single resource (e.g. a robotic arm, of which there's only one), you still might find a need to construct multiple objects, e.g. for a test. This is best modelled with a class that happens to have only one instance in real use, but allows for multiple instances in testing.

If you only have one object, then there's no memory savings of using a static variable over an instance variable. In either case, you just have one variable. The big difference is that the static var locks you into having just one object, and makes making a second object require refactoring.

  1. member is mutex for a shared resource used in only one function.
/* On Mutexes */
void PdfScraper::onFindKeywordFrequency( Tag t, QString designation, int n )
{
   static QMutex m;
   m.lock();
   db_Pdf.setValue( t, designation, n ); // db = database
   m.unlock();
}

This is an outstandingly fragile approach. What if some other developer needs to access db_Pdf, and creates their own local static QMutex m? Now you have two mutexes trying to enforce exclusive access to a shared resource, which in effect, is not exlusive at all anymore.

It would be much better to have an object which represents the resource (e.g. the db_Pdf). This object can then just store the mutex as an instance variable, in a way that actually makes it unique for the resource being protected. If two users have shared access to new db_Pdf object, they both automatically get synchronized by the same mutex.

  1. member is constant expression used in only one function.

This seems like a really good use case! It has some great benefits:

  1. The declaration of this constant is close to its use, making its relevance clear.

    If it were a constant somewhere at the top of the file, you might need a long name which captures its context, e.g. EVALUATE_IMAGE_IS_WEEKDAY_REGEX. Because this static constant is now close to its usage, you don't need the name to capture all this contextual information, so a short name like re becomes much more acceptable name.

  2. You get the tightest access control possible, which is even more restrictive than a private ivar.

But I'd be careful to only use this with truly constant data. i.e. not just a pointer that's a constant, but a value that's entirely constant, in its entirety. Otherwise this is just a bad global variable, with the usual worries about shared mutable state.

Interpretation: how does one typically interpret local static variables when coming across them in the wild?

It depends. Usually it's "What the fuck, why?!"

Performance & Memory Footprint: can or will this make a difference at all?

If this is a question you have to ask, then you probably don't need to worry about it.

Readability: when does it help or hinder the readability of code?

It can help, or it can hinder. IMO the third case is compelling where it helps, but the other two it's drastically worse.

3
  1. Interpretation: how does one typically interpret local static variables when coming across them in the wild?

Primarily it's a singleton. Where the scope is limited to the block it is defined in i.e. one typically doesn't need to search the code base for other usages.

  1. Performance & Memory Footprint: can or will this make a difference at all?

Since they are singletons (hence only initialized once), it's likely that in most real world code they won't be accessed enough to have a major affect on overall performance - however as always bench mark it.

  1. Readability: when does it help or hinder the readability of code?

I don't see any issue with the constant use case, the mutex is a bit of a special case, since it is expected it will have a longer lifetime, so again I don't see an issue there.

The mutable variable case bothers me a bit, since you can embed global state anywhere in the program, in short it is easy to miss unless you read every method. On the other hand that is exactly what local statics are designed to do...

2

I would reverse your question, and say when should a local static variable be made into a member function, and the answer to that question is easy: when it needs to accessed in two different places.

Local statics are encapsulation at its finest, only accessible where used.

2
  • Can you give an example? Say for instance, out of the three scenarios offered, would you use a member function?
    – Anon
    Commented Nov 24, 2023 at 23:14
  • Static variables should be used wherever they are appropriate - used in just one place, persist across invocations. I can’t say whether any of your examples should or not be static, because I don’t know where else you may want to use them or don’t. I will say there’s nothing wrong with reducing the lifespan of a variable if you find you don’t need the persistence. Static variables hold state, but the whole point of that is that state is only used within on place. Given a short function, that makes it incredibly easy to reason about. If you don’t need that state or you need it elsewhere…
    – jmoreno
    Commented Nov 24, 2023 at 23:27
2

Efficiency:
There is a reason to extract the global constants and/or state from your function, namely to remove the initialization, if dynamic, from the function. Even skipping it takes some time.
Conversely, if the initialization is expensive and the object rarely actually needed, that might be a reason to keep it there.
Anyway, that only seems to apply to your regex-objects from what I can see.

Comprehension:
A good reason to keep it there is minimizing its scope for easier comprehension of other code, and its interactions. Conversely, if it is well-named, and especially if it is constant, extracting it out will shorten the function, and all else being equal, less is easier to comprehend.

In the end, there are no hard and fast rules, you have to decide how to balance things for yourself.

2

Moving data from a singleton to a static variable is just pointless (in the best case).

You lose control about access. You can pass a complete singleton to another method, now you have two items to pass.

But the real problem is that singletons don’t stay singletons. And at that point you will have serious problems with your single static variable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.