What's a good pattern to calculate a variable only when it is used the first time?

C++

C++ Problem Overview


Not an actual problem but I'm looking for a pattern to improve the following logic:

void PrintToGameMasters()
{
    std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(message);
}

This code works, but the issue I have is that in most cases there aren't any gamemasters players so the message composition will be done for nothing.

I'd like to write something that would only create the message on the first use of that variable, but I can't come up with a good solution here.

EDIT: To make this question more precise, I'm looking for a solution that's not specific to strings, it could be a type without a function to test if it's initialized. Also big bonus points if we can keep the call to GetComplicatedDebugMessage at the top of the loop, I think a solution involving a wrapper would solve this.

C++ Solutions


Solution 1 - C++

Whereas std::string has empty value which might mean "not computed", you might use more generally std::optional which handle empty string and non default constructible types:

void PrintToGameMasters()
{
    std::optional<std::string> message;

    for (Player* player : GetAllPlayers()) {
       if (player->IsGameMaster()) {
           if (!message) {
              message = GetComplicatedDebugMessage();
           }
           player->SendMessage(*message);
       }
    }
}

Solution 2 - C++

Use data-oriented design; keep two lists of players: game masters and non-game masters (or all players like you have now + a separate vector of pointers to game-masters only).

void PrintToGameMasters()
{
    auto players = GetGameMasters(); // Returns ONLY game master players
    if (players.begin() != players.end()) {
        std::string message = GetComplicatedDebugMessage();
        for (Player* player : players) {
            player->SendMessage(message);
        }
    }
}

The goal is to minimize if-statements inside loops.

Optimize for the most common case, not the most generic one; the most common case is that a player is not a game master; so avoid looping over them.


P.S. Since you're developing a game, I want to add this link to Mike Acton's cppcon talk which you might find interesting.

Solution 3 - C++

Some good ideas here, but I like to keep it a bit more simple:

void PrintToGameMasters()
{
    std::string message;
    
    for (Player* player : GetAllPlayers())
    {
       if (player->IsGameMaster())
       {
           if (message.empty())
              message = GetComplicatedDebugMessage();
           
           player->SendMessage(message);
       }
    }
}

Everybody can follow this, and it's cheap as chips… plus it's easy as pie to debug.

Solution 4 - C++

You can use std::call_once with a lambda to call the function the first time you find a game master like

void PrintToGameMasters()
{
	std::once_flag of;
	std::string message;
	for (Player* player : GetAllPlayers())
	   if (player->IsGameMaster())
	   {
	       std::call_once(of, [&](){ message = GetComplicatedDebugMessage(); });
		   player->SendMessage(message);
	   }
}

Solution 5 - C++

Wrap the message in a mutable lambda:

auto makeMessage = [message = std::string()]() mutable -> std::string&
{
    if (message.empty()) {
        message = GetComplicatedDebugMessage();
    }
    return message;
};

for (Player* player : GetAllPlayers())
   if (player->IsGameMaster())
       player->SendMessage(makeMessage());

Solution 6 - C++

