Как мне реализовать что-то похожее на директиву компилятора Objective-C @encode() в ANSI C?

Директива @encode возвращает const char *, который представляет собой закодированный дескриптор типа различных элементов переданного типа данных. Ниже приведен пример:

struct test
{ int ti ;
  char tc ;
} ;

printf( "%s", @encode(struct test) ) ;
// returns "{test=ic}"

Я мог видеть использование sizeof() для определения примитивных типов, а если бы это был полный объект, я мог бы использовать методы класса для самоанализа.

Однако как он определяет каждый элемент непрозрачной структуры?


person Anderson    schedule 12.02.2010    source источник
comment
Он может их определить, поскольку является частью реализации компилятора. Ваш код не является, поэтому он не может.   -  person    schedule 13.02.2010
comment
Вы не можете сделать '@encode(blob)', не касаясь компилятора, но согласитесь ли вы на что-то вроде 'encode(blob, size)', в котором вы также передаете размер структуры функции кодирования? Это было бы довольно просто, хотя и не было бы переносимым или эффективным, поскольку не знало бы упаковки/порядка байтов/выравнивания структуры.   -  person Casey Barker    schedule 18.02.2010
comment
но он все равно не сможет узнать, что такое каждый элемент структуры...   -  person Anderson    schedule 18.02.2010
comment
Нет, но ему не нужно знать структуру данных, чтобы превратить их в плоский поток байтов, если все, что вам нужно сделать, это позже воспроизвести объект с помощью decode(). Так что, пока структура остается неизменной, вы можете просто использовать memcpy() для необработанных данных. У вас будет несколько фиктивных байтов заполнения, а любые многобайтовые поля будут в форме с целевым порядком байтов, но это будет работать в том смысле, что данные, которые вы закодировали в поток байтов, затем могут быть декодированы в ту же структуру. Это, конечно, глупо, но я не был уверен, к чему вы стремитесь. Если это кажется очевидным и тривиальным, просто игнорируйте меня. :)   -  person Casey Barker    schedule 18.02.2010


Ответы (3)


Ответ @Lothars может быть «циничным», но, к сожалению, он довольно близок к истине. Чтобы реализовать что-то вроде @encode(), вам нужен полноценный синтаксический анализатор для извлечения информации о типе. Ну, по крайней мере, для всего, кроме «тривиальных» операторов @encode() (т. е. @encode(char *)). Современные компиляторы обычно имеют два или три основных компонента:

  • Передняя часть.
  • Промежуточный конец (для некоторых компиляторов).
  • Задний конец.

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

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

Компиляторы, у которых есть «промежуточный конец», обычно делают это из-за какой-то необходимости: они поддерживают несколько «внешних интерфейсов», возможно, состоящих из совершенно разных языков. Другая причина заключается в упрощении оптимизации: все этапы оптимизации работают с одним и тем же промежуточным представлением. Набор компиляторов gcc является примером "трехступенчатого" компилятора. llvm можно считать компилятором «промежуточного и внутреннего уровня»: «виртуальная машина низкого уровня» является промежуточным представлением, и вся оптимизация происходит в этой форме. llvm также может удерживать его в этом промежуточном представлении вплоть до последней секунды — это позволяет «оптимизировать время соединения». Компилятор clang на самом деле представляет собой «передний конец», который (эффективно) выводит llvm промежуточное представление.

Итак, если вы хотите добавить функциональность @encode() к «существующему» компилятору, вам, вероятно, придется сделать это как «компилятор/препроцессор» «от источника к источнику». Именно так были написаны исходные компиляторы Objective-C и C++ — они анализировали входной исходный текст и преобразовывали его в «обычный C», который затем передавался стандартному компилятору C. Есть несколько способов сделать это:

Сверните свой собственный

  • Используйте yacc и lex, чтобы собрать парсер ANSI-C. Вам понадобится грамматика — грамматика ANSI C (Yacc) это хорошее начало. На самом деле, чтобы внести ясность, когда я говорю yacc, я на самом деле имею в виду bison и flex. А также другие различные yacc и lex подобные инструменты на основе C: lemon, dparser и т. д....
  • Используйте perl с Yapp или EYapp, которые являются псевдо-yacc клонами в perl. Вероятно, лучше подходит для быстрого прототипирования идеи по сравнению с yacc и lex на основе C - в конце концов, это perl: регулярные выражения, ассоциативные массивы, отсутствие управления памятью и т. д.
  • Создайте свой парсер с помощью Antlr. У меня нет опыта работы с этой цепочкой инструментов, но это еще один инструмент «компилятор компилятора», который (кажется) больше ориентирован на разработчиков Java. По-видимому, существуют свободно доступные грамматики C и Objective-C.

Взломать другой инструмент

