Разработка на чистом C
От: Lonely Dog Россия  
Дата: 30.10.16 11:44
Оценка: 6 (1) :))
Привет!

Я 15 лет пишу на C++, немного знаю C#, Python и пр. Но совершенно не понимаю, как писать на C. Как организовывать обработку ошибок (исключений нет, значит остаются коды возврата или setjmp/longjmp). RAII нет, значит надо в конце функции все чистить самому. Знаю, что у MS в драйверах используется goto cleanup для этих целей. Что-то типа:
void doSomething()
{
  XXX
  if (XXX)
    goto cleanup;

  cleanup:
    free(XXX);
}


Понятно, что можно использовать alloca для автоосвобождения памяти. Но не везде это подходит (стек все-таки ограничен). Далее, в C++ есть STL с кучей полезных структур данных (а есть ведь еще Boost/Poco). В чистом C ничего такого нет, т.е. надо использовать какие-то сторонние библиотеки. Какие?


Может есть какие-нибудь книги? Думаю, посмотреть что-то типа Writing device drivers for Linux.

Заранее спасибо.
Отредактировано 30.10.2016 11:45 Lonely Dog . Предыдущая версия .
Re: Разработка на чистом C
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.10.16 12:52
Оценка: +2 -2
Здравствуйте, Lonely Dog, Вы писали:

LD>Я 15 лет пишу на C++, немного знаю C#, Python и пр. Но совершенно не понимаю, как писать на C. Как организовывать обработку ошибок (исключений нет, значит остаются коды возврата или setjmp/longjmp). RAII нет, значит надо в конце функции все чистить самому.


Ну да, так и пишем, все руками. Полезность C++'ных автоматизмов сильно переоценена. В том смысле, что необходимость делать все явно добавляет, конечно, работы, но не так, чтобы очень много. С другой стороны, Си не подбросит сюрприза от того, что что-то автоматически случилось, с неочевидными побочными эффектами, а где и что, поди пойми.

Обработка ошибок в виде явно возвращаемых кодов возврата — это не такой уж и плохой вариант. В отличии от исключений, все перед глазами и все легко читается, ошибка не прилетит неизвестно откуда и не улетит неизвестно куда. setjmp/longjmp для обработки ошибок очень мало подходят, это экзотические функции, которые бывают полезны в редких случаях.

LD>Знаю, что у MS в драйверах используется goto cleanup для этих целей. Что-то типа:


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

LD>Понятно, что можно использовать alloca для автоосвобождения памяти. Но не везде это подходит (стек все-таки ограничен).


alloca освобождает память, но не закрывает файлы и т.п. Кроме того, с указателями, полученными от alloca, надо быть осторожным. Они становятся невалидными при выходе из функции, и могут стать невалидными при выходе из блока (я на такое нарывался, наверное, это был глюк компилятора, но кому от этого легче?).
Re: Разработка на чистом C
От: smeeld  
Дата: 30.10.16 13:11
Оценка: 1 (1) +2
Здравствуйте, Lonely Dog, Вы писали:


LD> Далее, в C++ есть STL с кучей полезных структур данных (а есть ведь еще Boost/Poco). В чистом C ничего такого нет, т.е. надо использовать какие-то сторонние библиотеки. Какие?



В каждом крупном проекте, типа linux kernel, solaris kernel, postfix, nginx etc, есть свой аналог STL. Свои map-ы, list-ы, unordered_map-ы, свой джентельменский набор содержимого STL-вского <algorithm>, и ещё куча всего, чего в STL- нет и не будет, просмотрите содержимое этой папки.
Re: Разработка на чистом C
От: VTT http://vtt.to
Дата: 30.10.16 13:50
Оценка: +1
Здравствуйте, Lonely Dog, Вы писали:

LD>Я 15 лет пишу на C++, немного знаю C#, Python и пр. Но совершенно не понимаю, как писать на C.

