Вопрос целесообразности обобщения
От: Sm0ke Россия ksi
Дата: 05.07.23 13:43
Оценка:
Начну пожалуй с конкретной задачи, которая вывела меня на эти размышления.

Неспешно делаю парсер своего яп. В отличии от си++ в языке нет требования на 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 в си++ (тут он лишь для упрощения).

p.s: Собственно сабж о том, в каких случаях лучше дублировать, а в каких обобщать
Отредактировано 05.07.2023 14:33 Sm0ke . Предыдущая версия . Еще …
Отредактировано 05.07.2023 14:17 Sm0ke . Предыдущая версия .
Отредактировано 05.07.2023 14:09 Sm0ke . Предыдущая версия .
Отредактировано 05.07.2023 13:58 Sm0ke . Предыдущая версия .
Отредактировано 05.07.2023 13:57 Sm0ke . Предыдущая версия .
Отредактировано 05.07.2023 13:51 Sm0ke . Предыдущая версия .
Re: Вопрос целесообразности обобщения
От: bnk СССР http://unmanagedvisio.com/
Дата: 05.07.23 15:17
Оценка:
Здравствуйте, Sm0ke, Вы писали:

IMHO ты сам через неделю забудешь кто там first кто second

Совсем по-простому, можно переменные завести (вместо комментариев):

auto res = v_map.try_emplace(v_key, v_arg);

auto was_added = res.second;

if( was_added ) {
   auto info = res.first->second;
   info.some_method();
}


Еще можно вместо структуры сделать типа декодер:


struct decode {
  decode(my_map::iterator& it) : module_name(it->first), info(it->second) {}
  const my_map::key_type& module_name;
  my_map::mapped_type& info;
};

it = ...
decode(it).module_name
decode(it).info
Re[2]: Вопрос целесообразности обобщения
От: Sm0ke Россия ksi
Дата: 05.07.23 16:11
Оценка: +1
Здравствуйте, bnk, Спасибо за ответ.

bnk>IMHO ты сам через неделю забудешь кто там first кто second


Дак я про что. Разработчики std::map заюзали std::pair имхо зря. Я вообще на данный момент не любитель этого pair с безымянными first и second.

bnk>Совсем по-простому, можно переменные завести (вместо комментариев):


bnk>
bnk>auto res = v_map.try_emplace(v_key, v_arg);

bnk>auto was_added = res.second;

bnk>if( was_added ) {
bnk>   auto info = res.first->second;
bnk>   info.some_method();
bnk>}
bnk>


bnk>Еще можно вместо структуры сделать типа декодер:


bnk>

bnk>struct decode {
bnk>  decode(my_map::iterator& it) : module_name(it->first), info(it->second) {}
bnk>  const my_map::key_type& module_name;
bnk>  my_map::mapped_type& info;
bnk>};

bnk>it = ...
bnk>decode(it).module_name
bnk>decode(it).info

bnk>


Выглядит как костыль. Делать pair + костыль, или сразу завести чёткую структуру с понятными именами.
Re: Вопрос целесообразности обобщения
От: sergii.p  
Дата: 10.07.23 08:25
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Согласитесь, что читателю кода было бы нагляднее видеть в качестве value_type структуру со свойствами (key, value).


конечно в теории оно должно быть лучше. Но на пректике мне визуально не нравится

struct MyStruct {
    struct MyPair {
        std::string key;
        std::string value;
    };
    MyPair insert();
};

MyStruct::MyPair MyStruct::insert() {
}


обилие вложенностей немного смущает. Особенно если кто-то не любит auto.

S>
S>auto res = v_map.try_emplace(v_key, v_arg);
S>if( res.was_added ) {
S>  res.it->value.some_method();
S>}
S>


S>Красота!


Это не красота. Вот это красота

const auto [it, was_added] = v_map.try_emplace(v_key, v_arg);
auto& [key, value] = *it;
if(was_added) {
    value.some_method();
}


Вообще, конечно заводить структуры вроде как правильно и особо тут спорить не о чем, но предложу другой подход: усилить систему типов.

template<typename Name> struct NewStringType {
    ...
};
using MuduleName = NewStringType<struct ModuleNameHelp>;
using VarName = NewStringType<struct VarNameHelp>;


и тогда особо пофигу: в структуре завёрнуты значения или в тюпле. Можно забыть/ошибиться. Компилятор за всем проследит и "аккуратно но сильно" даст по рукам.
Re[2]: Вопрос целесообразности обобщения
От: Sm0ke Россия ksi
Дата: 13.07.23 10:43
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>
SP>const auto [it, was_added] = v_map.try_emplace(v_key, v_arg);
SP>auto& [key, value] = *it;
SP>if(was_added) {
SP>    value.some_method();
SP>}
SP>


