Плохо ли использовать компилятор C ++ только для перегрузки функций?

60

Поэтому я работаю над дизайном программного обеспечения с использованием C для определенного процессора. Набор инструментов включает в себя возможность компилировать как C, так и C ++. Для того, что я делаю, в этой среде нет динамического выделения памяти, и программа в целом довольно проста. Не говоря уже о том, что устройство практически не имеет мощности процессора или ресурсов. Там действительно нет острой необходимости использовать любой C ++ вообще.

При этом есть несколько мест, где я выполняю перегрузку функций (особенность C ++). Мне нужно отправить несколько разных типов данных, и мне не хочется использовать printfформатирование стилей с каким-либо %s(или чем-то еще) аргументом. Я видел людей, у которых не было доступа к компилятору C ++, которые занимались printfэтим, но в моем случае поддержка C ++ доступна.

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

unsigned char*
const char*
unsigned char
const char

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

Кто-то еще увидел мою программу и спросил меня: «Почему вы используете файлы CPP?» Итак, это моя единственная причина. Это плохая практика?

Обновить

Я хотел бы ответить на некоторые вопросы:

Объективный ответ на вашу дилемму будет зависеть от:

  1. Значительно ли увеличивается размер исполняемого файла, если вы используете C ++.

На данный момент размер исполняемого файла занимает 4,0% памяти программ (из 5248 байт) и 8,3% памяти данных (из 342 байт). То есть компиляция для C ++ ... Я не знаю, как это будет выглядеть для C, потому что я не использовал компилятор C. Я знаю, что эта программа больше не будет развиваться, поэтому, насколько ограничены ресурсы, я бы сказал, что я в порядке ...

  1. Есть ли заметное негативное влияние на производительность, если вы используете C ++.

Ну, если есть, я ничего не заметил ... но опять же, возможно, поэтому я задаю этот вопрос, так как не до конца понимаю.

  1. Возможность повторного использования кода на другой платформе, где доступен только компилятор Си.

Я знаю, что ответ на это определенно нет . На самом деле мы рассматриваем возможность перехода на другой процессор, но только на более мощные процессоры на основе ARM (все, что я знаю наверняка, имеют цепочки инструментов компилятора C ++).

шпионить
источник
59
Мне известно, что я использую C ++ для проекта, использующего только функции C, чтобы иметь возможность //комментировать. Если это работает, почему бы и нет?
Жюль
76
Плохой практикой будет ограничивать себя C, если вы хорошо используете функции, которые он не предоставляет.
Джерри Коффин
35
Когда вы говорите «используйте компилятор C ++», вы имеете в виду «используйте C ++». Просто сказать это. Вы не можете скомпилировать C с помощью компилятора C ++, но вы можете легко переключаться с C на C ++, что бы вы на самом деле делали.
user253751
4
«Для того, что я делаю, динамическое распределение памяти на процессоре отсутствует, и программа в целом довольно проста. Не говоря уже о том, что устройство почти не имеет мощности или ресурсов процессора. На самом деле нет никакой необходимости использовать какой-либо C ++». Я надеюсь, что первое из этих двух предложений должно послужить причиной не использовать C ++, потому что они довольно плохие, если они есть. C ++ прекрасно подходит для использования со встроенными системами.
Pharap
21
@Jules Я уверен, что вы это знаете и давно обдумываете, но на тот случай, если кто-то читает это: //комментарии были в стандарте C с C99.
Дэвислор

Ответы:

77

Я бы не стал называть это «плохой практикой» как таковой , но я не уверен, что это действительно правильное решение вашей проблемы. Если все, что вам нужно, - это четыре отдельные функции для выполнения ваших четырех типов данных, почему бы не сделать то, что программисты на C делали с незапамятных времен:

void transmit_uchar_buffer(unsigned char *buffer);
void transmit_char_buffer(char *buffer);
void transmit_uchar(unsigned char c);
void transmit_char(char c);