LD>Как организовывать обработку ошибок (исключений нет, значит остаются коды возврата или setjmp/longjmp). RAII нет, значит надо в конце функции все чистить самому.
Ручками, ручками... Указатель на экземпляр класса передавать — сам, таблицу виртуальных методов сделать — сам, ресурсы подчистить — сам, проверить, что коды ошибок не игнорируются и результаты несработавших методов не используются — сам.
Мне представляется, что С++ первоначально набирал популярность как раз за счет перекладывания на компилятор многих существовавших рутинных аспектов программировании на С.

LD>Может есть какие-нибудь книги? Думаю, посмотреть что-то типа Writing device drivers for Linux.

Надо полагать, что в драйверах (и вообще в коде ОС) есть своя специфика.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re: Разработка на чистом C
От: smeeld  
Дата: 30.10.16 15:46
Оценка: 4 (1) +1 -11 :))) :))) :))) :))
Здравствуйте, Lonely Dog, Вы писали:

> Как организовывать обработку ошибок (исключений нет, значит остаются коды возврата или setjmp/longjmp).


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

> RAII нет, значит надо в конце функции все чистить самому. Знаю, что у MS в драйверах используется goto cleanup для этих целей. Что-то типа:


RAII тоже сомнительная фича. Сейчас всякая школота начинает пихать в конструкторы открытие дескрипторов, операции ввода/вывода, операции, требующие доступ к разделяемому ресурсу и пр. "тяжёлые" операции которые могут закончиться неудачно и, тем самым, экземпляр объекта будет непригоден для использования. Это глуповатый подход. Правильней создавать специальные фукции в духе init/create, вызывать их перед использоваием экземпляра, с проверкой результата инициализации. Корифеи индустрии так и поступают

#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>

#include "defines.h"
#include "language.h"
#include "core.h"

#include "..\common\mem.h"
#include "..\common\str.h"
#include "..\common\cui.h"
#include "..\common\crypt.h"
#include "..\common\console.h"

COREDATA coreData;

//Options for Listen.
static const Cui::SWITCH listenSwitches[] =
{
  {lng_switch_nologo,                lng_switch_nologo_help},
  {lng_switch_ipv4,                  lng_switch_ipv4_help},
  {lng_switch_ipv6,                  lng_switch_ipv6_help},
  {lng_switch_botport    L":[port]", lng_switch_botport_help},
  {lng_switch_clientport L":[port]", lng_switch_clientport_help}
};

//Teams
void commandListen(LPWSTR *switches, DWORD switchesCount);

static const Cui::COMMAND commands[] =
{
  {lng_command_listen, lng_command_listen_help, commandListen, (Cui::SWITCH *)listenSwitches, sizeof(listenSwitches) / sizeof(Cui::SWITCH)}
};

//////////////////////////////////////////////////// ////////////////////////////////////////////////

void Core::init(void)
{
  Mem::_zero(&coreData, sizeof(COREDATA));
  CWA(kernel32, GetModuleFileNameW)(NULL, coreData.fileName, MAX_PATH);

  coreData.exitCode         = Cui::EXITCODE_ERROR_BAD_COMMAND_LINE;
  coreData.commandLine.args = CWA(kernel32, CommandLineToArgvW)(CWA(kernel32, GetCommandLineW()), &coreData.commandLine.argsCount);
}

void Core::uninit(void)
{
  if(coreData.commandLine.args)CWA(kernel32, LocalFree)(coreData.commandLine.args);
}

void Core::showLogo(void)
{
  Console::writeFormatW(lng_logo, VERSION_MAJOR(BO_CLIENT_VERSION), VERSION_MINOR(BO_CLIENT_VERSION), VERSION_SUBMINOR(BO_CLIENT_VERSION), VERSION_BUILD(BO_CLIENT_VERSION));
}

//////////////////////////////////////////////////// ///////////////////////////
// Entry point.
//////////////////////////////////////////////////// ///////////////////////////

