Сообщение Вопрос целесообразности обобщения от 05.07.2023 13:43
Изменено 05.07.2023 14:17 Sm0ke
Вопрос целесообразности обобщения
Начну пожалуй с конкретной задачи, которая вывела меня на эти размышления.
Неспешно делаю парсер своего яп. В отличии от си++ в языке нет требования на forward declaration. И нет глобальных переменных.
Есть модули, в них есть модульные переменные (aka свойства модуля).
При инициализации переменной модуля в выражении может быть обращение к другой переменной, которая ещё не была определена.
В этом случае подставляется временный номер, а указатель на структуру, которая её хранит кладётся в специальную пачку неизвестных переменных.
Когда будет встречена новая переменная — сперва ей присваивается номер, затем происходит проверка есть ли её имя в той спец пачке.
Если есть, то по всем собранным указателям к имени этой переменной устанавливается актуальный номер, и её имя вычеркивается из пачки неизвестных.
Когда все модули будут загружены, то смотрим: остались ли в спец пачке неизвестные переменные, чтобы выдать при этом лог по ним.
Как лучше организовать пачку? Я думаю взять готовый std::map
Заглянем в доки std::map, и видим — там value_type это std::pair со свойствами first, second (вроде запомнить не сложно)
Метод для добавления элемента в мапу try_emplace() возвращает другой std::pair с итератором и флагом, было ли добавлено.
Согласитесь, что читателю кода было бы нагляднее видеть в качестве value_type структуру со свойствами (key, value).
А метод try_emplace() вместо std::pair<iterator, bool> лучше бы вернул структуру со свойствами (it, was_added).
Красота!
Структура std::pair имеет свойства (first, second), и для неё определены операторы сравнения.
Почему бы не взять её как ключ в std::map ?
Или же лучше определить новую структуру со свойствами (module_name, var_name) ??
Тем более конструктор в структуре std::pair не всегда нужен. Без него компиляция должна быть быстрее.
А 20-ый стандарт упрощает определение операторов сравнения для структур.
(Напомню, что ключ для std::map должен быть сравнимым).
? Наглядность кода, или обобщение ?
? заводить ли новую структуру под конкретную цель каждый раз, или взять готовый pair ?
Кстати ещё вопрос: В своих проектах вы используете std::tuple (казалось бы норм тема) ?
И кстати, я стараюсь не использовать auto в си++ (тут он лишь для упрощения).
Неспешно делаю парсер своего яп. В отличии от си++ в языке нет требования на forward declaration. И нет глобальных переменных.
Есть модули, в них есть модульные переменные (aka свойства модуля).
При инициализации переменной модуля в выражении может быть обращение к другой переменной, которая ещё не была определена.
В этом случае подставляется временный номер, а указатель на структуру, которая её хранит кладётся в специальную пачку неизвестных переменных.
Когда будет встречена новая переменная — сперва ей присваивается номер, затем происходит проверка есть ли её имя в той спец пачке.
Если есть, то по всем собранным указателям к имени этой переменной устанавливается актуальный номер, и её имя вычеркивается из пачки неизвестных.
Когда все модули будут загружены, то смотрим: остались ли в спец пачке неизвестные переменные, чтобы выдать при этом лог по ним.
Как лучше организовать пачку? Я думаю взять готовый std::map
Заглянем в доки std::map, и видим — там value_type это std::pair со свойствами first, second (вроде запомнить не сложно)
Метод для добавления элемента в мапу try_emplace() возвращает другой std::pair с итератором и флагом, было ли добавлено.
my_map v_map;
auto res = v_map.try_emplace(v_key, v_arg);
if( res.second ) { // second -- flag
res.first->second.some_method(); // first -- iterator // second -- значение
}
Согласитесь, что читателю кода было бы нагляднее видеть в качестве value_type структуру со свойствами (key, value).
А метод try_emplace() вместо std::pair<iterator, bool> лучше бы вернул структуру со свойствами (it, was_added).
auto res = v_map.try_emplace(v_key, v_arg);
if( res.was_added ) {
res.it->value.some_method();
}
Красота!
Структура std::pair имеет свойства (first, second), и для неё определены операторы сравнения.
Почему бы не взять её как ключ в std::map ?
using my_key = std::pair<std::string, std::string>; // first -- имя модуля, second -- имя переменной
using my_map = std::map<my_key, my_var_info>;
Или же лучше определить новую структуру со свойствами (module_name, var_name) ??
Тем более конструктор в структуре std::pair не всегда нужен. Без него компиляция должна быть быстрее.
А 20-ый стандарт упрощает определение операторов сравнения для структур.
(Напомню, что ключ для std::map должен быть сравнимым).
struct my_key {
std::string
module_name,
var_name;
friend bool operator == (const my_key &, const my_key &) = default;
friend auto operator <=> (const my_key &, const my_key &) = default;
};
auto res = v_map.try_emplace({mod_name, var_name});
res.it->key.module_name // против res.first->first.first
? Наглядность кода, или обобщение ?
? заводить ли новую структуру под конкретную цель каждый раз, или взять готовый pair ?
Кстати ещё вопрос: В своих проектах вы используете std::tuple (казалось бы норм тема) ?
И кстати, я стараюсь не использовать auto в си++ (тут он лишь для упрощения).
Вопрос целесообразности обобщения
Начну пожалуй с конкретной задачи, которая вывела меня на эти размышления.
Неспешно делаю парсер своего яп. В отличии от си++ в языке нет требования на forward declaration. И нет глобальных переменных.
Есть модули, в них есть модульные переменные (aka свойства модуля).
При инициализации переменной модуля в выражении может быть обращение к другой переменной, которая ещё не была определена.
В этом случае подставляется временный номер, а указатель на структуру, которая её хранит кладётся в специальную пачку неизвестных переменных.
Когда будет встречена новая переменная — сперва ей присваивается номер, затем происходит проверка есть ли её имя в той спец пачке.
Если есть, то по всем собранным указателям к имени этой переменной устанавливается актуальный номер, и её имя вычеркивается из пачки неизвестных.
Когда все модули будут загружены, то смотрим: остались ли в спец пачке неизвестные переменные, чтобы выдать при этом лог по ним.
Как лучше организовать пачку? Я думаю взять готовый std::map
Заглянем в доки std::map, и видим — там value_type это std::pair со свойствами first, second (вроде запомнить не сложно)
Метод для добавления элемента в мапу try_emplace() возвращает другой std::pair с итератором и флагом, было ли добавлено.
Согласитесь, что читателю кода было бы нагляднее видеть в качестве value_type структуру со свойствами (key, value).
А метод try_emplace() вместо std::pair<iterator, bool> лучше бы вернул структуру со свойствами (it, was_added).
Красота!
Структура std::pair имеет свойства (first, second), и для неё определены операторы сравнения.
Почему бы не взять её как ключ в std::map ?
Или же лучше определить новую структуру со свойствами (module_name, var_name) ??
Тем более конструктор в структуре std::pair не всегда нужен. Без него компиляция должна быть быстрее.
А 20-ый стандарт упрощает определение операторов сравнения для структур.
(Напомню, что ключ для std::map должен быть сравнимым через третий параметр шаблона).
? Наглядность кода, или обобщение ?
? заводить ли новую структуру под конкретную цель каждый раз, или взять готовый pair ?
Кстати ещё вопрос: В своих проектах вы используете std::tuple (казалось бы норм тема) ?
И кстати, я стараюсь не использовать auto в си++ (тут он лишь для упрощения).
Неспешно делаю парсер своего яп. В отличии от си++ в языке нет требования на forward declaration. И нет глобальных переменных.
Есть модули, в них есть модульные переменные (aka свойства модуля).
При инициализации переменной модуля в выражении может быть обращение к другой переменной, которая ещё не была определена.
В этом случае подставляется временный номер, а указатель на структуру, которая её хранит кладётся в специальную пачку неизвестных переменных.
Когда будет встречена новая переменная — сперва ей присваивается номер, затем происходит проверка есть ли её имя в той спец пачке.
Если есть, то по всем собранным указателям к имени этой переменной устанавливается актуальный номер, и её имя вычеркивается из пачки неизвестных.
Когда все модули будут загружены, то смотрим: остались ли в спец пачке неизвестные переменные, чтобы выдать при этом лог по ним.
Как лучше организовать пачку? Я думаю взять готовый std::map
Заглянем в доки std::map, и видим — там value_type это std::pair со свойствами first, second (вроде запомнить не сложно)
Метод для добавления элемента в мапу try_emplace() возвращает другой std::pair с итератором и флагом, было ли добавлено.
my_map v_map;
auto res = v_map.try_emplace(v_key, v_arg);
if( res.second ) { // second -- flag
res.first->second.some_method(); // first -- iterator // second -- значение
}
Согласитесь, что читателю кода было бы нагляднее видеть в качестве value_type структуру со свойствами (key, value).
А метод try_emplace() вместо std::pair<iterator, bool> лучше бы вернул структуру со свойствами (it, was_added).
auto res = v_map.try_emplace(v_key, v_arg);
if( res.was_added ) {
res.it->value.some_method();
}
Красота!
Структура std::pair имеет свойства (first, second), и для неё определены операторы сравнения.
Почему бы не взять её как ключ в std::map ?
using my_key = std::pair<std::string, std::string>; // first -- имя модуля, second -- имя переменной
using my_map = std::map<my_key, my_var_info>;
Или же лучше определить новую структуру со свойствами (module_name, var_name) ??
Тем более конструктор в структуре std::pair не всегда нужен. Без него компиляция должна быть быстрее.
А 20-ый стандарт упрощает определение операторов сравнения для структур.
(Напомню, что ключ для std::map должен быть сравнимым через третий параметр шаблона).
struct my_key {
std::string
module_name,
var_name;
friend bool operator == (const my_key &, const my_key &) = default;
friend auto operator <=> (const my_key &, const my_key &) = default;
};
auto res = v_map.try_emplace({mod_name, var_name});
res.it->key.module_name // против res.first->first.first
? Наглядность кода, или обобщение ?
? заводить ли новую структуру под конкретную цель каждый раз, или взять готовый pair ?
Кстати ещё вопрос: В своих проектах вы используете std::tuple (казалось бы норм тема) ?
И кстати, я стараюсь не использовать auto в си++ (тут он лишь для упрощения).