В любом случае, это то, что компилятор C ++ делает за кулисами, и это не так уж сложно для программиста. Избегает всех проблем «почему вы пишете не совсем C с компилятором C ++» и означает, что никто в вашем проекте не будет смущен тем, какие биты C ++ «разрешены», а какие нет.

Филип Кендалл
источник
8
Я бы сказал, почему нет, потому что передаваемый тип (вероятно) является деталью реализации, поэтому его скрытие и предоставление компилятору возможности выбора реализации может привести к более читаемому коду. И если использование функции C ++ улучшает читабельность, то почему бы не сделать это?
Жюль
29
Можно даже #defineпередать () с помощью C11_Generic
Deduplicator
16
@Jules Потому что тогда очень сложно понять, какие функции C ++ разрешено использовать в проекте. Будет ли отклонено потенциальное изменение, содержащее объекты? Как насчет шаблонов? Комментарии в стиле C ++? Конечно, вы можете обойти это с документом в стиле кодирования, но если все, что вы делаете, это простой случай перегрузки функции, тогда просто напишите C вместо этого.
Филипп Кендалл,
25
@Phillip «Комментарии в стиле C ++» действительны на C уже более десяти лет.
Дэвид Конрад
12
@Jules: в то время как передаваемый тип, вероятно, является деталью реализации программного обеспечения, которое составляет страховые кавычки, приложение OP звучит как встроенная система, выполняющая последовательную связь, где тип и размер данных имеют большое значение.
whatsisname
55

Использование только некоторых возможностей C ++, в то время как в противном случае его трактуют как C, не совсем обычное явление, но и не совсем неслыханное. Фактически, некоторые люди вообще не используют никаких функций в C ++, кроме более строгой и более мощной проверки типов. Они просто пишут на C (стараясь писать только в общем пересечении C ++ и C), затем компилируют с помощью компилятора C ++ для проверки типов и компилятора C для генерации кода (или просто все время придерживаются компилятора C ++).

Linux является примером кодовой базы, где люди регулярно спрашивают, что идентификаторы, такие как, classпереименовываются в klassили kclass, чтобы они могли компилировать Linux с помощью компилятора C ++. Очевидно, учитывая мнение Линуса о C ++ , их всегда сбивают с толку: - GCC - это пример кодовой базы, которая сначала была преобразована в «C ++ - чистый C», а затем постепенно реорганизована для использования большего количества функций C ++.

Нет ничего плохого в том, что вы делаете. Если вы действительно параноидально относитесь к качеству генерации кода вашего компилятора C ++, вы можете использовать компилятор, такой как Comeau C ++, который компилируется в C в качестве целевого языка, а затем использовать компилятор C. Таким образом, вы также можете проводить выборочные проверки кода, чтобы увидеть, внедряет ли C ++ какой-либо непредвиденный код, чувствительный к производительности. Это не должно иметь место для простой перегрузки, которая буквально просто автоматически генерирует функции с разными именами - IOW, именно то, что вы будете делать в C в любом случае.

Йорг Миттаг
источник
15

Объективный ответ на вашу дилемму будет зависеть от:

  1. Значительно ли увеличивается размер исполняемого файла, если вы используете C ++.
  2. Есть ли заметное негативное влияние на производительность, если вы используете C ++.
  3. Возможность повторного использования кода на другой платформе, где доступен только компилятор Си.

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

Если ответы на все вопросы «нет», я не вижу причин, почему вы не должны использовать C ++.