void WINAPI entryPoint(void)
{
  Mem::init();
  Console::init();  
  Crypt::init();
  Core::init();
  
  CUI_DEFAULT_COMMANDLINE_HELPER;

  Core::uninit();
  Crypt::uninit();
  Console::uninit();
  Mem::uninit();
  
  CWA(kernel32, ExitProcess)(coreData.exitCode);
}
Re[2]: Разработка на чистом C
От: b0r3d0m  
Дата: 30.10.16 16:27
Оценка: +1
S>RAII тоже сомнительная фича. Сейчас всякая школота начинает пихать в конструкторы открытие дескрипторов, операции ввода/вывода, операции, требующие доступ к разделяемому ресурсу и пр. "тяжёлые" операции которые могут закончиться неудачно и, тем самым, экземпляр объекта будет непригоден для использования. Это глуповатый подход. Правильней создавать специальные фукции в духе init/create, вызывать их перед использоваием экземпляра, с проверкой результата инициализации. Корифеи индустрии так и поступают

Ваши корифеи и unininit поди перед каждым return'ом ставить не забывают никогда?

А вот с мнением о помещении "сложного" кода в конструкторы я согласен.
Re[2]: Разработка на чистом C
От: Evgeny.Panasyuk Россия  
Дата: 30.10.16 16:58
Оценка: 1 (1) +6
Здравствуйте, Pzz, Вы писали:

Pzz>Ну да, так и пишем, все руками. Полезность C++'ных автоматизмов сильно переоценена. В том смысле, что необходимость делать все явно добавляет, конечно, работы, но не так, чтобы очень много.


Error-prone, легко забыть и трудно поддерживать.

Pzz>Обработка ошибок в виде явно возвращаемых кодов возврата — это не такой уж и плохой вариант. В отличии от исключений, все перед глазами и все легко читается, ошибка не прилетит неизвестно откуда и не улетит неизвестно куда.


Error codes точно также летят наверх неизвестно куда.
Хотя часто вообще не летят, ибо на них забивают — пример
Автор: MTD
Дата: 21.02.13
.

Pzz>setjmp/longjmp для обработки ошибок очень мало подходят, это экзотические функции, которые бывают полезны в редких случаях.


Их используют для эмуляции исключений.
Re: Разработка на чистом C
От: Evgeny.Panasyuk Россия  
Дата: 30.10.16 17:08
Оценка: 2 (1)
Здравствуйте, Lonely Dog, Вы писали:

LD>RAII нет, значит надо в конце функции все чистить самому.


RAII можно частично сэмулировать через for + макросы, будет эдакий аналог using из C# / with из Python / try-with-resources из Java.
(for позволяет выполнить заданный код (cleanup) в конце блока for(...){ ... } /*auto_cleanup*/ )
Re[3]: Разработка на чистом C
От: smeeld  
Дата: 30.10.16 17:40
Оценка: -6 :))) :)
Здравствуйте, b0r3d0m, Вы писали:


B>Ваши корифеи и unininit поди перед каждым return'ом ставить не забывают никогда?


Забывают? Тут про разработчиков ПО говорим, или про пациентов клиники для склеротиков?
Re[4]: Разработка на чистом C
От: b0r3d0m  
Дата: 30.10.16 18:12
Оценка: +6
B>>Ваши корифеи и unininit поди перед каждым return'ом ставить не забывают никогда?

S>Забывают? Тут про разработчиков ПО говорим, или про пациентов клиники для склеротиков?


Как там в стране единорогов?
Re: Разработка на чистом C
От: kov_serg Россия  
Дата: 30.10.16 18:35
Оценка: 22 (3) +3
Здравствуйте, Lonely Dog, Вы писали:

LD>Привет!


LD>Я 15 лет пишу на C++, немного знаю C#, Python и пр. Но совершенно не понимаю, как писать на C. Как организовывать обработку ошибок (исключений нет, значит остаются коды возврата или setjmp/longjmp). RAII нет, значит надо в конце функции все чистить самому. Знаю, что у MS в драйверах используется goto cleanup для этих целей. Что-то типа:

LD>Понятно, что можно использовать alloca для автоосвобождения памяти. Но не везде это подходит (стек все-таки ограничен). Далее, в C++ есть STL с кучей полезных структур данных (а есть ведь еще Boost/Poco). В чистом C ничего такого нет, т.е. надо использовать какие-то сторонние библиотеки. Какие?
Сторонних библиотек предостаточно:
http://sglib.sourceforge.net/
https://sourceforge.net/projects/ctl/
https://developer.gnome.org/glib/2.50/
http://docs.libuv.org/en/v1.x/
...
На чистом С несколько иной подход. Тут более важна организация кода, а не конкретные локальные приёмы. Совершенно не обязательно тащить универсальность из C++ иногда есть требования запрещающие использование динамической памяти и вполне себе компилируется и работает на ограниченных ресурсах с заранее выделенными массивами для обработки.

