Обратные вызовы в C++ - [49]
6.3.3. Интерфейс API и обработка ошибок
Исходя из концепции «API как оболочка», сигнатура интерфейсных функций API должна повторять сигнатуру методов интерфейсного класса. Однако здесь мы сталкиваемся с некоторыми проблемами, одна из которых – это обработка ошибок.
В исходной реализации мы обрабатывали ошибки с помощью исключений. Теперь исключения использовать нельзя, в системных API они недопустимы. Тем не менее, вызываемая функция должна как-то уведомить о возникновении ошибки, для чего могут использоваться следующие способы:
1) функция возвращает результат, для которого некоторое предопределенное значение говорит о том, что произошла ошибка. Код ошибки возвращается с помощью отдельного вызова;
2) код ошибки возвращается через дополнительный параметр функции;
3) все функции возвращают результат выполнения, который является кодом ошибки.
Ни один способов не является идеальным, каждый имеет свои достоинства и недостатки. Так, в первом способе возникают сложности, если результат, возвращаемый функцией, не имеет значений, которые недопустимы и могут сигнализировать о возникновении ошибки36. Во втором способе для всех вызовов придется использовать дополнительную переменную – код ошибки, даже если он нас не интересует. В третьем способе, если функция возвращает результат, то для него приходится использовать отдельный входной параметр, что не всегда удобно.
В нашем случае мы выберем третий способ, исходя из следующих соображений: объявления функций будут выглядеть единообразно; возникновение ошибки можно узнать непосредственно в момент вызова, (например, в операторе if); если функция не возвращает значений, то ей не нужно передавать никакие дополнительные параметры. Объявления интерфейсных функций с возвратом ошибок представлены в Листинг 104.
>typedef unsigned int ErrorCode;
>LIB_API ErrorCode initialize();
>LIB_API ErrorCode shutDown();
>LIB_API ErrorCode assignDriver(DriverType type);
>LIB_API ErrorCode getAssignedDriver(DriverType* type);
>LIB_API ErrorCode getSensorDriver(SensorNumber number, DriverType* type);
>LIB_API ErrorCode addSensor(SensorType type, SensorNumber number);
>LIB_API ErrorCode deleteSensor(SensorNumber number);
>LIB_API ErrorCode isSensorExist(SensorNumber number, int* isExist);
>LIB_API ErrorCode isSensorOperable(SensorNumber number, int* isOperable);
>LIB_API ErrorCode getSensorValue(SensorNumber number, SensorValue* value);
>LIB_API ErrorCode querySensorValue(SensorNumber number, SensorValueCallback callback, void* pContextData);
>LIB_API ErrorCode readSensorValues(SensorValueCallback callback, void* pContextData);
>LIB_API ErrorCode getMinValue(SensorNumber first, SensorNumber last, SensorValue* value);
>LIB_API ErrorCode getMaxValue(SensorNumber first, SensorNumber last, SensorValue* value);
>LIB_API ErrorCode setAlert(SensorNumber number, SensorAlertCallback callback, SensorValue alertValue, AlertRule alertRule, CheckAlertTimeout checkTimeoutSeс, void* pContextData);
>LIB_API ErrorCode resetAlert(SensorNumber number);
>LIB_API ErrorCode setSimulateReadCallback(OnSimulateReadValue callback, void* pContextData);
>LIB_API ErrorCode setSimulateOperableCallback(OnSimulateOperable callback, void* pContextData);
В реализации этих функций мы будем возвращать код ошибки, получая его из перехваченного исключения. В качестве примера рассмотрим реализацию функции для получения значения датчика (Листинг 105).
>ErrorCode getSensorValue(SensorNumber number, SensorValue* value)
>{
> ErrorCode error = ERROR_NO; // (1)
> try
> {
> *value = g_SensorControl->getSensorValue(number); // (2)
> }
> catch (sensor::sensor_exception& e) // (3)
> {
> error = e.code(); // (4)
> }
> return error; // (5)
>}
В строке 1 объявляем переменную – код возврата. В строке 2 осуществляем вызов метода класса, который заключен в блок try. В строке 3 осуществляется перехват исключения, в строке 4 присваивается код ошибки, который возвращается в строке 5.
Итак, мы придумали, как в интерфейсных функциях осуществлять обработку ошибок. Теперь перед нами встает следующая проблема: как настраивать типы драйверов, ведь в исходной реализации для этого используются классы? Прежде чем перейти к решению этой задачи, остановимся на реализации многопоточной работы, поскольку используемые там конструкции нам понадобятся в дальнейшем.
6.3.4. Многопоточная работа
В исходной реализации в каждом потоке мы могли создать свой экземпляр класса ISensorControl и работать с ним независимо. В случае API это не работает, потому что экземпляр класса в реализации интерфейса объявляется глобальным, и все интерфейсные функции обращаются к одному и тому же экземпляру класса. Выходом здесь будет выделение отдельной области памяти для экземпляра класса в рамках одного потока, т. е. использование локальной памяти потока.
До появления стандарта C++ 11 использовать локальную память потока было непросто: для этого требовалось явное обращение к функциям операционной системы, что усложняло реализацию и делало код платформенно-зависимым. В C++ 11 появилось ключевое слово
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.