Р Саху
источник
9
Я должен сказать, что никогда не сталкивался с компилятором C ++, который генерирует значительно худший код, чем компилятор C для кода, написанного в общем подмножестве двух языков. Компиляторы C ++ получают плохую репутацию как по размеру, так и по производительности, но, по моему опыту, это всегда нецелесообразное использование функций C ++, которые вызывали проблему ... В частности, если вас беспокоит размер, не используйте iostreams и не используйте шаблоны, но в остальном у вас должно быть все в порядке.
Жюль
@Jules: Как раз то, что это стоит (не так много, IMO), я видел, что было продано в качестве одного компилятора для C и C ++ (Turbo C ++ 1.0, если память служит), дают значительно разные результаты для идентичного ввода. Однако, насколько я понимаю, это было до того, как они закончили свой собственный компилятор C ++, поэтому, несмотря на то, что он выглядел как один компилятор снаружи, у него действительно было два совершенно отдельных компилятора - один для C, другой для C ++.
Джерри Коффин
1
@JerryCoffin Если память не изменяет, продукты Turbo никогда не имели отличную репутацию. И если бы это была версия 1.0, вы могли бы быть извинены за то, что не были очень усовершенствованы. Так что это, вероятно, не очень представительный.
Бармар
10
@ Бармар: На самом деле, у них довольно долгое время была довольно приличная репутация. Их плохая репутация в настоящее время обусловлена ​​главным образом их возрастом. Они конкурируют с другими компиляторами того же винтажа, но никто не задает вопросов о том, как сделать что-то с gcc 1.4 (или чем-то еще). Но вы правы - это не очень представительно.
Джерри Коффин
1
@Jules Я бы сказал, что даже шаблоны в порядке. Несмотря на популярную теорию, создание шаблона не приводит к неявному увеличению размера кода, размер кода обычно не увеличивается до тех пор, пока не будет использована функция из шаблона (в этом случае увеличение размера будет пропорционально размеру функции) или если шаблон не будет объявление статических переменных. Правила встраивания все еще применяются, поэтому можно писать шаблоны, в которых все функции встроены компилятором.
Pharap
13

Вы задаете вопрос, как будто компиляция с C ++ просто дала вам доступ к большему количеству функций. Это не вариант. Компиляция с помощью компилятора C ++ означает, что ваш исходный код интерпретируется как исходный код C ++, а C ++ отличается от языка C. Оба имеют общее подмножество, достаточно большое для полезного программирования, но у каждого есть функции, которых нет у другого, и это Можно написать код, который приемлем на обоих языках, но интерпретируется по-разному.

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

Что касается ваших объективных критериев,

  1. Значительно ли увеличивается размер исполняемого файла, если вы используете C ++.

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

  1. Есть ли заметное негативное влияние на производительность, если вы используете C ++.

Я сомневаюсь, что вы увидите заметную разницу в производительности кода, который вы описываете, по сравнению с гипотетическим аналогом на чистом C.

  1. Возможность повторного использования кода на другой платформе, где доступен только компилятор Си.

Я хотел бы пролить немного другой свет на это: если вы хотите связать код, который вы создаете с C ++, с другим кодом, то либо этот другой код также нужно будет написать на C ++ и собрать с C ++, либо вам понадобится сделать специальное условие в своем собственном коде (объявляя связь "C"), что, кроме того, вы вообще не можете сделать для своих перегруженных функций. С другой стороны, если ваш код написан на C и скомпилирован как C, то другие могут связать его с модулями C и C ++. Такую проблему обычно можно преодолеть, переворачивая таблицы, но, поскольку вам действительно не нужен C ++, тогда зачем вообще принимать такую ​​проблему?

Джон Боллинджер
источник
4
«Исполняемый файл может быть немного больше при компиляции в C ++ ... в той степени, в которой любые символы сохраняются в исполняемом файле». Однако, чтобы быть справедливым, любой приличный набор инструментов для оптимизации должен иметь опцию компоновщика для удаления этих символов в сборках «релиза», что означает, что они будут только раздувать ваши отладочные сборки. Я не думаю, что это большая потеря. На самом деле, это чаще всего выгода.
Коди Грей,
5

В свое время использование компилятора C ++ в качестве «лучшего C» рекламировалось как сценарий использования. Фактически, ранний C ++ был именно таким. Основополагающим принципом проектирования было то, что вы можете использовать только те функции, которые вам нужны, и не будете нести стоимость функций, которые вы не используете. Таким образом, вы могли бы перегрузить функции (объявив намерение с помощью overloadключевого слова!), И остальная часть вашего проекта не только скомпилируется, но и сгенерирует код не хуже, чем компилятор C.