LD>Может есть какие-нибудь книги? Думаю, посмотреть что-то типа Writing device drivers for Linux.

Книги тут можешь глянуть http://rsdn.org/forum/cpp/6568718.1
Автор: kov_serg
Дата: 30.09.16
в часности я тут https://cloud.mail.ru/public/8muB/R1WsFgRDF записал несколько книг.
Re[5]: Разработка на чистом C
От: smeeld  
Дата: 30.10.16 18:45
Оценка: +1 -6
Здравствуйте, b0r3d0m, Вы писали:

B>Как там в стране единорогов?


Значит, кто-то всё таки пишет код вместе с пациентами клиники для склеротиков. Но не все разрабы таковыми являются, это нужно понимать.
Re[6]: Разработка на чистом C
От: b0r3d0m  
Дата: 30.10.16 19:10
Оценка: +2
B>>Как там в стране единорогов?

S>Значит, кто-то всё таки пишет код вместе с пациентами клиники для склеротиков. Но не все разрабы таковыми являются, это нужно понимать.


Я пишу код вместе с людьми, а людям, как известно, свойственно ошибаться. Вы хотите сказать, что ни разу не встречали ни одной ошибки, связанной с пропущенной деинициализацией?
Re[2]: Разработка на чистом C
От: Evgeny.Panasyuk Россия  
Дата: 30.10.16 19:26
Оценка: 3 (1) +2
Здравствуйте, kov_serg, Вы писали:

_>На чистом С несколько иной подход. Тут более важна организация кода, а не конкретные локальные приёмы.


RAII и исключения это глобальные приёмы. Про "иной подход" на C можно вот тут почитать.

_>Совершенно не обязательно тащить универсальность из C++ иногда есть требования запрещающие использование динамической памяти и вполне себе компилируется и работает на ограниченных ресурсах с заранее выделенными массивами для обработки.


C++ прекрасно работает в таких рамках.
Re[2]: Разработка на чистом C
От: push  
Дата: 30.10.16 21:00
Оценка: +8
Здравствуйте, smeeld, Вы писали:

S>Обработка исключений в С++ слишком дорогая операция,

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

S>и для проверки результатов работы каждой функции это явно не годится.

Аааа, вот оно что, вы неправильно пользуетесь исключениями.

S>RAII тоже сомнительная фича.

Это единственно адекватный способ управления ресурсами. Другой вариант — это использование сборщиков мусора, но для задач, решаемых с помощью С++ это не вариант. Да и RAII удобней в силу своей предсказуемости.

S>Сейчас всякая школота начинает пихать в конструкторы открытие дескрипторов, операции ввода/вывода, операции, требующие доступ к разделяемому ресурсу и пр.

А не школота, как я понимаю, для управления ресурсами не будет использовать RAII и при исключениях (и не только) страдать от утечек памяти, ресурсов, и недосозданных/битых объектов? А потом героических их фиксить костылями и считать, что С++ "плохой язык, потому что в нём течёт память"?

S>"тяжёлые" операции которые могут закончиться неудачно и, тем самым, экземпляр объекта будет непригоден для использования. Это глуповатый подход.

эта проблема возникает из-за отказа от RAII, как я и написал выше

S>Правильней создавать специальные фукции в духе init/create, вызывать их перед использоваием экземпляра, с проверкой результата инициализации.

Чем правильней, почему правильней?

S>Корифеи индустрии так и поступают

Ну это же смешной аргумент, не? Потому, что те же корифеи и лажу гонят тоже. Вообще это глупо создавать себе кумиров — всё надо пропускать через свой мозг.
Подход init/create используется чтобы подвязать С-код к плюсовому объекту, либо при использовании паттернов "шаблонный метод"/"строитель". А в целом для С++ оно ненужно.
Re[5]: Разработка на чистом C
От: Erop Россия  
Дата: 30.10.16 21:36
Оценка: +1
Здравствуйте, b0r3d0m, Вы писали:

