LINUX.ORG.RU

[совет-тред]Как сообщить об ошибке?


0

1

Всем доброго времени суток!

Вопрос возник, есть некий класс, содержащий в себе буфер ввода-вывода, для класса перегружен оператор []:

class A
{
    unsigned char *buffer;
    unsigned long buffer_size;
  public:
    unsigned char &operator[](const int index);
};

unsigned char &A::operator[](const int index)
{
  // здесь надо проверить индекс на соответствие размеру буфера
  return buffer[index];
}

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

★★

открой stl и посмотри, делов то

const_reference
at(size_type __n) const
{
    _M_range_check(__n);
    return (*this)[__n];
}

[..]

/// Safety check used only from at().
void
_M_range_check(size_type __n) const
{
    if (__n >= this->size())
    __throw_out_of_range(__N("vector::_M_range_check"));
}
shty ★★★★★
()
Ответ на: комментарий от shty

Что-то сам не додумался, не проснусь никак.

m1rag3 ★★
() автор топика

Еще можно добавить внутренний флаг ошибки и метод его получения. В случае возврата какого-то конкретного «ошибочного» значения проверять флаг. Примерно так:

const unsigned char ERR_CHAR = (unsigned char) -1;
class A {
  bool is_ok;
  // ...
public:
  bool ok() const { return is_ok; }
  // ...
};

unsigned char &A::operator[](const int index)
{
  if(index >= size) { 
    is_ok = false; 
    return ERR_CHAR;
  }
  return buffer[index];
}

// ... 

void f()
{
  A a;
  // ...
  unsigned char c = a[idx];
  if( ERR_CHAR == c && !a.ok() ) {
    // handle error
  }
}
DELIRIUM ☆☆☆☆☆
()

exception'ы. Они дешевые по сравнению с последующей поддержкой и расширением кода.

anonymous
()

Смотря какое поведение требуется получить.

1. Делать вид, что ничего не произошло, возвращать ссылку на static char и кое-как пытаться продолжать работать.

2. Считать такую ситуацию недопустимой, кидать исключение, и, соответственно, делать вдоль, если исключение не поймают.

Вариант «код ошибки» — это костыли. В Си++ так не делают, крайне дурной тон.

geekless ★★
()
Ответ на: комментарий от DELIRIUM

Еще можно добавить внутренний флаг ошибки и метод его получения. В случае возврата какого-то конкретного «ошибочного» значения проверять флаг. Примерно так:

говнокод. и не C++-style.

anonymous
()
Ответ на: комментарий от anonymous

Ну я же как альтернативу предложил +) Исключения уже до меня советовали.

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от geekless

Требуется прозрачная работа с объектом, как с массивом. Но допускать возврата ошибочных значений, не соответствующих содержимому буфера, нельзя. Ну а выход за пределы буфера - это вообще фаталити.

m1rag3 ★★
() автор топика
Ответ на: комментарий от m1rag3

забавно наблюдать за радикально настроенными плюсистами «код ошибки костыли» «в ц++ так не делают» эти же люди наверно и городят тонны говна где надо и не надо (паттерны и прочий мусор в программе размером 5x5) все крайне плохо для этих пациентов потому что они не делает КАК УДОБНО в зависимости от ситуации а управляются какими то догмами (стадный инстинкт?) и вообще я таких людей считаю ограниченными увидят что то не в ООП и кричат «в ц++ так не деается» ну как школьники или просто узколобые создания

сам люблю ц++ но не признаю таких «шизонутых фанатиков» которые руководствуются правилом «делай как это должно быть в ц++ или смерть» в первую очередь удобство во вторую все остальное а не наоборот

anonymous
()

http://www.boost.org/doc/libs/1_35_0/libs/optional/doc/html/index.html

во первых если хочется проверять код ошибки то нужна функция а не оператор ну а что делать когда «нет возвращаемого значения» известно в boost есть class optional суть его проста это специализированный std::pair с ним можно делать так вот пример из буста

optional<char> get_async_input()
{
    if ( !queue.empty() )
        return optional<char>(queue.top());
    else return optional<char>(); // uninitialized
}

void receive_async_message()
{
    optional<char> rcv ;
    // The safe boolean conversion from 'rcv' is used here.
    while ( (rcv = get_async_input()) && !timeout() )
        output(*rcv);
}

anonymous
()
Ответ на: комментарий от anonymous

Никакого фанатизма. Про эксепшены имхо здесь самое оно, просто решил лишний раз убедиться. А вот STL, Boost и т.д. абсолютно согласен, если не требуется ынтырпрайс-код и задача относительно проста, то пожалуйста, закодить пару простых классов и не наворачивать шаблонов гору. Как мне один местный обитатель сказал про мой код (http://www.linux.org.ru/forum/development/6524119#comment-6524438) - си с классами. Как бы там ни было, я за такой подход, если не требуется иного. При этом не претендую на звание профи, скорее наоборот, полный нуб, в чем убедился, прочитав Шилдта и множество интересных статей и блогов в нете. И вряд ли все согласятся с таким мнением.

m1rag3 ★★
() автор топика
Ответ на: комментарий от m1rag3

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

m0rph ★★★★★
()
Ответ на: комментарий от m0rph

Для ошибок программиста есть утверждения. Исключения для исключительных, редких ситуаций. Код возврата, если ошибка имеет вероятность близкую к вероятности правильного результата. Исключения довольно накладны.

Booster ★★
()
Ответ на: комментарий от Booster

вообще как удобно так и надо поступать а не следовать правилам

даже если вероятность ошибки велика если это удобно можно использовать исключения вот вам пример есть тпранзакция общения с smtp сервером

send_smtp_command
recv_smtp_command
send_smtp_command
recv_smtp_command
..............
...........
........
если я проектирую XX_smtp_command то я кину исключение а весь код транзакции включу в один try catch вместо if'а после каждого вызова
try
{
send_smtp_command
recv_smtp_command
send_smtp_command
recv_smtp_command
..............
...........
........
}
catch (...)
{
puts(ololo);
}
сравните с этим мясом
if(!send_smtp_command)
puts(ololo);
if (!recv_smtp_command)
puts(ololo);
if (!send_smtp_command)
puts(ololo);
if (!recv_smtp_command)
puts(ololo);
..............
...........
........
вывод как удобно так и поступай в каждом конкреном случае

anonymous
()
Ответ на: комментарий от anonymous

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

Если исключений будет много, то есть вероятность что производительность сильно просядет. Я не замерял в C++, но например в .NET исключения сверх тормозные. И часто код не выглядит очень ужасным от использования возвращаемых значений, особенно если таких вызовов не много.

Booster ★★
()
Ответ на: комментарий от anonymous

И кстати я бы не сказал что try catch это верх красоты. Если такой лабуды много, то тоже выходит мясо.

Booster ★★
()
Ответ на: комментарий от Booster

> Для ошибок программиста есть утверждения. Исключения для исключительных, редких ситуаций. Код возврата, если ошибка имеет вероятность близкую к вероятности правильного результата. Исключения довольно накладны.

Именно потому что исключения накладны применять их для сообщения об ошибке большого смысла нет. Поэтому я и сторонник их использования исключительно в ситуации, где напортачил сам программист. Например пусть существует класс File имеющий методы open, close, read, write. Если программист не открыв файл попытается сделать вызов read, вполне можно сгенерировать исключение. При нормальном выполнении программы управление в эту ветку передано не будет никогда, поэтому исключения никак не замедлят процесс. А вот на стадии разработки по рукам будут давать замечательно. Конечно обычно в таких случаях применяются assert'ы и это хорошая альтернатива исключениям.

m0rph ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.