2010-07-01 2 views
20

Я использую коды ошибок для обработки ошибок в моем проекте на C++. Проблема заключается в том, как вернуть коды ошибок из функции, которая должна возвращать некоторую переменную/объект.Правильный подход для возврата кодов ошибок в C++

считают это:

long val = myobject.doSomething(); 

Здесь MyObject является объектом некоторого класса. Если функция doSomething встречает некоторое условие ошибки, то как она должна уведомить вызывающего абонента (без использования исключений).

Возможные решения:

  1. Есть элемент данных (скажем err_) в классе, который может быть проверен абонентом. Но это было бы небезопасно в многопоточном приложении, использующем один и тот же объект и вызывающем одну и ту же функцию.
  2. Используйте некоторую глобальную переменную ошибки, снова такую ​​же проблему в многопоточной среде.

Теперь, как я могу уведомить вызывающего абонента о некотором состоянии ошибки?

+0

Связанный: [Результат и ошибка кода C++ int foo (...)] (http://stackoverflow.com/questions/1882047/convention-result-and-code-error-c-int-foo) –

+9

Вы, я только что обнаружил причину №1 для исключений, BTW. Но почему вы также отвергаете другое общее решение для сообщения об ошибках, которое заключается в возврате кода ошибки и использовании out-parameters для результатов функции? – MSalters

+0

@MSalters Я не использую другой подход, о котором вы говорили, потому что часть кода уже выполнена, и это добавит к несогласованности в стиле – Jeet

ответ

4

Вы можете передать переменную как ссылку и код ошибки возврата в ней.

+2

Более распространенной практикой является передача переменной witch, которая получит результат по ссылочному и обратному коду ошибки. – adf88

+2

Это обычная, но уродливая практика. Ожидаемая идиома намного лучше. –

3

Обычно возвращают код возврата/ошибки и предоставляют доступное свойство или элемент с результатами.

int retCode = myobject.doSomething(); 
if (retCode < 0){ //Or whatever you error convention is 
    //Do error handling 
}else{ 
    long val = myobject.result; 
} 

Это также обычно передать указатель, который устанавливается на возвращаемое значение и возвращает код возврата/ошибки. (См. HrQueryAllRows).

long val = INIT_VAL; 
int retCode = myObject.doSomething(&val); 

if (retCode < 0){ 
    //Do error handling 
}else{ 
    //Do something with val... 
} 
2

Верните рукоятку ошибки. У диспетчера ошибок сохраняются коды ошибок и дополнительные сведения (например, ERROR_INVALID_PARAMETER и пар с именами, например ParameterName="pszFileName"). К этой информации можно обращаться с помощью дескриптора. Вызывающий может проверить дескриптор ошибки на NO_ERROR_HANDLE. Если значение true, ошибки не было. Вызывающий может увеличить информацию об ошибке и передать дескриптор в стек. Менеджер ошибок может быть один для процесса или по одному для каждого потока.

2

У вас есть три варианта:

Создать класс, содержащий возвращаемое значение и возможный код ошибки.

Используйте что-то вроде boost :: optional для возвращаемого значения, которое допускает недействительные ответы.

Передайте ссылку на переменную и верните в нее любой возможный код ошибки.

2

Наиболее распространенной практикой является возвращение код ошибки

long result; 
int error = some_obj.SomeMethod(&result); 

или возвращает значение, указывающее, что произошла ошибка:

long result = some_obj.SomeMethod(); 
if (result < 0) error = some_obj.GetError(); 
+1

Метод 'GetError()' действительно ограничивает вашу способность писать качественный параллельный код (вы сохраняете состояние из вызова метода в своем объекте). – weberc2

28

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

Maybe<long> result = object.somemethod(); 

Maybe шаблон будет иметь способ быть инстанцирован с кодом ошибки (возможно, статический метод):

return Maybe<long>::error(code); 

Но обычно просто возвращаются со значением:

Maybe<long> retval; 
retval = 15; 
return retval; 

(Вы должны были бы, конечно, переопределяет соответствующие конструктор, оператор присваивания, и т.д.)

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

Maybe<long> result = object.somemethod(); 
if (result.is_error) 
{ 
    ... handle the error ... 
} 
else 
{ 
    ... use the result ... 
} 

Опять вы должны были бы соответствующие операторы, определенные для использования Maybe<long> везде, где есть long требуется.

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

+6

+1 - Это довольно элегантно. Вы можете расширить его, добавив оператор-листинг к 'bool', чтобы вы могли написать' if (result) 'вместо' if (! Result.is_error) 'и добавить исключение при доступе к результату результата, если результатом является ошибка , Я знаю, что бросать исключение - это то, чего мы избегаем, но это действительно было бы хорошим примером для этого. –

+1

Я бы сказал, что доступ к значению, когда он является состоянием ошибки, является идеальной ситуацией для бросания исключения, фактически, поскольку он показывает очевидную ошибку программиста: не проверять ошибки. –

+0

@JUST: И если вы подумаете об этом чуть позже, вы сразу же придете к исключению исключений вместо того, чтобы полагаться на программистов при проверке возвращаемых значений. – sbi

2

Вы можете использовать исключения.

+6

-1, снова прочитайте вопрос. – adf88

+1

Перед тем, как использовать исключение, вам нужно спросить: «Это действительно исключительная ситуация, которая гарантирует массовый накладные расходы, связанные с выбросом исключения?« –

+3

@ adf88: Я только что прочитал вопрос, и я все еще думаю, что swegi ударил ноготь по голове.« + 1 »от меня. – sbi

2

Я хотел бы предложить следующее:

class foo { 
public: 
    long doSomething(); 
    long doSomething(error_code &e); 
}; 

Где error_code некоторый тип, который содержит ошибку. Это может быть целое или лучшее что-то, основанное на boost::system::error_code.

И поставить две функции:

  1. Первая версия бросает ошибку, например, бросить boost::system::system_error, созданный из boost::system::error_code.
  2. Второй возвращает код ошибки в e.
0

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

, например

#define FILE_ERROR  1 
#define SANITY_ERROR  2 

int WriteToFile(char* Data, int iErrorCode) 
{ 
    char* FileName; 

    if (!FileOpen(FileName, &iErrorCode)) 
    { 
    //Analyze error code and make decision on what to ignore or terminate 
    iErrorCode = FILE_ERROR; 
    return 0; 
    } 

} 

int FileOpen(char* FileName, int* iErrorCode) 
{ 

    if (FileName == null) 
    { 
     iErrorCode = SANITY_ERROR; 
     return 0; 
    } 

    ///// next code blocks 
    return 1; 
} 
0

Вы можете вернуть std::pair держит как код ошибки (или объект ошибки) и желаемый объект возврата. Объекту интереса нужен конструктор или значение по умолчанию, чтобы вы могли вернуть что-то даже при возникновении ошибки.

Смежные вопросы