Должен ли «портативный» C компилироваться как C ++? - PullRequest
18 голосов
/ 03 апреля 2009

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

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

Ответы [ 11 ]

18 голосов
/ 03 апреля 2009

Текущий стандарт C ++ (1998) включает стандарт C (1989). Если оставить в стороне мелкий шрифт, касающийся безопасности типов, это означает, что «хороший» C89 должен прекрасно скомпилироваться в компиляторе C ++.

Проблема в том, что текущий стандарт C соответствует стандарту 1999 года (C99), который не , но официально является частью стандарта C ++ (AFAIK) (*). Это означает, что многие из «приятных» функций C99 (long long int, stdint.h, ...), в то время как поддерживается многими компиляторами C ++, не строго совместимы.

«Портативный» C означает что-то совсем другое и не имеет ничего общего с официальными стандартами ISO / ANSI. Это означает, что ваш код не делает предположений относительно среды хоста. (Размер int, порядковый номер, нестандартные функции или числа с ошибками и тому подобное.)

Из руководства по стилю кодирования, которое я однажды написал для кроссплатформенного проекта:

Кроссплатформенная ДНК (не предполагать)

  • Нет собственных типов данных. Вы можете использовать только те типы данных, которые объявлены в стандартной библиотеке.
  • char, short, int и long имеют разные размеры, как float, double и long double.
  • int - это не 32 бита.
  • char не подписан и не подписан.
  • символ не может содержать число, только символы.
  • Преобразование короткого типа в более длинный (int -> long) нарушает правила выравнивания ЦП.
  • int и int * имеют разный размер.
  • int * и long * имеют различный размер (как и указатели на любой другой тип данных).
  • Вы помните, что родные типы данных даже не существуют?
  • 'a' - 'A' не дает того же результата, что и 'z' - 'Z'.
  • 'Z' - 'A' не дает того же результата, что и 'z' - 'a', и не равно 25.
  • Вы не можете ничего сделать с указателем NULL, кроме как проверить его значение; разыменование приведет к краху системы.
  • Арифметика, включающая как подписанные, так и неподписанные типы, не работает.
  • Правила выравнивания для типов данных изменяются случайным образом.
  • Внутренняя структура типов данных изменяется случайным образом.
  • Специфическое поведение избыточных и недостаточных потоков изменяется случайным образом.
  • ABI при вызове функции изменяются случайным образом.
  • Операнды оцениваются в случайном порядке.
  • Только компилятор может обойти эту случайность. Случайность изменится со следующим выпуском CPU / OS / компилятора.
  • Для указателей == и! = Работают только для указателей на тот же тип данных.
  • <,> работают только для указателей на один и тот же массив. Они работают только для символов, явно объявленных как unsigned.
  • Вы все еще помните, что родные типы данных не существуют?
  • size_t (тип возвращаемого значения sizeof) не может быть приведен ни к какому другому типу данных.
  • ptrdiff_t (тип возвращаемого значения вычитания одного указателя из другого) не может быть преобразован в любой другой тип данных.
  • wchar_t (тип "широкого" символа, точный характер которого определяется реализацией) не может быть преобразован в любой другой тип данных.
  • Любой тип данных ..._ t нельзя преобразовать в другой тип данных

(*) : Это было правдой во время написания. С C ++ 11 все немного изменилось, но суть моего ответа верна.

16 голосов
/ 03 апреля 2009

Нет. Мой ответ Зачем искусственно ограничивать ваш код до C? имеет несколько примеров совместимого со стандартами C99, не компилируемого как C ++; ранее C имел меньше различий, но C ++ имеет более строгую типизацию и другую обработку списка аргументов функции (void).

Относительно того, есть ли преимущество в том, чтобы сделать C 'переносимым' для C ++ - конкретный проект, на который ссылался этот ответ, был виртуальной машиной для языка, основанного на признаках, поэтому не соответствует объектной модели C ++ и имеет Во многих случаях вы извлекаете void* из стека интерпретатора и затем конвертируете в структуры, представляющие макет встроенных типов объектов. Чтобы сделать код «переносимым» на C ++, нужно было бы добавить много приведений, которые ничего не делают для безопасности типов.

8 голосов
/ 03 апреля 2009

Переносимость означает написание вашего кода таким образом, чтобы он компилировался и имел одинаковое поведение с использованием разных компиляторов и / или разных платформ (т.е. полагался на поведение, предписанное стандартом (ами) ISO, где это возможно).

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

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

3 голосов
/ 03 апреля 2009

Нет, «portable» не означает «компилируется на компиляторе C ++», это означает «компилируется на любом стандартном компиляторе C» с согласованным, определенным поведением.

И не лишайте себя, скажем, улучшений C99 только для поддержания совместимости с C ++.

Но пока поддержание совместимости не связывает вам руки, если вы можете избежать использования «классов», «виртуальных» и тому подобного, тем лучше. Если вы пишете с открытым исходным кодом, кто-то может захотеть перенести ваш код на C ++; если вы берете на прокат, ваша компания / клиент может захотеть портировать когда-нибудь в будущем. эй, может быть, вы даже захотите портировать его на C ++ в будущем

Быть «хорошим управляющим», не оставляя мусора у костра, - это просто хорошая карма во всем, что ты делаешь.