С тех пор языки несколько разошлись.

Каждый mallocв вашем C-коде будет ошибкой несоответствия типов - не проблема в вашем случае без динамической памяти! Точно так же, все ваши voidуказатели в C будут сбивать вас с толку, так как вам придется добавлять явные приведения. Но ... почему вы делаете это таким образом ... вы будете все больше и больше идти по пути использования функций C ++.

Таким образом, это может быть возможно с некоторой дополнительной работой. Но это послужит воротами для более широкого внедрения C ++. Через несколько лет люди будут жаловаться на ваш унаследованный код, который выглядит так, как будто он был написан в 1989 году, поскольку они заменяют ваши mallocвызовы new, вырывают блоки кода из тел циклов, чтобы вместо этого просто вызывать алгоритм, и ругают небезопасный поддельный полиморфизм, который было бы тривиально, если бы компилятору было позволено это сделать.

С другой стороны, вы знаете, что было бы все равно, если бы вы написали это на C, так что когда-либо было бы неправильно писать на C вместо C ++, учитывая его существование? Если ответ «нет», то использование функций Cherry, выбранных в C ++, также не может быть неправильным .

JDługosz
источник
2

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

Я читал о встроенных компиляторах C, которые включали некоторые функции из C ++, в том числе возможность

foo.someFunction(a,b,c);

интерпретируется как, по существу,

typeOfFoo_someFunction(foo, a, b, c);

а также возможность перегрузки статических функций (проблемы искажения имен возникают только при экспортефункции перегружены). Нет причин, по которым компиляторы C не должны поддерживать такую ​​функциональность, не налагая каких-либо дополнительных требований на среду компоновки и времени выполнения, но рефлексивный отказ многих компиляторов C почти всего от C ++ в целях избежания нагрузки на окружающую среду заставляет их отклонить даже функции, которые не навязывают такие расходы. Между тем, культура C ++ мало интересуется любой средой, которая не может поддерживать все функции этого языка. Если или пока культура С не изменится, чтобы принять такие функции, или культура С ++ не изменится, чтобы поощрять реализации легких подмножеств, «гибридный» код может страдать от многих ограничений обоих языков, хотя и ограничен в своей способности использовать Преимущества работы в комбинированном суперсет.

Если платформа поддерживает как C, так и C ++, дополнительный библиотечный код, необходимый для поддержки C ++, вероятно, сделает исполняемый файл несколько больше, хотя эффект не будет особенно значительным. Выполнение «функций C» в C ++, вероятно, не будет особенно затронуто. Более серьезная проблема может заключаться в том, как оптимизаторы C и C ++ обрабатывают различные угловые случаи. Поведение типов данных в C в основном определяется содержимым хранящихся в нем битов, а в C ++ есть распознанная категория структур (PODS - простые старые структуры данных), семантика которых также в основном определяется хранимыми битами. Однако и стандарты C, и C ++ иногда допускают поведение, противоположное тому, что подразумевается в хранимых битах, а исключения для поведения «сохраненных битов» различаются между двумя языками.

Supercat
источник
1
Интересное мнение, но не затрагивает вопрос ОП.
Р Саху
@RSahu: код, который соответствует «культуре», окружающей язык, лучше поддерживать и легче адаптировать к множеству реализаций, чем код, который этого не делает. Культуры C и C ++ не имеют ничего общего с типом использования, который предлагает OP, я добавлю несколько более конкретных моментов в отношении конкретных вопросов.
суперкат
1
Обратите внимание, что в стандартном комитете C ++ есть встроенная / финансовая / игровая группа, которая нацелена на решение именно тех ситуаций, в которых «небольшой интерес», по вашему мнению, отбрасывается.
Якк
@Yakk: Признаюсь, я не очень внимательно следил за миром C ++, поскольку мои интересы в основном связаны с C; Я знаю, что были предприняты усилия для большего количества встроенных диалектов, но я не думал, что они действительно получили где-нибудь.
суперкат
2