Вот бы можно было так без auto :

[my_map::iterator it, bool was_added] = v_map.try_emplace(v_key, v_arg);


SP>Вообще, конечно заводить структуры вроде как правильно и особо тут спорить не о чем, но предложу другой подход: усилить систему типов.


SP>
SP>template<typename Name> struct NewStringType {
SP>    ...
SP>};
SP>using MuduleName = NewStringType<struct ModuleNameHelp>;
SP>using VarName = NewStringType<struct VarNameHelp>;
SP>


Чтобы нельзя было сравнивать имя модуля с именем переменной?

Вот тут как раз можно бы и обобщить. Такая обёртка подошла бы не только для строк. Назвать к примеру wrap, и добавить как параметр шаблона хранимый тип.

template <typename T, typename Unique>
struct wrap {
  using value_type = T;
  using unique_type = Unique;

  // data
  value_type value;

  friend bool operator == (const wrap &, const wrap &) = default;
  friend auto operator <=> (const wrap &, const wrap &) = default;
};


Но мне кажется, что на этот случай наверняка должно быть уже готовое решение.
Re[3]: Вопрос целесообразности обобщения
От: sergii.p  
Дата: 14.07.23 12:18
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Вот бы можно было так без auto :


S>
S>[my_map::iterator it, bool was_added] = v_map.try_emplace(v_key, v_arg);
S>


что за язык? Такое на С++ не компилируется.

S>Вот тут как раз можно бы и обобщить. Такая обёртка подошла бы не только для строк. Назвать к примеру wrap, и добавить как параметр шаблона хранимый тип.

S>Но мне кажется, что на этот случай наверняка должно быть уже готовое решение.

"обобщить" это не про С++ Для целочисленных значений есть давно известная идиома в виде enum class. А для других типов каждый изгаляется как может. Есть к примеру макрос BOOST_STRONG_TYPEDEF. Но честно говоря ради пары десятков строчек заводить зависимость от буста не многим по душе. Может было бы неплохо если бы в языке расширили идиому enum class. Не знаю насколько оно реализуемо. Но вот было бы что-то такое

enum class ModuleName : std::string { Invalid = "" };


и жизнь заиграла бы новыми красками
Re[4]: Вопрос целесообразности обобщения
От: Sm0ke Россия ksi
Дата: 15.07.23 12:42
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, Sm0ke, Вы писали:


S>>Вот бы можно было так без auto :


S>>
S>>[my_map::iterator it, bool was_added] = v_map.try_emplace(v_key, v_arg);
S>>


SP>что за язык? Такое на С++ не компилируется.


Я же написал, что мне хотелось бы, чтобы добавили в стандарт такую возможность.
Чтобы можно было использовать Structured binding не через auto, а с конкретными типами)

S>>Вот тут как раз можно бы и обобщить. Такая обёртка подошла бы не только для строк. Назвать к примеру wrap, и добавить как параметр шаблона хранимый тип.

S>>Но мне кажется, что на этот случай наверняка должно быть уже готовое решение.

SP>"обобщить" это не про С++ Для целочисленных значений есть давно известная идиома в виде enum class. А для других типов каждый изгаляется как может. Есть к примеру макрос BOOST_STRONG_TYPEDEF. Но честно говоря ради пары десятков строчек заводить зависимость от буста не многим по душе. Может было бы неплохо если бы в языке расширили идиому enum class. Не знаю насколько оно реализуемо. Но вот было бы что-то такое


SP>
SP>enum class ModuleName : std::string { Invalid = "" };
SP>


SP>и жизнь заиграла бы новыми красками


В php ввели enum трёх типов: общие, целочисленные и текстовые. Так что там это можно)
https://www.php.net/manual/en/language.enumerations.backed.php
Отредактировано 15.07.2023 12:43 Sm0ke . Предыдущая версия .
Re[5]: Вопрос целесообразности обобщения
От: _NN_ www.nemerleweb.com
Дата: 05.09.23 18:44
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Я же написал, что мне хотелось бы, чтобы добавили в стандарт такую возможность.

S>Чтобы можно было использовать Structured binding не через auto, а с конкретными типами)

Я так понимаю дальше предложений дело не пошло: P0480R0 , P0480R1
http://rsdn.nemerleweb.com
http://nemerleweb.com
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.