Третий аргумент main
#новичкам
Почти всегда вы пишите функцию main вот так:
Если вы пишите утилиту командной строки или просто хотите познать боль, то вам нужно парсить аргументы командной строки. В этом случае вы определяете main вот так:
Однако в стандарте описан еще один способ определения main:
Стандарт разрешает компиляторам давать возможность пользователям как-то по-другому задавать аргументы для main. И хоть это будет не переносимо, нам не всегда нужна кроссплатформенность.
Самая часто встречающаяся нестандартная сигнатура main следующая:
Третий аргумент main - это массив строк переменных окружения в формате "KEY=value". Массив завершается null pointer'ом.
Программа получает копию переменных окружения родительского процесса (например, терминала или скрипта). Только лишь независимую копию: изменение набора и значения переменных снаружи программы никак не влияет на то, что происходит внутри нее.
Вот минимальный примерчик:
Возможный вывод:
Вы не так часто можете увидеть этот формат сигнатуры main по уже очевидным для вас причинам:
- нестандарт
- а самое главное - это дело надо парсить. Засовывать в мапу какую-то и искать потом по ключу нужную переменную. А зачем, если есть std::getenv или его брат getenv из Сей.
Рассказываю про это, чтобы вы при встрече в таким форматом аргументов main не думали, что что-то базовое упустили при изучении плюсов. Ну или просто для расширения кругозора)
Expand your horizons. Stay cool.
#NONSTANDARD #goodoldc
#новичкам
Почти всегда вы пишите функцию main вот так:
int main() {
// some code
}
Если вы пишите утилиту командной строки или просто хотите познать боль, то вам нужно парсить аргументы командной строки. В этом случае вы определяете main вот так:
int main (int argc, char* argv[]) {
// argc - количество переданных аргументов
// argv - массив из переданных аргументов
// some parsing
}
Однако в стандарте описан еще один способ определения main:
int main(/ implementation-defined /) {body}
Стандарт разрешает компиляторам давать возможность пользователям как-то по-другому задавать аргументы для main. И хоть это будет не переносимо, нам не всегда нужна кроссплатформенность.
Самая часто встречающаяся нестандартная сигнатура main следующая:
int main(int argc, char* argv[], char* envp[]) {}
Третий аргумент main - это массив строк переменных окружения в формате "KEY=value". Массив завершается null pointer'ом.
Программа получает копию переменных окружения родительского процесса (например, терминала или скрипта). Только лишь независимую копию: изменение набора и значения переменных снаружи программы никак не влияет на то, что происходит внутри нее.
Вот минимальный примерчик:
#include <iostream>
int main(int argc, char* argv[], char* envp[]) {
std::cout << "Environment variables:\n";
for (char** env = envp; *env != nullptr; env++) {
std::cout << *env << "\n";
}
return 0;
}
Возможный вывод:
PATH=/usr/local/bin:/usr/bin:/bin
USER=grokaem_cpp
...
Вы не так часто можете увидеть этот формат сигнатуры main по уже очевидным для вас причинам:
- нестандарт
- а самое главное - это дело надо парсить. Засовывать в мапу какую-то и искать потом по ключу нужную переменную. А зачем, если есть std::getenv или его брат getenv из Сей.
Рассказываю про это, чтобы вы при встрече в таким форматом аргументов main не думали, что что-то базовое упустили при изучении плюсов. Ну или просто для расширения кругозора)
Expand your horizons. Stay cool.
#NONSTANDARD #goodoldc
👍46❤18🔥4😱4
environ
#опытным
В POSIX-совместимых системах (Linux, macOS, BSD) существует еще один альтернатива параметру
В сущности, у нее такой же функционал, что и у envp, только эту переменную видно из любой единицы трансляции, так как это глобальная переменная, имеющая внешнюю линковку.
В остальном, особенности работы такие же, как и у envp.
Но если мы уже зашли по колено в Posix, то там есть функция setenv, которая позволяет менять переменные окружения уже в ходе выполнения программы:
И эти изменения будут видеть все ранее оговоренные методы получения значений env переменных. Однако это все не тредсейф и нужна защита в виде мьютексов.
Не встречал кейсов изменения переменных окружения. Наверное, это можно использовать, как костыльный механизм общения между модулями программы. Если у кого есть достойные примеры применения setenv, черканите в комментах, буду благодарен.
Интересно, как много в С|С++ методов получения окружения процесса. Но рекомендуется использовать конечно стандартный вариант std::getenv.
Be visiable. Stay cool.
#NONSTANDARD
#опытным
В POSIX-совместимых системах (Linux, macOS, BSD) существует еще один альтернатива параметру
envp
в функции main() и функции std::getenv()
для получения значений переменных окружения. Это глобальная переменная extern char** environ
, которая предоставляет прямой доступ ко всему окружению процесса.В сущности, у нее такой же функционал, что и у envp, только эту переменную видно из любой единицы трансляции, так как это глобальная переменная, имеющая внешнюю линковку.
#include <stdio.h>
extern char** environ;
void foo() {
for(char** env = environ; *env != NULL; env++) {
printf("%s\n", *env);
}
}
В остальном, особенности работы такие же, как и у envp.
Но если мы уже зашли по колено в Posix, то там есть функция setenv, которая позволяет менять переменные окружения уже в ходе выполнения программы:
#include <stdlib.h>
int setenv(const char *envname, const char *envval, int overwrite);
И эти изменения будут видеть все ранее оговоренные методы получения значений env переменных. Однако это все не тредсейф и нужна защита в виде мьютексов.
Не встречал кейсов изменения переменных окружения. Наверное, это можно использовать, как костыльный механизм общения между модулями программы. Если у кого есть достойные примеры применения setenv, черканите в комментах, буду благодарен.
Интересно, как много в С|С++ методов получения окружения процесса. Но рекомендуется использовать конечно стандартный вариант std::getenv.
Be visiable. Stay cool.
#NONSTANDARD
20❤15👍7🔥7
Офер в Яндекс за 48 часов: ищем бэкендеров
В команду нужны опытные бэкенд-разработчики на C++, Python, Java и Go. Приглашаем на Мультитрек — онлайн-программу быстрой адаптации.
Всего за 2 дня вы можете получить офер:
• До 18 августа подать заявку и пройти предварительный отбор
• 23 августа решить задачи на технических секциях
• 24 августа пройти финальное собеседование и получить офер
После этого будет возможность поработать с тремя командами и выбрать проект по душе.
Создаём технологии, которые меняют мир. Присоединяйтесь! Оставляйте заявку на сайте.
В команду нужны опытные бэкенд-разработчики на C++, Python, Java и Go. Приглашаем на Мультитрек — онлайн-программу быстрой адаптации.
Всего за 2 дня вы можете получить офер:
• До 18 августа подать заявку и пройти предварительный отбор
• 23 августа решить задачи на технических секциях
• 24 августа пройти финальное собеседование и получить офер
После этого будет возможность поработать с тремя командами и выбрать проект по душе.
Создаём технологии, которые меняют мир. Присоединяйтесь! Оставляйте заявку на сайте.
👎13❤8🔥4👍3😁3🗿2
Ревью
#новичкам
Пролистывал намедни один тг канал по С++, его название начинается на Senior и заканчивается на С++ Developer. Там обычно постится очень "качественный контент" и мне на глаза попался код, который я бы хотел закинуть вам на тряпкозакидательство.
В рамках #ревью мы приводим кусок кода, а вы в комментариях прожариваете его до полного well done: говорите, что вам не нравится, и как это исправить. Комментатора с самым большим количеством отмеченных проблем упомянем в завтрашнем посте с разбором.
Вот такой код. Под оригинальным постом с этим кодом коллеги призвали руки рубить за такой код. Давайте сделаем так, чтобы он не вызывал испанского стыда, а только возвышенные чувства платонической любви к С++.
Раз, два, три, код в порядок приведи!
Critique your decisions. Stay cool.
#новичкам
Пролистывал намедни один тг канал по С++, его название начинается на Senior и заканчивается на С++ Developer. Там обычно постится очень "качественный контент" и мне на глаза попался код, который я бы хотел закинуть вам на тряпкозакидательство.
В рамках #ревью мы приводим кусок кода, а вы в комментариях прожариваете его до полного well done: говорите, что вам не нравится, и как это исправить. Комментатора с самым большим количеством отмеченных проблем упомянем в завтрашнем посте с разбором.
struct Message {
int data;
};
std::queue<Message *> msgQueue;
void sender() {
for (int i = 0; i < 20; ++i) {
Message *msg = new Message();
msg->data = i;
msgQueue.push(msg);
std::cout << "Sent: " << msg->data << std::endl;
}
}
void receiver() {
while (true) {
if (msgQueue.empty()) {
break;
}
Message *msg = msgQueue.front();
msgQueue.pop();
std::cout << "Received: " << msg->data << std::endl;
delete msg;
}
}
int main() {
std::thread t1(sender);
std::thread t2(receiver);
t1.join();
t2.join();
return 0;
}
Вот такой код. Под оригинальным постом с этим кодом коллеги призвали руки рубить за такой код. Давайте сделаем так, чтобы он не вызывал испанского стыда, а только возвышенные чувства платонической любви к С++.
Раз, два, три, код в порядок приведи!
Critique your decisions. Stay cool.
1❤24😱8🔥5👍4👎2😁2
Разбор ревью
#новичкам
Большое спасибо всем участникам ревью, которые проявили активность под предыщущим постом. Всем и каждому посылаем лучи благодарности!
Было непросто выбрать самый эффективный по критике комментарий, потому что некоторые люди предлагали странные решения. В итоге, мы выбрали @seweeex и вот его коммент. Давайте похлопаем ему 👏👏👏.
Теперь к сути. В этом коде не так уж и много проблем, просто они жирные и явно бросаются в глаза.
Поехали разбирать.
🔞 Зачем-то в очереди хранятся сырые указатели. Смысла в этом особого нет, кроме как подложить себе свинью на будущее и поджечь жёпы комментаторов. Тут даже умные указатели не нужны, зачем дополнительные аллокации? В очереди можно хранить сами объекты и никаких проблем с менеджментом памяти не будет.
🔞 Использование сырых указателей приводит например к тому, что при вылете исключения из метода push, произойдет утечка памяти. Да, элементов мы закидываем в очередь немного, но концептуально проблема есть. Решается это опять же через хранение обычных объектов.
🔞 Слон в посудной лавке здесь - это конечно отсутствие синхронизации на очереди. Это в принципе ub и дальше не о чем говорить. Нужна не стандартная, а потокобезопасная очередь.
Очередь должна быть блокирующей, чтобы не тратить активно ресурс CPU на ожидание нового сообщения. Это решается с помощью кондвара.
🔞 Ресивер может зашедулиться раньше сендера, увидит пустую очередь и выйдет из цикла, не обработав ни одной задачи. Поэтому нужна система сигнализации: очередь должна ждать прихода новых задач, пока ей не скажут, что больше задач нет.
🔞 Бесконечный цикл в ресивере выглядит не очень. Если можно не писать бесконечных циклов и не вставлять брейки, то лучше этого не делать. break и continue усложняют понимание кода. Лучше перенести забирание элемента из очереди прям в шапку цикла.
🔞 Гонка на потоконебезопасном логировании. Нужен мьютекс, чтобы сообщения не интерферировали.
По сути все. Главное изменение - вынесение блокирующей потокобезопасной очереди в отдельный шаблонный класс, который хранит объекты типа Т. С шаблонами можно долго играться и далеко зайти, поэтому приведем самую простую реализацию, которая справляется со своими задачами в данном кейсе, но может быть улучшена для корректной работы с самыми разными типами:
Пишите свои дополнения, если что-то забыли.
Make things better. Stay cool.
#новичкам
Большое спасибо всем участникам ревью, которые проявили активность под предыщущим постом. Всем и каждому посылаем лучи благодарности!
Было непросто выбрать самый эффективный по критике комментарий, потому что некоторые люди предлагали странные решения. В итоге, мы выбрали @seweeex и вот его коммент. Давайте похлопаем ему 👏👏👏.
Теперь к сути. В этом коде не так уж и много проблем, просто они жирные и явно бросаются в глаза.
Поехали разбирать.
🔞 Зачем-то в очереди хранятся сырые указатели. Смысла в этом особого нет, кроме как подложить себе свинью на будущее и поджечь жёпы комментаторов. Тут даже умные указатели не нужны, зачем дополнительные аллокации? В очереди можно хранить сами объекты и никаких проблем с менеджментом памяти не будет.
🔞 Использование сырых указателей приводит например к тому, что при вылете исключения из метода push, произойдет утечка памяти. Да, элементов мы закидываем в очередь немного, но концептуально проблема есть. Решается это опять же через хранение обычных объектов.
🔞 Слон в посудной лавке здесь - это конечно отсутствие синхронизации на очереди. Это в принципе ub и дальше не о чем говорить. Нужна не стандартная, а потокобезопасная очередь.
Очередь должна быть блокирующей, чтобы не тратить активно ресурс CPU на ожидание нового сообщения. Это решается с помощью кондвара.
🔞 Ресивер может зашедулиться раньше сендера, увидит пустую очередь и выйдет из цикла, не обработав ни одной задачи. Поэтому нужна система сигнализации: очередь должна ждать прихода новых задач, пока ей не скажут, что больше задач нет.
🔞 Бесконечный цикл в ресивере выглядит не очень. Если можно не писать бесконечных циклов и не вставлять брейки, то лучше этого не делать. break и continue усложняют понимание кода. Лучше перенести забирание элемента из очереди прям в шапку цикла.
🔞 Гонка на потоконебезопасном логировании. Нужен мьютекс, чтобы сообщения не интерферировали.
По сути все. Главное изменение - вынесение блокирующей потокобезопасной очереди в отдельный шаблонный класс, который хранит объекты типа Т. С шаблонами можно долго играться и далеко зайти, поэтому приведем самую простую реализацию, которая справляется со своими задачами в данном кейсе, но может быть улучшена для корректной работы с самыми разными типами:
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <optional>
#include <print>
struct Message {
int data;
};
template<typename T>
class ThreadSafeQueue {
public:
void push(T msg) {
{
std::lock_guard lock(mutex_);
queue_.push(std::move(msg));
}
cv_.notify_one();
}
std::optional<T> pop() {
std::unique_lock lock(mutex_);
cv_.wait(lock, [this] { return !queue_.empty() || stopped_; });
if (stopped_ && queue_.empty()) {
return std::nullopt;
}
auto msg = std::move(queue_.front());
queue_.pop();
return msg;
}
void stop_receive() {
stopped_ = true;
cv_.notify_all();
}
private:
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable cv_;
std::atomic<bool> stopped_ = false;
};
void println(const std::string& str) {
static std::mutex mtx;
std::lock_guard lock(mtx);
std::cout << str << std::endl;
}
void sender(ThreadSafeQueue<Message>& msgQueue) {
for (int i = 0; i < 20; ++i) {
auto msg = Message(i);
println("Sent: " + std::to_string(msg.data));
msgQueue.push(std::move(msg));
}
msgQueue.stop_receive();
}
void receiver(ThreadSafeQueue<Message>& msgQueue) {
while (auto msg = msgQueue.pop()) {
println("Received: " + std::to_string(msg.value().data));
}
}
int main() {
ThreadSafeQueue<Message> msgQueue;
std::thread t1(sender, std::ref(msgQueue));
std::thread t2(receiver, std::ref(msgQueue));
t1.join();
t2.join();
}
Пишите свои дополнения, если что-то забыли.
Make things better. Stay cool.
16❤28👍8👏5🤨1
В России можно посещать IT-мероприятия хоть каждый день: как оффлайн, так и онлайн
Но где их находить? Как узнавать о них раньше, чем когда все начнут выкладывать фотографии оттуда?
Переходите на канал IT-Мероприятия России. В нём каждый день анонсируются мероприятия со всех городов России
📆 в канале размещаются как онлайн, так и оффлайн мероприятия;
👩💻 можно найти ивенты по любому стеку: программирование, frontend-backend разработка, кибербезопасность, дата-аналитика, osint, devops и другие;
🎙 разнообразные форматы мероприятий: митапы с коллегами по цеху, конференции и вебинары с известными опытными специалистами, форумы и олимпиады от важных представителей индустрии и многое другое
А чтобы не искать по разным форумам и чатам новости о предстоящих ивентах:
🚀 IT-мероприятия России — подписывайся и будь в курсе всех предстоящих мероприятий!
Но где их находить? Как узнавать о них раньше, чем когда все начнут выкладывать фотографии оттуда?
Переходите на канал IT-Мероприятия России. В нём каждый день анонсируются мероприятия со всех городов России
📆 в канале размещаются как онлайн, так и оффлайн мероприятия;
👩💻 можно найти ивенты по любому стеку: программирование, frontend-backend разработка, кибербезопасность, дата-аналитика, osint, devops и другие;
🎙 разнообразные форматы мероприятий: митапы с коллегами по цеху, конференции и вебинары с известными опытными специалистами, форумы и олимпиады от важных представителей индустрии и многое другое
А чтобы не искать по разным форумам и чатам новости о предстоящих ивентах:
🚀 IT-мероприятия России — подписывайся и будь в курсе всех предстоящих мероприятий!
😁5❤4👍3🔥1😢1
or and not
#новичкам
В С/C++ всегда был не очень дружелюбный синтаксис операторов. Показать вот такой код человеку, который не в зуб ногой в программировании:
есть вероятность, что он подумает, что его прокляли шаманы тумба-юмба.
Однако знали ли вы, что в С/C++ есть альтернативный синтаксис токенов? Символы операторов заменяются на короткие слова и код выше становится почти питонячьим:
Выглядит свежо! Хотя было доступно еще с С++98.
Вот полный список альтернативных токенов:
Последние токены для скобок - это конечно дичь. Но остальные - вполне интересные варианты.
В сях альтернативные токены были введены в С95, поэтому до этого момента токенов не было в языке. Но даже с их введением все продолжали использовать привычный синтаксис. Видимо поэтому мы так до сих пор и остались на уровне наскальной живописи.
А вы используете в продакшен коде альтернативные токены?
Evolve. Stay cool.
#fun #goodoldc
#новичкам
В С/C++ всегда был не очень дружелюбный синтаксис операторов. Показать вот такой код человеку, который не в зуб ногой в программировании:
if (x > 0 && y < 10 || !z) {
// ...
}
есть вероятность, что он подумает, что его прокляли шаманы тумба-юмба.
Однако знали ли вы, что в С/C++ есть альтернативный синтаксис токенов? Символы операторов заменяются на короткие слова и код выше становится почти питонячьим:
if (x > 0 and y < 10 or not z) {
// ...
}
Выглядит свежо! Хотя было доступно еще с С++98.
Вот полный список альтернативных токенов:
&& - and
&= - and eq
& - bitand
| - bitor
~ - compl
!= - not eq
|| - or
|= - or eq
^ - xor
^= - xor eq
{ - <%
} - %>
[ - <:
] - :>
# - %:
## - %:%:
Последние токены для скобок - это конечно дичь. Но остальные - вполне интересные варианты.
В сях альтернативные токены были введены в С95, поэтому до этого момента токенов не было в языке. Но даже с их введением все продолжали использовать привычный синтаксис. Видимо поэтому мы так до сих пор и остались на уровне наскальной живописи.
А вы используете в продакшен коде альтернативные токены?
Evolve. Stay cool.
#fun #goodoldc
❤9🔥5👍4