Еще один важный фактор для рассмотрения: кто бы ни унаследовал ваш код.

Всегда ли этот человек будет программистом Си с компилятором Си? Я так полагаю.

Будет ли этот человек также программистом C ++ с компилятором C ++? Я хотел бы быть достаточно уверенным в этом, прежде чем сделать мой код зависимым от C ++.

reinierpost
источник
2

Полиморфизм - это действительно хорошая функция, которую C ++ предоставляет бесплатно. Однако есть и другие аспекты, которые вам, возможно, придется учитывать при выборе компилятора. Существует альтернатива полиморфизму в C, но его использование может вызвать некоторые брови. Его функция variadic, пожалуйста, обратитесь к учебнику Variadic function .

В вашем случае это было бы что-то вроде

enum serialDataTypes
{
 INT =0,
 INT_P =1,
 ....
 }Arg_type_t;
....
....
void transmit(Arg_type_t serial_data_type, ...)
{
  va_list args;
  va_start(args, serial_data_type);

  switch(serial_data_type)
  {
    case INT: 
    //Send integer
    break;

    case INT_P:
    //Send data at integer pointer
    break;
    ...
   }
 va_end(args);
}

Мне нравится подход Филлипса, но он наполняет вашу библиотеку множеством звонков. С учетом вышесказанного интерфейс чистый. У него есть свои недостатки и, в конечном счете, это вопрос выбора.

nikhil_kotian
источник
Функции Variadic в первую очередь используются в тех случаях, когда число аргументов и их типы являются переменными. В противном случае, вам это не нужно, и, в частности, это слишком для вашего конкретного примера. Было бы проще использовать обычную функцию, которая принимает Arg_type_tи void *указывает на данные. С учетом сказанного, да, предоставление (единственной) функции аргумента, указывающего тип данных, действительно является жизнеспособной альтернативой.
Джон Боллинджер
1

Для вашего конкретного случая статической перегрузки «функции» для нескольких различных типов, вы могли бы рассмотреть вместо этого просто использование C11 с его _Genericмакро- механизмом. Я чувствую, что этого может быть достаточно для ваших ограниченных потребностей.

Используя ответ Филиппа Кендалла, вы можете определить:

#define transmit(X) _Generic((X),      \
   unsigned char*: transmit_uchar_buffer, \
   char*: transmit_char_buffer,           \
   unsigned char: transmit_uchar,         \
   char: transmit_char) ((X))

и код transmit(foo) любого типа foo(среди четырех типов, перечисленных выше).

Если вы заботитесь только о компиляторах GCC (и совместимых, например, Clang ), вы можете рассмотреть __builtin_types_compatible_pего typeofрасширение.

Василий Старынкевич
источник
1

Плохо ли использовать компилятор C ++ только для перегрузки функций?

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

C сторона

С точки зрения C, я считаю настолько расточительным, чтобы ваш код требовал C ++ просто для использования перегрузки функций. Если вы не используете его для статического полиморфизма с шаблонами C ++, это такой тривиальный синтаксический сахар, полученный в обмен на переключение на совершенно другой язык. Кроме того, если вы когда-нибудь захотите экспортировать свои функции в dylib (может быть, а может и не иметь практического значения), вы больше не сможете делать это практически для широкого использования со всеми именованными символами.

Сторона C ++

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

Ваш обычный вид кода на C достаточно разумно и «безопасно» писать, если вы работаете с системой типов C, которая запрещает такие вещи, как копирование ctors в structs. Как только вы работаете в гораздо более богатой системе типов C ++, ежедневные функции, которые имеют огромное значение, например, memsetи memcpyне становятся функциями, вам следует постоянно полагаться. Вместо этого это функции, которых вы обычно хотите избежать, как чумы, поскольку с типами C ++ вам не следует рассматривать их как необработанные биты и байты, которые нужно копировать, перетасовывать и освобождать. Даже если в данный момент ваш код использует только такие вещи, как memsetпримитивы и UDT POD, в тот момент, когда кто-либо добавляет ctor к любому используемому вами UDT (включая просто добавление члена, для которого он нужен, например,std::unique_ptrчлен) против таких функций или виртуальной функции или чего-либо в этом роде, она делает все ваше обычное кодирование в стиле C восприимчивым к неопределенному поведению. Возьми это у самого Херба Саттера:

