1

I have this method:

bool CDemoPickerDlg::IsStudentTalk(CString strAssignment)
{
    bool bStudentTalk = false;

    CString strTalkMain, strTalkClass;

    if (theApp.UseTranslationINI())
    {
        strTalkMain = theApp.GetSMMethod(_T("IDS_STR_HISTORY_TALK_MAIN"));
        strTalkClass = theApp.GetSMMethod(_T("IDS_STR_HISTORY_TALK_AUX"));
    }
    else
    {
        strTalkMain.LoadString(IDS_STR_HISTORY_TALK_MAIN);
        strTalkClass.LoadString(IDS_STR_HISTORY_TALK_AUX);
    }
    int iTalkMainLen = strTalkMain.GetLength();
    int iTalkClassLen = strTalkClass.GetLength();

    if (strAssignment.Left(iTalkMainLen) == strTalkMain ||
        strAssignment.Left(iTalkClassLen) == strTalkClass)
    {
        bStudentTalk = true;
    }

    return bStudentTalk;
}

It is called multiple times. Without added "member variables" to the class to cache values is there any other way to create the values for the two CString and int values just the once? As they will not change for the duration of the program.

The method above is static. I know about assigning a value to a static variable but I understand that can only be done once at the time of declaration. Have I miss-understood that?

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 1
    You can use `static` inside a function to make a variable that will live for the entire program. – super Jul 14 '18 at 13:55
  • @super I just thought that the value had to be provided at the time of declaration, which I can't do since I have to use LoadString etc.. The method itself is also "static". – Andrew Truckle Jul 14 '18 at 13:57
  • You can have a `static bool areValuesInitialized = false;` to check if they should be initialized. But IMO you should probably refactor the caching to somewhere else so it can be re-used in other parts of your code. – super Jul 14 '18 at 14:02
  • @super Please see my comment on the answer provided. Thanks. – Andrew Truckle Jul 14 '18 at 14:24

2 Answers2

1

You can use a static constant (or variable, but why make it variable if it isn't supposed to be changed?) at function scope:

static CString const someImmutableText = <some initializer>;

The placeholder <some initializer> above can be a literal, a function call or any other expression that you can initialize a CString from. The static makes sure the object is only created once and subsequently only initialized once, too.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • The value reading read in is language specific. My program is in or 40 languages. Some of them are partial languages where most of the GUI us English and just a bit is translated (I read these from INI file) and the others are full transloations where I read from resources. So I can't hard code the values in the method. I must read them in but i only wanted to read them in once to save on doing the same thing multiple times. – Andrew Truckle Jul 14 '18 at 14:23
  • 1
    @AndrewTruckle You can make a function that reads from your INI files or resources or whatever you need, then initialize the variable with that. – super Jul 14 '18 at 14:33
1

@Ulrich's answer will of course work fine, but if <some initializer> is non-trivial there is a hidden downside - as of C++11, the compiler is required to generate a threadsafe initialiser.

This has minimal runtime overhead but it does generate quite a lot of code, see at Godbolt, and if you have a lot of these then this can add up.

If there are no multi-threading issues (which generally there aren't, especially in initialisation code), then there is a simple alternative which will eliminate this code. In fact, it's so simple that it's barely worth posting at all, but I'll do it here anyway for completeness. It's just this; please excuse the anglicisms:

static bool initialised;
static Foo *initialise_me;
static Bar *initialise_me_too;
...

if (!initialised)
{
    initialise_me = new Foo (...);
    initialise_me_too = new Bar (...);
    ...
    initialised = true;
}
...

Note that the variables to be initialised are declared as raw pointers here and allocated with new. This is done for a reason - the one thing you most definitely don't want is to call constructors at the point where you declare these variables, else you'll be right back where you started. There are no object lifetime issues because the variables remain in existence for the entire duration of the program, so it's all good.

And, in fact, you don't actually need that bool at all - just test (say) initialise_me against nullptr.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • So the bool is guaranteed to default to false is it ? – Andrew Truckle Jul 15 '18 at 00:00
  • 1
    Of course! Globals and statics are initialised to zero, that's basic stuff. – Paul Sanders Jul 15 '18 at 05:15
  • Is that worth it? Yes, the threadsafe initialization is a bunch of overhead compared to this. However, for one-time initializations, that cost is probably negligible. Also, this approach has a cost, it needs to be written and understood. It may be worth the while though in programs with a short runtime and in resource-constrained environments. – Ulrich Eckhardt Jul 17 '18 at 16:46
  • Paul, apart from the comment above, I would make it more explicit that the destructors of the objects are willingly and knowingly not called. This can have have positive effects (no need to clean up allocated memory before exiting) but also negative ones (missing flush for logfiles, leak-detector being triggered). – Ulrich Eckhardt Jul 17 '18 at 16:48
  • @UlrichEckhardt _Is it worth it_? Well, the code generated is there for all to see at Gidbolt so you can judge for yourself. _I would make it more explicit that the destructors of the objects are willingly and knowingly not called_ I don't really see the need for that so I don't plan to add it. – Paul Sanders Jul 17 '18 at 20:06