You can extend the approach of using an std::optional (as in Jarod41's answer) with lazy evaluation on top. This would also fulfill the requirement to "keep the call to GetComplicatedDebugMessage at the top of the loop".

template <typename T>
class Lazy : public std::optional<T> {
public:
    Lazy(std::function<T()> f) : fn(f) { }
    T operator*() {
        if (!*this)
            std::optional<T>::operator=(fn());
        return this->value();
    }
private:
    std::function<T()> fn;
};

void PrintToGameMasters()
{
    Lazy<std::string> message(GetComplicatedDebugMessage);
    for (Player* player : GetAllPlayers())
        if (player->IsGameMaster())
            player->SendMessage(*message);
}

Solution 7 - C++

Not sure if this is the best pattern, but you can delay computation with a lambda:

void PrintToGameMasters()
{
    std::string message = "";
    auto getDebugMessage = [&message]() -> const std::string& { 
        if (message.empty()) {
            message = GetComplicatedDebugMessage();
        }
        return message;
    };

    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(getDebugMessage());
}

Solution 8 - C++

I'm not sure why you want to keep the definition of message above the loop. If someone's reading the code to analyze what happens in the case where std::end(GetAllPlayers())==std::begin(GetAllPlayers)(), you don't want to clutter their mental workspace with irrelevant variables.

If you're willing to give that up, then static is your friend:

void PrintToGameMasters()
{
    for (auto const &player : GetAllPlayers())
        if (player->IsGameMaster())
        {
            //Initialization of a static variable occurs exactly once, even when multithreaded,
            //precisely when the defining line is hit for the first time
            static auto const &message{GetComplicatedDebugMessage()};
            player->SendMessage(message);
        }
}

Solution 9 - C++

This is literally one of the things std::future is designed to solve:

void PrintToGameMasters()
{
    auto message = std::async(
        std::launch::deferred,
        []{return GetComplicatedDebugMessage();}
    );
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(message.get());
}

Invoking std::async with std::launch::deferred causes the task to be “executed on the calling thread the first time its result is requested”.

Solution 10 - C++

This works. As the MIT license would put it:

> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED

#include <Windows.h>
#include <cstdlib>
#include <cstdio>
#include <string>

struct Player {
	bool isGameMaster;
	int id;
};

int __stdcall IsGameMaster(Player* self) {
	return self->isGameMaster ? 1 : 0;
}

// Could've been "SendMessage"... but Windows.h
void __stdcall SendMessageToPlayer(Player* self, std::string* msg) {
	printf("Player %d says: %s\n", self->id - 1000 + 1, msg->c_str());
}

Player g_players[18];
Player* __stdcall GetAllPlayers(void){
	return &g_players[0];
}

std::string le_message = "hi, I'm a game master";
std::string* __stdcall GetComplicatedMessage(void) {
	puts("GENERATING COMPLICATED MESSAGE. HOGGING CPU FOR 3 DAYS!");
	return &le_message; // to make my assembly life easier
}

__declspec(naked) void PrintToGameMasters(void){
	__asm {
		push ebp;
		mov ebp, esp;
		sub esp, 8;
		
		call GetAllPlayers;
		mov [ebp-4], eax;
		
		// this is 'i', the loop iteration counter
		// I chose esi because it is preserved by stdcalls
		xor esi, esi;

		do_loop:

		// Player* player = &g_players[i];
		mov ebx, esi;
		imul ebx, SIZE Player;
		add ebx, [ebp-4]; // ebx = g_players + sizeof(Player) * i, or &g_players[i]

		// if (player->IsGameMaster()) {
		push ebx;
		call IsGameMaster;
		test eax, eax;
		jz no_print;

		// msg = GetComplicatedMessage();
		get_msg_start:
		call GetComplicatedMessage;
		mov [ebp-8], eax;
		jmp short delete_self;
		get_msg_end:

		// player->SendMessage(msg);
		push [ebp-8];
		push ebx;
		call SendMessageToPlayer;

		// }
		no_print:
		inc esi;
		cmp esi, 18;
		jb do_loop;

		mov esp, ebp;
		pop ebp;
		ret;

		delete_self:
		mov ecx, get_msg_start;
		mov eax, get_msg_end;

		sub eax, ecx;
		mov byte ptr [ecx], 0xEB; // jmp short
		mov byte ptr [ecx+1], al; // relative offset
		jmp get_msg_end;
	}
}

int main(){
	for (int i = 0; i < 18; i++) {
		g_players[i].isGameMaster = (i == 12 || i == 15); // players 13 and 16
		g_players[i].id = i + 1000;
	}

	DWORD oldProtect;
	VirtualProtect(&PrintToGameMasters, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);

	PrintToGameMasters();

	return 0;
}

code working as intended

It is much faster than the if (!message) message = GetMessage() approach, unless you have a CPU with a branch predictor (which you likely do). In that case, it's slower (or maybe equally fast, but not faster), uglier, less portable, and will likely get you killed by a psychopath.

Solution 11 - C++

You can use a custom local type with a conversion operator:

void PrintToGameMasters()
{
    struct {
        operator std::string const &(void)
        {
            static auto const real_value{GetComplicatedDebugMessage()};
            return real_value;
        }
    } message;
    for (auto const &player : GetAllPlayers())
       if (player->IsGameMaster())
           player->SendMessage(message);
}

Of course, this assumes that GetComplicatedDebugMessage is in fact stateless. Otherwise, you'll need to resort to capturing a lambda, or similar tricks described in the other answers here.

Solution 12 - C++

Really hope this helps

Try maybe implementing this logic:

#include <iostream>

using namespace std;

int main()
{
    bool GameMaster,first_time,used_before;
    
    GameMaster = true;
    first_time = false;
    used_before = false;
    
    GameMaster = first_time;
    first_time = used_before;
    
     
    for( int i = 0; i < 5; i++ ) {
         
        if(GameMaster==used_before) {
            cout<<"    First Time";
            GameMaster = true;
        }
            
        if(GameMaster!=used_before) {
            cout<<"    Old News";
        }
    }

    return 0;
}

With Response:

  First Time    Old News    Old News    Old News    Old News    Old News 

Solution 13 - C++

static variables are initialized on first time through. So:

void PrintToGameMasters()
{
    for (Player* player : GetAllPlayers())
       if (player->IsGameMaster()) {
           static std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info
           player->SendMessage(message);
       }
}

This is, of course, assuming that calculating the value once at most (rather than once per call at most) is a valid way of proceeding. It's not clear from how you phrased your task whether this is the case.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKelnoView Question on Stackoverflow
Solution 1 - C++Jarod42View Answer on Stackoverflow
Solution 2 - C++rustyxView Answer on Stackoverflow
Solution 3 - C++Lightness Races in OrbitView Answer on Stackoverflow
Solution 4 - C++NathanOliverView Answer on Stackoverflow
Solution 5 - C++Nikos C.View Answer on Stackoverflow
Solution 6 - C++Christoph FreundlView Answer on Stackoverflow
Solution 7 - C++0x5453View Answer on Stackoverflow
Solution 8 - C++Jacob ManakerView Answer on Stackoverflow
Solution 9 - C++Konrad RudolphView Answer on Stackoverflow
Solution 10 - C++MartinView Answer on Stackoverflow
Solution 11 - C++Jacob ManakerView Answer on Stackoverflow
Solution 12 - C++Andrei ElekesView Answer on Stackoverflow
Solution 13 - C++user11939662View Answer on Stackoverflow