memcpyи memcmpнарушать систему типов. Использование memcpyдля копирования объектов похоже на зарабатывание денег с помощью ксерокса. Использование memcmpдля сравнения объектов похоже на сравнение леопардов путем подсчета их пятен. Инструменты и методы могут показаться выполненными, но они слишком грубые, чтобы делать это приемлемо. Все объекты C ++ предназначены для сокрытия информации (возможно, самый прибыльный принцип в разработке программного обеспечения; см. Пункт 11): объекты скрывают данные (см. Пункт 41) и разрабатывают точные абстракции для копирования этих данных с помощью конструкторов и операторов присваивания (см. Пункты с 52 по 55) , Преодоление всего этого memcpyявляется серьезным нарушением сокрытия информации и часто приводит к утечкам памяти и ресурсов (в лучшем случае), сбоям (хуже) или неопределенному поведению (хуже всего) - стандартам кодирования C ++.

Многие разработчики C не согласны с этим, и это справедливо, поскольку философия применима только в том случае, если вы пишете код на C ++. Вы , скорее всего , будете писать очень проблемный код , если вы используете такие функции , как memcpyвсе время в коде , который строит , как C ++ , но это совершенно нормально , если вы делаете это в C . Эти два языка сильно отличаются в этом отношении из-за различий в системе типов. Очень заманчиво взглянуть на подмножество функций, общих для этих двух, и поверить, что одну можно использовать как другую, особенно на стороне C ++, но код C + (или код C--), как правило, гораздо более проблематичен, чем и C, и C ++ код.

Аналогично, вы не должны использовать, скажем, mallocв контексте стиля C (что подразумевает отсутствие EH), если он может напрямую вызывать любые функции C ++, которые могут генерировать, поскольку в результате вы получаете неявную точку выхода в своей функции в результате исключение, которое вы не можете эффективно отловить при написании кода в стиле C, до того, как сможете использовать freeэту память. Таким образом , всякий раз , когда у вас есть файл , который строит в C ++ с .cppрасширением или что - то , и он делает все эти типы вещей , как malloc, memcpy, memset,qsortи т. д., тогда он задает проблемы в дальнейшем, если это не так, если только это не деталь реализации класса, который работает только с примитивными типами, и в этот момент ему по-прежнему необходимо выполнять обработку исключений, чтобы быть безопасным для исключений. Если вы пишете код на Си ++ вместо этого вы хотите , чтобы в основном полагаться на RAII и использовать такие вещи , как vector, unique_ptr, shared_ptrи т.д., и избежать все нормальное кодирования C-стиля , когда это возможно.

Причина, по которой вы можете играть с бритвенными лезвиями в C и рентгеновских типах данных и играть с их битами и байтами, не будучи склонной к побочному ущербу в команде (хотя вы все равно можете по-настоящему навредить себе в любом случае), не в том, что C типы могут делать, но из-за того, что они никогда не смогут сделать, В тот момент, когда вы расширите систему типов C, включив в нее такие функции C ++, как ctors, dtors и vtables, а также обработку исключений, весь идиоматический код C будет представлен намного, гораздо более опасным, чем в настоящее время, и вы увидите новый вид развитие философии и менталитета, которые будут поощрять совершенно другой стиль кодирования, как вы видите в C ++, который теперь рассматривает даже использование грубых указателей на ошибки для класса, который управляет памятью, в отличие от, скажем, ресурса, соответствующего RAII unique_ptr. Это мышление не развивалось из абсолютного чувства безопасности. Он развился из того, что C ++ определенно должен быть защищен от таких функций, как обработка исключений, учитывая то, что он просто позволяет использовать в своей системе типов.

Исключение-безопасность

