Используйте утверждение границы слова \b
:
/\bme\b/
Принимая во внимание, что std::string
имеет пустое значение, которое могло бы означать "не вычисленный", Вы могли бы использовать в более общем плане std::optional
, которые обрабатывают пустую строку и не конструируемые типы по умолчанию:
void PrintToGameMasters()
{
std::optional<std::string> message;
for (Player* player : GetAllPlayers()) {
if (player->IsGameMaster()) {
if (!message) {
message = GetComplicatedDebugMessage();
}
player->SendMessage(*message);
}
}
}
Используйте ориентированный на данные на дизайн ; сохраните два списка плееров: игровые ведущие устройства и неигровые ведущие устройства (или все плееры как Вы имеют теперь + отдельный вектор указателей только на преподавателей физкультуры).
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);
}
}
}
цель состоит в том, чтобы минимизировать if
- операторы в циклах.
Оптимизируют для наиболее распространенного случая, не самого универсального; наиболее распространенный случай - то, что игрок не является игровым ведущим устройством; поэтому избегайте цикличного выполнения по ним.
<час>P.S., Так как Вы разрабатываете игру, я хочу добавить эту ссылку Mike Acton разговор о cppcon , который Вы могли бы найти интересным.
Можно использовать std::call_once
с лямбдой для вызывания функции в первый раз, когда Вы находите игровое ведущее устройство как
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);
}
}
Некоторые хорошие идеи здесь, но мне нравится сохранять это немного более простым:
void PrintToGameMasters()
{
std::string message;
for (Player* player : GetAllPlayers())
{
if (player->IsGameMaster())
{
if (message.empty())
message = GetComplicatedDebugMessage();
player->SendMessage(message);
}
}
}
Все могут следовать за этим, и это дешево как chips… плюс он легок как круг отладить.
Перенесите сообщение в изменяемую лямбду:
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());
Не уверенный, если это - лучший шаблон, но можно задержать вычисление с лямбдой:
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());
}
Можно расширить подход использования std::optional
(как в ответе Jarod41) с отложенными вычислениями на вершине. Это также выполнило бы требование для "хранения вызова к GetComplicatedDebugMessage
наверху цикла".
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);
}
Я не уверен, почему Вы хотите сохранить определение message
выше цикла. Если чье-то чтение кода для анализа, что происходит в случае, где std::end(GetAllPlayers())==std::begin(GetAllPlayers)()
, Вы не хотите создавать помехи их умственной рабочей области несоответствующими переменными.
, Если Вы готовы сдаться, это, затем static
является Вашим другом:
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);
}
}
Это работает. Как лицензия MIT выразилась бы:
ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЕНО "AS", БЕЗ ГАРАНТИИ ЛЮБОГО ДОБРОГО, ЯВНОГО ИЛИ ПОДРАЗУМЕВАЕМОГО
#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;
}
, Это намного быстрее , чем эти if (!message) message = GetMessage()
подход, если у Вас нет ЦП с предиктором ответвления (который Вы, вероятно, делаете). В этом случае это медленнее (или возможно одинаково быстро, но не быстрее), более ужасно, менее портативно, и будет, вероятно уничтожать Вас психопатом .
Это - буквально одна из вещей std::future
, разработан для решения:
void PrintToGameMasters()
{
auto message = std::async(
std::launch::deferred,
[]{return GetComplicatedDebugMessage();}
);
for (Player* player : GetAllPlayers())
if (player->IsGameMaster())
player->SendMessage(message.get());
}
Вызов std::async
with std::launch::deferred
причины задача быть “executed на вызывающем потоке в первый раз его результатом является requested”.
Можно использовать пользовательский локальный тип с оператором преобразования:
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);
}
, Конечно, это предполагает, что GetComplicatedDebugMessage
является на самом деле не сохраняющим состояние. Иначе необходимо будет обратиться к получению лямбды или подобных приемов, описанных в других ответах здесь.
Действительно надежда это помогает
Попытка, возможно, реализовывая эту логику:
#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;
}
С Ответом:
First Time Old News Old News Old News Old News Old News
static
переменные инициализируются на первом разе через. Так:
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);
}
}
Это, конечно, предполагает, что вычисление значения однажды самое большее (а не однажды на вызов самое большее) является допустимым способом продолжиться. Не ясно из того, как Вы формулировали свою задачу ли дело обстоит так.