Поэтому я работаю над дизайном программного обеспечения с использованием C для определенного процессора. Набор инструментов включает в себя возможность компилировать как C, так и C ++. Для того, что я делаю, в этой среде нет динамического выделения памяти, и программа в целом довольно проста. Не говоря уже о том, что устройство практически не имеет мощности процессора или ресурсов. Там действительно нет острой необходимости использовать любой C ++ вообще.
При этом есть несколько мест, где я выполняю перегрузку функций (особенность C ++). Мне нужно отправить несколько разных типов данных, и мне не хочется использовать printf
форматирование стилей с каким-либо %s
(или чем-то еще) аргументом. Я видел людей, у которых не было доступа к компилятору C ++, которые занимались printf
этим, но в моем случае поддержка C ++ доступна.
Теперь я уверен, что у меня может возникнуть вопрос, почему для начала мне нужно перегрузить функцию. Поэтому я постараюсь ответить на это прямо сейчас. Мне нужно передавать различные типы данных через последовательный порт, поэтому у меня есть несколько перегрузок, которые передают следующие типы данных:
unsigned char*
const char*
unsigned char
const char
Я бы предпочел не иметь один метод, который обрабатывает все эти вещи. Когда я вызываю функцию, я просто хочу, чтобы она передавала последовательный порт, у меня не так много ресурсов, поэтому я не хочу делать ничего, кроме своей передачи.
Кто-то еще увидел мою программу и спросил меня: «Почему вы используете файлы CPP?» Итак, это моя единственная причина. Это плохая практика?
Обновить
Я хотел бы ответить на некоторые вопросы:
Объективный ответ на вашу дилемму будет зависеть от:
Значительно ли увеличивается размер исполняемого файла, если вы используете C ++.
На данный момент размер исполняемого файла занимает 4,0% памяти программ (из 5248 байт) и 8,3% памяти данных (из 342 байт). То есть компиляция для C ++ ... Я не знаю, как это будет выглядеть для C, потому что я не использовал компилятор C. Я знаю, что эта программа больше не будет развиваться, поэтому, насколько ограничены ресурсы, я бы сказал, что я в порядке ...
Есть ли заметное негативное влияние на производительность, если вы используете C ++.
Ну, если есть, я ничего не заметил ... но опять же, возможно, поэтому я задаю этот вопрос, так как не до конца понимаю.
Возможность повторного использования кода на другой платформе, где доступен только компилятор Си.
Я знаю, что ответ на это определенно нет . На самом деле мы рассматриваем возможность перехода на другой процессор, но только на более мощные процессоры на основе ARM (все, что я знаю наверняка, имеют цепочки инструментов компилятора C ++).
//
комментировать. Если это работает, почему бы и нет?//
комментарии были в стандарте C с C99.Ответы:
Я бы не стал называть это «плохой практикой» как таковой , но я не уверен, что это действительно правильное решение вашей проблемы. Если все, что вам нужно, - это четыре отдельные функции для выполнения ваших четырех типов данных, почему бы не сделать то, что программисты на C делали с незапамятных времен:
В любом случае, это то, что компилятор C ++ делает за кулисами, и это не так уж сложно для программиста. Избегает всех проблем «почему вы пишете не совсем C с компилятором C ++» и означает, что никто в вашем проекте не будет смущен тем, какие биты C ++ «разрешены», а какие нет.
источник
#define
передать () с помощью C11_Generic
Использование только некоторых возможностей 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 в любом случае.
источник
Объективный ответ на вашу дилемму будет зависеть от:
Если на любой из вопросов ответ «да», вам лучше создать функции с разными именами для разных типов данных и придерживаться C.
Если ответы на все вопросы «нет», я не вижу причин, почему вы не должны использовать C ++.
источник
Вы задаете вопрос, как будто компиляция с C ++ просто дала вам доступ к большему количеству функций. Это не вариант. Компиляция с помощью компилятора C ++ означает, что ваш исходный код интерпретируется как исходный код C ++, а C ++ отличается от языка C. Оба имеют общее подмножество, достаточно большое для полезного программирования, но у каждого есть функции, которых нет у другого, и это Можно написать код, который приемлем на обоих языках, но интерпретируется по-разному.
Если на самом деле все, что вам нужно, это перегрузка функций, то я действительно не вижу смысла путать проблему с помощью C ++. Вместо разных функций с одинаковыми именами, отличающихся своими списками параметров, просто пишите функции с разными именами.
Что касается ваших объективных критериев,
Исполняемый файл может быть немного больше при компиляции в C ++, по сравнению с аналогичным кодом C, созданным как таковой. Как минимум, исполняемый файл C ++ будет иметь сомнительное преимущество в распределении имен C ++ для всех имен функций в той степени, в которой любые символы сохраняются в исполняемом файле. (И это на самом деле то, что обеспечивает ваши перегрузки в первую очередь.) Достаточно ли велика разница, чтобы быть важной для вас - это то, что вы должны определить экспериментальным путем.
Я сомневаюсь, что вы увидите заметную разницу в производительности кода, который вы описываете, по сравнению с гипотетическим аналогом на чистом C.
Я хотел бы пролить немного другой свет на это: если вы хотите связать код, который вы создаете с C ++, с другим кодом, то либо этот другой код также нужно будет написать на C ++ и собрать с C ++, либо вам понадобится сделать специальное условие в своем собственном коде (объявляя связь "C"), что, кроме того, вы вообще не можете сделать для своих перегруженных функций. С другой стороны, если ваш код написан на C и скомпилирован как C, то другие могут связать его с модулями C и C ++. Такую проблему обычно можно преодолеть, переворачивая таблицы, но, поскольку вам действительно не нужен C ++, тогда зачем вообще принимать такую проблему?
источник
В свое время использование компилятора C ++ в качестве «лучшего C» рекламировалось как сценарий использования. Фактически, ранний C ++ был именно таким. Основополагающим принципом проектирования было то, что вы можете использовать только те функции, которые вам нужны, и не будете нести стоимость функций, которые вы не используете. Таким образом, вы могли бы перегрузить функции (объявив намерение с помощью
overload
ключевого слова!), И остальная часть вашего проекта не только скомпилируется, но и сгенерирует код не хуже, чем компилятор C.С тех пор языки несколько разошлись.
Каждый
malloc
в вашем C-коде будет ошибкой несоответствия типов - не проблема в вашем случае без динамической памяти! Точно так же, все вашиvoid
указатели в C будут сбивать вас с толку, так как вам придется добавлять явные приведения. Но ... почему вы делаете это таким образом ... вы будете все больше и больше идти по пути использования функций C ++.Таким образом, это может быть возможно с некоторой дополнительной работой. Но это послужит воротами для более широкого внедрения C ++. Через несколько лет люди будут жаловаться на ваш унаследованный код, который выглядит так, как будто он был написан в 1989 году, поскольку они заменяют ваши
malloc
вызовыnew
, вырывают блоки кода из тел циклов, чтобы вместо этого просто вызывать алгоритм, и ругают небезопасный поддельный полиморфизм, который было бы тривиально, если бы компилятору было позволено это сделать.С другой стороны, вы знаете, что было бы все равно, если бы вы написали это на C, так что когда-либо было бы неправильно писать на C вместо C ++, учитывая его существование? Если ответ «нет», то использование функций Cherry, выбранных в C ++, также не может быть неправильным .
источник
Многие языки программирования имеют одну или несколько культур, которые развиваются вокруг них, и имеют определенные идеи о том, каким должен быть язык. Хотя должно быть возможно, практично и полезно иметь язык низкого уровня, подходящий для системного программирования, который дополняет диалект C, подходящий для этой цели, с некоторыми функциями из C ++, которые также будут подходящими, культуры, окружающие эти два языка, не ' Особенно одобряю такое слияние.
Я читал о встроенных компиляторах C, которые включали некоторые функции из C ++, в том числе возможность
интерпретируется как, по существу,
а также возможность перегрузки статических функций (проблемы искажения имен возникают только при экспортефункции перегружены). Нет причин, по которым компиляторы C не должны поддерживать такую функциональность, не налагая каких-либо дополнительных требований на среду компоновки и времени выполнения, но рефлексивный отказ многих компиляторов C почти всего от C ++ в целях избежания нагрузки на окружающую среду заставляет их отклонить даже функции, которые не навязывают такие расходы. Между тем, культура C ++ мало интересуется любой средой, которая не может поддерживать все функции этого языка. Если или пока культура С не изменится, чтобы принять такие функции, или культура С ++ не изменится, чтобы поощрять реализации легких подмножеств, «гибридный» код может страдать от многих ограничений обоих языков, хотя и ограничен в своей способности использовать Преимущества работы в комбинированном суперсет.
Если платформа поддерживает как C, так и C ++, дополнительный библиотечный код, необходимый для поддержки C ++, вероятно, сделает исполняемый файл несколько больше, хотя эффект не будет особенно значительным. Выполнение «функций C» в C ++, вероятно, не будет особенно затронуто. Более серьезная проблема может заключаться в том, как оптимизаторы C и C ++ обрабатывают различные угловые случаи. Поведение типов данных в C в основном определяется содержимым хранящихся в нем битов, а в C ++ есть распознанная категория структур (PODS - простые старые структуры данных), семантика которых также в основном определяется хранимыми битами. Однако и стандарты C, и C ++ иногда допускают поведение, противоположное тому, что подразумевается в хранимых битах, а исключения для поведения «сохраненных битов» различаются между двумя языками.
источник
Еще один важный фактор для рассмотрения: кто бы ни унаследовал ваш код.
Всегда ли этот человек будет программистом Си с компилятором Си? Я так полагаю.
Будет ли этот человек также программистом C ++ с компилятором C ++? Я хотел бы быть достаточно уверенным в этом, прежде чем сделать мой код зависимым от C ++.
источник
Полиморфизм - это действительно хорошая функция, которую C ++ предоставляет бесплатно. Однако есть и другие аспекты, которые вам, возможно, придется учитывать при выборе компилятора. Существует альтернатива полиморфизму в C, но его использование может вызвать некоторые брови. Его функция variadic, пожалуйста, обратитесь к учебнику Variadic function .
В вашем случае это было бы что-то вроде
Мне нравится подход Филлипса, но он наполняет вашу библиотеку множеством звонков. С учетом вышесказанного интерфейс чистый. У него есть свои недостатки и, в конечном счете, это вопрос выбора.
источник
Arg_type_t
иvoid *
указывает на данные. С учетом сказанного, да, предоставление (единственной) функции аргумента, указывающего тип данных, действительно является жизнеспособной альтернативой.Для вашего конкретного случая статической перегрузки «функции» для нескольких различных типов, вы могли бы рассмотреть вместо этого просто использование C11 с его
_Generic
макро- механизмом. Я чувствую, что этого может быть достаточно для ваших ограниченных потребностей.Используя ответ Филиппа Кендалла, вы можете определить:
и код
transmit(foo)
любого типаfoo
(среди четырех типов, перечисленных выше).Если вы заботитесь только о компиляторах GCC (и совместимых, например, Clang ), вы можете рассмотреть
__builtin_types_compatible_p
егоtypeof
расширение.источник
ИМХО точка зрения, да, и мне нужно стать шизофреником, чтобы ответить на этот вопрос, поскольку я люблю оба языка, но это не имеет ничего общего с эффективностью, а скорее с безопасностью и идиоматическим использованием языков.
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 восприимчивым к неопределенному поведению. Возьми это у самого Херба Саттера:Многие разработчики 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 ++, и просто используется
std::vector, dynamic_cast, unique_ptr, shared_ptr
и т. Д. В коде, который прямо или косвенно вызывается вашим кодом, полагая, что он безвреден, поскольку ваш код уже «предположительно» C ++. код. В этот момент мы сталкиваемся с вероятностью того, что что-то произойдет, и тогда, когда вы возьмете совершенно прекрасный и красивый C-код, подобный этому:... сейчас сломано. Это должно быть переписано, чтобы быть безопасным от исключений:
Валовой! Вот почему большинство разработчиков C ++ требовали бы этого:
Выше приведен RAII-совместимый безопасный код исключения, который обычно одобряют разработчики C ++, так как функция не пропустит, какая строка кода вызывает неявный выход в результате a
throw
.Выбрать язык
Вы должны либо принять систему типов и философию C ++ с RAII, безопасностью исключений, шаблонами, ООП и т. Д., Либо использовать C, который в основном вращается вокруг необработанных битов и байтов. Вы не должны создавать нечестивый брак между этими двумя языками, а вместо этого разделять их на разные языки, чтобы относиться к ним по-разному, а не стирать их вместе.
Эти языки хотят жениться на тебе. Вы обычно должны выбрать один вместо того, чтобы встречаться и дурачиться с обоими. Или вы можете быть многоженцем, как я, и жениться на обоих, но вы должны полностью поменять свое мышление, проводя время друг с другом, и держать их хорошо отделенными друг от друга, чтобы они не сражались друг с другом.
Бинарный размер
Просто из любопытства я попытался взять мою реализацию и тесты для бесплатного списка прямо сейчас и перенести ее на C ++, так как мне стало действительно интересно:
... и хотел знать, будет ли размер бинарного файла увеличиваться при создании C ++. Мне потребовалось разбросать явное приведение повсюду, что было бесполезно (одна из причин, по которой мне больше нравится писать вещи низкого уровня, такие как распределители и структуры данных в C), но заняло всего минуту.
Это было просто сравнение 64-разрядной сборки MSVC для простого консольного приложения и с кодом, который не использовал никаких функций C ++, даже перегрузку операторов - только разница между созданием его с C и использованием, скажем,
<cstdlib>
вместо<stdlib.h>
и такие вещи, но я был удивлен, обнаружив, что это не имеет никакого значения к размеру двоичного файла!Двоичный файл был
9,728
байтовым, когда он был встроен в C, а также9,278
байтами, когда он был скомпилирован как код C ++. Я на самом деле не ожидал этого. Я думал, что такие вещи, как EH, по крайней мере, добавят туда немного (думал, что это будет, по крайней мере, как сто байт по-другому), хотя, вероятно, он смог понять, что нет необходимости добавлять инструкции, связанные с EH, так как я просто используя стандартную библиотеку C и ничего не выбрасывает. Я думал что-тодобавил бы немного к двоичному размеру в любом случае, как RTTI. Во всяком случае, это было круто видеть это. Конечно, я не думаю, что вы должны обобщать этот один результат, но это, по крайней мере, меня немного впечатлило. Это также не оказало никакого влияния на тесты, и, естественно, так, поскольку я представляю, что идентичный результирующий размер двоичного файла также означал идентичные результирующие машинные инструкции.Тем не менее, кому небезразличен размер двоичного файла в связи с вопросами безопасности и разработки, упомянутыми выше? Итак, еще раз, выберите язык и примите его философию вместо того, чтобы пытаться убить его; это то, что я рекомендую.
источник