Примечание. У меня нет личного опыта использования какого-либо из этих инструментов для добавления @encode(), но я подозреваю, что они очень помогут.

  • CIL. Личного опыта работы с этим инструментом нет, но он предназначен для разбора исходного кода C, а затем "выполнения вещи" с ним. Из того, что я могу почерпнуть из документации, этот инструмент должен позволить вам извлечь необходимую информацию о типе.
  • Sparse — стоит посмотреть, но не уверен.
  • clang — не использовал его для этой цели, но якобы одной из целей было сделать его «легко взломанным». "только для таких вещей. В частности (и опять же, без личного опыта) при выполнении «тяжелой работы» всего синтаксического анализа, что позволяет вам сосредоточиться на «интересной» части, которая в данном случае будет извлекать контекстно-зависимую информацию о типе синтаксиса, а затем преобразовать ее в на простую строку C.
  • плагины gcc — плагины представляют собой gcc 4.5 (текущая альфа-версия/ бета-версия компилятора) и «могут» позволить вам легко подключиться к компилятору для извлечения необходимой информации о типе. Понятия не имею, допускает ли архитектура плагина такие вещи.

Другие

  • Coccinelle – недавно добавил это в закладки, чтобы "посмотреть позже". Это «может» сделать то, что вы хотите, и «может» сделать это без особых усилий.
  • MetaC — недавно тоже добавил это в закладки. Не представляю, насколько это будет полезно.
  • mygcc — «Может» делать то, что вы хотите. Это интересная идея, но она не применима напрямую к тому, что вы хотите. С веб-страницы: «Mygcc позволяет программистам добавлять свои собственные проверки, которые учитывают синтаксис, поток управления и информацию о потоке данных».

Ссылки.

Правка №1, бонусные ссылки.

@Lothar делает хороший вывод в своем комментарии. На самом деле я собирался включить lcc, но, похоже, он потерялся по пути.

  • lcc — компилятор lcc C. Это компилятор C, который особенно мал, по крайней мере, с точки зрения размера исходного кода. У него также есть книга, которую я очень рекомендую.
  • tcc — компилятор tcc C. Не так поучительно, как lcc, но определенно стоит посмотреть.
  • poc — компилятор poc Objective-C. Это компилятор Objective-C "от источника к источнику". Он анализирует исходный код Objective-C и генерирует исходный код C, который затем передает gcc (ну, обычно gcc). Имеет ряд расширений/функций Objective-C, недоступных в gcc. Определенно стоит посмотреть.
person johne    schedule 14.02.2010
comment
Вау, это хороший и длинный ответ. Я сохранил детали для своего ответа, потому что кто-то, задающий такой наивный вопрос, ИМХО не может написать парсер C. В вашем списке ссылок вы забываете о простых компиляторах c, таких как LCC или TinyCC. Они лучше для новичка. GCC, clang/LLVM настолько сложны, что даже опытный пользователь, не работающий полный рабочий день в качестве компилятора, может понять исходный код. - person Lothar; 14.02.2010
comment
@ Лотар, я не думаю, что мой вопрос был наивным. Я спросил это открыто, чтобы не влиять на направление ответа. Когда я спросил об этом в первый раз, я добавил, что я думал, что это должно быть реализовано в компиляторе, но не знал, в какой части; количество проницательных ответов и просмотров было ничтожным по сравнению с этим. Да, это ОТЛИЧНЫЙ ответ, и я обязательно буду использовать его в качестве справочного материала в будущем! И я ожидаю, что многие другие пользователи также найдут его ответ полезным. Спасибо, Джон! - person Anderson; 18.02.2010

Вы бы реализовали это, сначала реализовав компилятор ANSI C, а затем добавив к нему некоторые прагмы и функции, специфичные для реализации.

Да, я знаю, что это циничный ответ, и я принимаю отрицательные голоса.

person Lothar    schedule 13.02.2010
comment
... и я подумал, что реализация препроцессора может показаться слишком сложной. - person mark4o; 14.02.2010

Один из способов сделать это — написать препроцессор, который считывает исходный код для определений типов, а также заменяет @encode... соответствующим строковым литералом.

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

Если вы используете компилятор, который поддерживает плагины (например, GCC 4.5), это также может быть возможно написать плагин компилятора для этого. Затем ваш плагин может использовать информацию о типе, которую компилятор уже проанализировал. Очевидно, что этот подход будет очень специфичным для компилятора.

person mark4o    schedule 12.02.2010
comment
Имейте в виду, что если вы выбираете этот путь, ваш препроцессор @encode должен работать после препроцессора C, чтобы типы, определенные между макросами #ifdef препроцессора C, также были проанализированы препроцессором @encode. . - person dreamlax; 13.02.2010
comment
Спасибо mark4o! Я хотел разделить ответ между тобой и Джоном, но не позволил. Мне нравится ваш, потому что он более лаконичен, но он предлагает несколько возможных путей для достижения того же решения. - person Anderson; 18.02.2010