UPD: Вопрос решён. Помогла функция glutTimerFunc(), убираем glutPostRedisplay() из idle-функции, создаём таймер с обновлением в 1000/FPS_LIMIT миллисекунд и создаём новый таймер в том таймере, тем самым таймер создаёт другой таймер и так по кругу.
std::vector<std::string>, а std::set<std::string> и все те же две std::unordered_map<std::string_view, std::string_view>. Set позаботится о том, чтобы хранить только уникальные строки. У std::set, кстати, тоже есть функция emplace. std::map, а std::unordered_nap. Тебе стоит самостоятельно понять принципиальную разницу этих контейнеров и почему первый в твоей задаче непригоден.std::unordered_map<std::string, std::string>. Первый контейнер хранит прямой перевод, второй - обратный перевод. Контейнеров два т.к. слова в разных языках могут писаться одинаково, а значения у них могут быть разные и переводы, следовательно, тоже разные.emplace. Изучи документацию и найди для себя обоснование - почему именно эта функция будет эффективнее всего работать с парой std::string_view в качестве параметров.std::string в качестве параметров шаблона std::unordered_nap таково, что TranslateForward возвращает std::string_view, который не владеет памятью на которую ссылается. Вместе с этим, тип std::string_view не рассматривается как тип долгосрочного хранения (рассматривать его как долгосрочное хранилище - в большинстве случаев опасно). Такая сигнатура требует от Translator владеть памятью строк возвращаемых из функций Translate*. В самой задаче использование string_view оправдано экономией памяти.
Translator::Add(string("first"), string("second")) - первое выделение делается вот тут. Зачем?data.emplace_back(first) - второе выделение делается вот тут и ниже во втором emplace_back.std::string_view в качестве типа параметров твоей функции?srand((unsigned)time(NULL)); - в течении секунды твое зерно будет одинаковым, а следовательно и все нарисованные точки тоже будут одинаковыми. Функция time возвращает текущее количество секунд в UTC формате. Тебе не стоило писать эту строчку в displayFunction, тебе стоило написать ее в main точно перед glutInit.glutPostRedisplay внутри idle-функции - это правильно, так в GLUT и поступает запрос на перерисовку кадра. Что мне надо сделать? - Отрыть файл.
Какой файл открыть? - Значит или путь файла известен внутри функции, или передается параметром.
Для чего открыть файл: для записи или для чтения? - Видимо или функция должна своими именем об этом говорить, или режим работы с файлом надо передавать параметром.
А что делать с дескриптором открытого в функции файла? - Возвращать, пожалуй.
А что если файл не удалось открыть? - Наверное надо предусмотреть обработку ошибок и возвращать недействительный дескриптор файла.
Зачем мне нужен дескриптор открытого файла? - Чтобы считать оттуда что-либо или записать туда что-либо.
Что мне надо сделать чтобы считать из файла что-либо? - ...
Что мне надо сделать чтобы записать в файл что-либо? - ...
Что мне делать с дескриптором открытого файла когда он больше не нужен? - ...
Но вот вынести в отдельные функции Создание файла, Запись данных и Считывание данных не могу.