И, пожалуйста, постарайтесь не допустить несовместимости ваших заголовков. Даже если ваш код никогда не переносится, людям, возможно, придется ссылаться на него из C ++, поэтому иметь заголовок, в котором не используются зарезервированные слова C ++, здорово.

2 голосов
/ 06 января 2010

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

char * p = (char *)malloc(100);

против

char * p = malloc(100);

и когда я пишу "объектно-ориентированный" модуль библиотеки C, мне действительно нравится использовать 'this' в качестве моего указателя на объект, и поскольку это ключевое слово C ++, оно не будет компилироваться в C ++ (это намеренно, так как модули такого типа бессмысленно в C ++, учитывая, что они существуют как таковые в stl и библиотеках).

2 голосов
/ 20 мая 2009

FWIW, как только проект наберет определенный размер и импульс, не исключено, что он действительно выиграет от совместимости с C ++: даже если он не будет перенесен на C ++ напрямую, на самом деле много современные инструменты, связанные с работой / обработкой исходного кода C ++.

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

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

Посмотрите, например, на проекты linux (kernel) или gcc, оба из которых в основном "только C", однако в обоих сообществах разработчиков регулярно обсуждаются потенциальные выгоды от перехода на C ++.

И на самом деле, в настоящее время продолжаются усилия gcc ( в дереве FSF!) По переносу источников gcc в допустимый синтаксис C ++ (см. gcc-in-cxx для детали), так что компилятор C ++ может быть использован для компиляции исходного кода.

Это было в основном инициировано долгосрочным хакером gcc: Ианом Лэнсом Тейлором.

Первоначально, это только для того, чтобы обеспечить лучшую проверку ошибок, а также улучшенную совместимость (то есть, когда этот шаг завершен, это означает, что вам не обязательно иметь компилятор C для компиляции gcc, вы могли бы также просто используйте компилятор C ++, если вы оказались «просто» разработчиком C ++, и это то, что вы получили в любом случае).

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

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

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

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

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

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

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

2 голосов
/ 03 апреля 2009

Определенно распространенной практикой является компиляция кода C с использованием компилятора C ++ для более строгой проверки типов. Несмотря на то, что существуют специальные инструменты для C, например lint, более удобно использовать компилятор C ++.

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

Если вам нужно строго придерживаться кода C, вам следует быть осторожным.

2 голосов
/ 03 апреля 2009

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

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

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

1 голос
/ 03 апреля 2009

AFAIK весь код в классическом тексте. Язык программирования C, второе издание, может быть скомпилирован с использованием стандартных компиляторов C ++, таких как GCC (g ++). Если ваш код на C соответствует стандартам, которым придерживается этот классический текст, то он достаточно хорош, и вы готовы скомпилировать код на C, используя компилятор C ++.

Возьмем пример исходного кода ядра Linux, который в основном написан на C с использованием некоторого встроенного кода на ассемблере, это кошмарная компиляция кода ядра Linux с помощью компилятора C ++, потому что из-за наименьшей возможной причины используется «new» как имя переменной в коде ядра Linux, где C ++ не позволяет использовать «new» в качестве имени переменной. Я просто привожу один пример здесь. Помните, что ядро ​​linux переносимо, компилируется и отлично работает в архитектурах intel, ppc, sparc и т. Д. Это просто для иллюстрации того, что переносимость имеет разные значения в мире программного обеспечения. Если вы хотите скомпилировать код C с использованием компилятора C ++, вы переносите свою кодовую базу с C на C ++. Я вижу его как два разных языка программирования по самой очевидной причине, что программисты на C не очень любят C ++. Но мне они оба нравятся, и я их очень много использую. Ваш код на C является переносимым, но вы должны убедиться, что вы следуете стандартным методам, чтобы ваш код на C ++ был переносимым, пока вы переносите код на C ++. Читайте дальше, чтобы узнать, откуда вы получите стандартные методы.

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

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

При выборе переносимости используйте следующие правила:

a) Нужно ли компилировать ваш код (C или C ++) на разных архитектурах, возможно, с использованием собственных компиляторов C / C ++? б) Изучите компиляторы C / C ++ на различных архитектурах, которые вы хотите запустить в своей программе, и планируйте перенос кода. Инвестируйте хорошее время на это. c) Насколько это возможно, старайтесь обеспечить чистый уровень разделения между кодом C и кодом C ++. Если ваш код C является переносимым, вам просто нужно снова написать обертки C ++ вокруг этого переносимого кода C, используя методы переносимого кодирования C ++.

Обратимся к некоторым хорошим книгам по C ++ о том, как писать переносимый код на C ++. Я лично рекомендую язык программирования C ++ от самого Бьярна Страуструпа, серию Effective C ++ от Scott meyers и популярные статьи о DDJ на сайте www.ddj.com.

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

1 голос
/ 03 апреля 2009

Нет, возможность компилирования в C ++ не является обычной интерпретацией переносимого. Работая с действительно старым кодом, объявления в стиле K & R очень переносимы, но не могут быть скомпилированы в C ++.

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

Да, хорошо поддерживать как можно больше совместимость с C ++ - у других людей может быть веская причина для необходимости компилировать код на C как C ++. Например, если они хотят включить его в приложение MFC, им придется создать простой C в отдельной DLL или библиотеке, а не просто включать ваш код в один проект.

Существует также аргумент, что запуск компилятора в режиме C ++ может обнаруживать незначительные ошибки, в зависимости от компилятора, если он применяет различные оптимизации.

...