B>Как там в стране единорогов?

В нашей версии страны унихренов, они же единороги, давно изобрели статический анализ кода и такую организацию владения ресурсами, что бы не быть критически зависимым от RAII, а что в вашей не в курсе...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Разработка на чистом C
От: Erop Россия  
Дата: 30.10.16 21:40
Оценка:
Здравствуйте, Lonely Dog, Вы писали:

LD>Понятно, что можно использовать alloca для автоосвобождения памяти. Но не везде это подходит (стек все-таки ограничен). Далее, в C++ есть STL с кучей полезных структур данных (а есть ведь еще Boost/Poco). В чистом C ничего такого нет, т.е. надо использовать какие-то сторонние библиотеки. Какие?


А что за С? Если 99, то там есть массивы с динамическим размером, так что alloca не особо понятно зачем нужна...
Если на С# писал, что тебе будет наверное удобно так организовывать код, что бы ресурсы регились в глобальном менеджере, и через него же и освобождались. Можно сделать такой менеджер, что бы у него можно было брать состояния, и потом к ним откатывать
Это будет работать похоже на GC в шарпе...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Разработка на чистом C
От: b0r3d0m  
Дата: 30.10.16 21:40
Оценка:
E>В нашей версии страны унихренов, они же единороги, давно изобрели статический анализ кода и такую организацию владения ресурсами, что бы не быть критически зависимым от RAII, а что в вашей не в курсе...

Статический анализ, который выявляет невызванные в определённых местах функции наподобие cleanup?

И что за организация владения ресурсами такая?
Re[3]: Разработка на чистом C
От: smeeld  
Дата: 30.10.16 22:07
Оценка:
Здравствуйте, push, Вы писали:

S>>и для проверки результатов работы каждой функции это явно не годится.

P>Аааа, вот оно что, вы неправильно пользуетесь исключениями.

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


P>Это единственно адекватный способ управления ресурсами. Другой вариант — это использование сборщиков мусора, но для задач, решаемых с помощью С++ это не вариант. Да и RAII удобней в силу своей предсказуемости.


Вот у Вас объект, он должен содержать установленное соединение с удалённым хостом, у нас RAII, поэтому соединение должно устанавливаться в конструкторе (типа никаких init/create). Более того, мы, как крутые C++-ники, обязательно сделаем emplace этого объекта в какой-нибудь контейнер, так как экземпляров этих объектов в программе создаётся много и чтоб руками их не уничтожать, нужно чтоб они уничтожались автоматически, которые не были унитожены в процессе исполнения. Так же мы не должны откатываться куда-то далеко, так как ошибка в установлении одного соединения, не значит, что будет ошибка в установлении другого соединения, при создании другого экземпляра. Что это значит? А то, что при использовании каждого экземпляра, нам нужно будет постоянно проверять, установилось ли там соединение в конструкторе при создании экземпляра, или нет. То есть, код везде будет содержать пресловутое if(is_good()) then_cool(), на что будут затрачиваться процессорные такты. На фоне этого init/create, вызываемое единожды при создании экземпляра такого объекта перед помещением его в контейнер, будет более эффективным решением. Там что-то говорили про сборщик мусора, память далеко не единственный ресурс, который может выделяться для объекта, и он самый безпроблемный по части выделения ресурс.

P>А не школота, как я понимаю, для управления ресурсами не будет использовать RAII и при исключениях (и не только) страдать от утечек памяти, ресурсов, и недосозданных/битых объектов?


Опять про память, память вообще не есть проблема.
Re[2]: Разработка на чистом C
От: uncommon Ниоткуда  
Дата: 31.10.16 02:09
Оценка: 2 (1)
Здравствуйте, smeeld, Вы писали:

S>RAII тоже сомнительная фича.


Лёня Поттер с тобой не согласен. Он предпочитает GCC cleanup attribute, а это почти что RAII.

И вообще в GCC вагон и маленькая тележка всяких расширений, без которых жизнь в C не мила.
Отредактировано 31.10.2016 2:13 uncommon . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.