Макрос C для замены .at() на []

#c

#c

Вопрос:

У меня часто возникает ошибка выхода индекса за пределы, которую можно легко обнаружить, если я использую .at() вместо [] для доступа к элементу в векторе или строке. Однако at() замедляет мою программу в 5 раз из-за проверки границ.

Я пытаюсь написать макрос для замены .at(someVariable) на [someVariable] таким образом, я могу просто раскомментировать макрос вместо того, чтобы заменять каждый .at() на [] вручную. Я прочитал документацию по макросам на cppreference.com но, похоже, не могу придумать способ получить эту функциональность.

Ответ №1:

В общем, я бы избегал такого рода макросов, поскольку они «невидимы» (поэтому измененная семантика незаметно скрыта от тех, кто не знает) и может изменять функциональность кода, который, как ожидается, at выйдет за пределы, даже в сборках релизов. Во всяком случае, я бы определил что-то, что выделяется, например, все в верхнем регистре AT .

К счастью, на самом деле это не требуется, поскольку среды выполнения C «большой тройки» уже имеют встроенную функциональность для условного включения проверки границ operator[] (что также имеет то преимущество, что оно более читабельно, чем at ):

Кстати, некоторые из этих ошибок (те, которые фактически превышают выделенный размер, а не только «логически допустимый» размер вектора) также могут быть обнаружены с помощью средства очистки адресов ( -fsanitize=address в gcc и clang) или valgrind (медленно!) и подобных инструментов.

Ответ №2:

Вот один из способов, который не включает макросы — он использует c 17 if constexpr для простоты:

 #include <vector>

constexpr bool safe_mode = true;  // or false

template<class Container>
auto at(Containeramp; v, std::size_t i) -> decltype(auto)
{
    if constexpr (safe_mode)
        return v.at(i);
    else
        return v[i];
}

intamp; test(std::vector<int>amp; v)
{
    return at(v, 6);
}
  

Комментарии:

1. Почему decltype(auto) тянется?

2. @CruzJean если вы имеете в виду, почему я решил написать это именно так, вероятно, в силу привычки. Я пишу достаточное количество шаблонного кода и считаю полезным иметь в своем распоряжении все типы функций / методов. Поэтому я склонен стандартизировать конечные возвращаемые типы. Сказав это, я вижу, что я смешал стили в приведенном выше коде. 🙂

3. Ах, мне просто было любопытно, было ли какое-то тонкое различие, о котором я не знал.

4. По состоянию на год назад, по предложению одного из руководителей проекта в нашем приложении, я начал использовать конечные типы возвращаемых данных преимущественно в своем коде (почти исключительно), и я нахожу их гораздо более удобочитаемыми. Это и в сочетании с «почти всегда автоматически», результирующий код C разбирается намного чище. Все зависит от мнения, я знаю.

Ответ №3:

Вы могли бы использовать полное имя оператора разыменования массива:

 #define at(x) operator[](x)
  

(x) Часть не является существенной, но заменит только at , если за ней следует аргумент, а не заменяет слово само по себе. Вы также захотите определить это после включения всех стандартных заголовков, иначе это заменит at объявления функций-членов в классах, которые имеют это, что приведет к ошибкам компиляции.

Комментарии:

1. Почему вам не нужен . before at в макросе? Не должен ли он заменить . тоже?

2. @user3586940 Вы не можете заменить . в макросе, так как . не может быть в имени макроса.