Опять же, в тот момент, когда вы находитесь на земле C ++, люди будут ожидать, что ваш код будет безопасным для исключений. Люди могут поддерживать ваш код в будущем, учитывая, что он уже написан и компилируется в C ++, и просто используется std::vector, dynamic_cast, unique_ptr, shared_ptrи т. Д. В коде, который прямо или косвенно вызывается вашим кодом, полагая, что он безвреден, поскольку ваш код уже «предположительно» C ++. код. В этот момент мы сталкиваемся с вероятностью того, что что-то произойдет, и тогда, когда вы возьмете совершенно прекрасный и красивый C-код, подобный этому:

int some_func(int n, ...)
{
    int* x = calloc(n, sizeof(int));
    if (x)
    {
        f(n, x); // some function which, now being a C++ function, may 
                 // throw today or in the future.
        ...
        free(x);
        return success;
    }
    return fail;
}

... сейчас сломано. Это должно быть переписано, чтобы быть безопасным от исключений:

int some_func(int n, ...)
{
    int* x = calloc(n, sizeof(int));
    if (x)
    {
        try
        {
            f(n, x); // some function which, now being a C++ function, may 
                     // throw today or in the future (maybe someone used
                     // std::vector inside of it).
        }
        catch (...)
        {
            free(x);
            throw;
        }
        ...
        free(x);
        return success;
    }
    return fail;
}

Валовой! Вот почему большинство разработчиков C ++ требовали бы этого:

void some_func(int n, ...)
{
    vector<int> x(n);
    f(x); // some function which, now being a C++ function, may throw today
          // or in the future.
}

Выше приведен RAII-совместимый безопасный код исключения, который обычно одобряют разработчики C ++, так как функция не пропустит, какая строка кода вызывает неявный выход в результате a throw.

Выбрать язык

Вы должны либо принять систему типов и философию C ++ с RAII, безопасностью исключений, шаблонами, ООП и т. Д., Либо использовать C, который в основном вращается вокруг необработанных битов и байтов. Вы не должны создавать нечестивый брак между этими двумя языками, а вместо этого разделять их на разные языки, чтобы относиться к ним по-разному, а не стирать их вместе.

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

Бинарный размер

Просто из любопытства я попытался взять мою реализацию и тесты для бесплатного списка прямо сейчас и перенести ее на C ++, так как мне стало действительно интересно:

[...] не знаю, как это будет выглядеть для C, потому что я не использовал компилятор C.

... и хотел знать, будет ли размер бинарного файла увеличиваться при создании C ++. Мне потребовалось разбросать явное приведение повсюду, что было бесполезно (одна из причин, по которой мне больше нравится писать вещи низкого уровня, такие как распределители и структуры данных в C), но заняло всего минуту.

Это было просто сравнение 64-разрядной сборки MSVC для простого консольного приложения и с кодом, который не использовал никаких функций C ++, даже перегрузку операторов - только разница между созданием его с C и использованием, скажем, <cstdlib>вместо <stdlib.h>и такие вещи, но я был удивлен, обнаружив, что это не имеет никакого значения к размеру двоичного файла!

Двоичный файл был 9,728байтовым, когда он был встроен в C, а также 9,278байтами, когда он был скомпилирован как код C ++. Я на самом деле не ожидал этого. Я думал, что такие вещи, как EH, по крайней мере, добавят туда немного (думал, что это будет, по крайней мере, как сто байт по-другому), хотя, вероятно, он смог понять, что нет необходимости добавлять инструкции, связанные с EH, так как я просто используя стандартную библиотеку C и ничего не выбрасывает. Я думал что-тодобавил бы немного к двоичному размеру в любом случае, как RTTI. Во всяком случае, это было круто видеть это. Конечно, я не думаю, что вы должны обобщать этот один результат, но это, по крайней мере, меня немного впечатлило. Это также не оказало никакого влияния на тесты, и, естественно, так, поскольку я представляю, что идентичный результирующий размер двоичного файла также означал идентичные результирующие машинные инструкции.

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


источник