Почему С++ не имеет отражения?

Это несколько странный вопрос. Мои цели - понять решение языкового дизайна и определить возможности отражения на С++.

  • Почему С++ языковой комитет не пошел на реализацию рефлексии на языке? Отражается ли слишком сложно на языке, который не работает на виртуальной машине (например, java)?

  • Если кто-то должен был реализовать отражение для С++, каковы будут проблемы?

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

Ответ 1

В С++ есть несколько проблем с отражением.

  • Многое нужно добавить, а комитет С++ довольно консервативен и не тратит время на радикальные новые функции, если они не уверены, что он окупится. (Было предложено добавить модульную систему, подобную сборке .NET, и, хотя я думаю, что существует общее мнение о том, что было бы неплохо иметь, это не их главный приоритет на данный момент, и был отброшен до тех пор, пока после С++ 0x. Мотивация для этой функции - избавиться от системы #include, но она также позволит по крайней мере некоторые метаданные).

  • Вы не платите за то, что не делаете использовать. Это одна из основных философия дизайна, лежащая в основе С++. Зачем мой код переносится метаданные, если мне это может не понадобиться? Кроме того, добавление метаданных может блокировать компилятор из оптимизирующий. Зачем мне платить стоимость в моем коде, если мне никогда не понадобится что метаданные?

  • Это приводит нас к еще одной большой точке: С++ дает очень мало гарантий о скомпилированном коде. компилятору разрешено делать довольно что угодно, до тех пор, пока конечная функциональность - это то, что ожидается. Например, ваш классы не требуются для будь там. Компилятор может оптимизировать их, встроенный все, что они делают, и это часто делает именно это, потому что даже простой шаблонный код имеет тенденцию создать довольно много шаблонов конкретизации. Стандарт С++ библиотека опирается на эту агрессивную оптимизация. Функторы только если накладные расходы создание и уничтожение объект можно оптимизировать. operator[] на векторе сравнимо только с сырым индексирование массива в производительности потому что весь оператор может быть встроенный и полностью удаленный из скомпилированного кода. С# и Java сделать много гарантий относительно вывода компилятора. Если я определяю класс в С#, то этот класс будет существуют в результирующей сборке. Даже если я его никогда не использую. Даже если все звонки на его функции-члены могли бы быть встроенным. Класс должен быть там, чтобы отражение могло найти Это. Часть этого облегчается с помощью С# компиляция в байт-код, что означает что компилятор JIT может удалить определения классов и встроенные если он любит, даже если исходный компилятор С# не может. В С++, у вас есть только один компилятор, и он должен выводить эффективный код. если ты было разрешено проверять метаданные исполняемого файла С++, вы ожидаете см. каждый класс, который он определил, который означает, что компилятор для сохранения всех определенных классов, даже если они не нужны.

  • А потом есть шаблоны. Шаблоны на С++ не похожи на шаблоны дженерики на других языках. каждый создание шаблона создает Новый тип. std::vector<int> - совершенно отдельный класс из std::vector<float>. Это добавляет много разных типов в целом программа. Какое должно быть наше отражение? видеть? Шаблон std::vector? Но как это возможно, так как это исходный код, который не имеет значение во время выполнения? Это должно было бы увидеть отдельные классы std::vector<int> и std::vector<float>. А также std::vector<int>::iterator и std::vector<float>::iterator, то же самое для const_iterator и т.д. А также как только вы входите в шаблон метапрограммирование, вы быстро оказываетесь создавая сотни шаблонов, все из которых вставляются и удаляются снова компилятором. У них нет смысл, за исключением части метапрограмма времени компиляции. Все эти сотни классов будут видны к размышлению? Им придется, потому что иначе наше отражение было бы бесполезно, если оно даже не гарантирует, что классы, которые я определил, действительно будут там. И побочная проблема заключается в том, что класс шаблона не существует до его создания. Представьте себе программу, которая использует std::vector<int>. Должна ли наша система отражения видеть std::vector<int>::iterator? С одной стороны, вы наверняка ожидаете этого. Это важный класс, который определяется в терминах std::vector<int>, который существует в метаданных. С другой стороны, если программа никогда не использует этот шаблон класса итератора, его тип никогда не будет создан, поэтому компилятор не будет генерировать класс в первую очередь. И это слишком поздно, чтобы создать его во время выполнения, поскольку для этого требуется доступ к исходному коду.

  • И, наконец, отражение не совсем как жизненно важный в С++, как в С#. причина снова, шаблон метапрограммированием. Он не может решить все, но для многих случаев, когда вы в противном случае прибегли бы к отражения, можно написать метапрограмма, которая делает то же самое вещь во время компиляции. boost::type_traits является простым пример. Вы хотите узнать о типе T? Проверьте его type_traits. В С# вам придется ловить рыбу после тип используя отражение. отражение будет по-прежнему вещи (основное использование, которое я вижу, что метапрограммирование не может легко заменить, для автогенерированных код сериализации), но это несут значительные С++, и это просто не так часто, как на других языках.

Изменить: В ответ на комментарии:

cdleary: Да, символы отладки делают что-то подобное, поскольку они хранят метаданные о типах, используемых в исполняемом файле. Но они также страдают от проблем, которые я описал. Если вы когда-либо пробовали отлаживать сборку выпусков, вы поймете, что я имею в виду. Существуют большие логические пробелы, в которых вы создали класс в исходном коде, который был включен в окончательный код. Если бы вы использовали рефлексию для чего-нибудь полезного, вам нужно было бы быть более надежным и последовательным. Как бы то ни было, типы будут исчезать и исчезать почти каждый раз, когда вы компилируете. Вы изменяете крошечную мелочь, и компилятор решает изменить, какие типы встраиваются, а какие - нет. Как извлечь из этого что-нибудь полезное, если вам даже не гарантировано, что наиболее релевантные типы будут представлены в ваших метаданных? Тип, который вы искали, возможно, был в последней сборке, но теперь он исчез. И завтра кто-то проверит небольшое невинное изменение на маленькую невиновную функцию, что делает тип достаточно большим, чтобы он не полностью входил, так что он снова вернется. Это все еще полезно для отладочных символов, но не намного больше. Я бы не хотел пытаться генерировать код сериализации для класса под этими терминами.

Эван Теран: Конечно, эти проблемы могут быть решены. Но это относится к моей точке №1. Это займет много работы, и в комитете С++ есть много вещей, которые, по их мнению, важнее. Можно ли получить ограниченное отражение (и было бы ограничено) на С++ действительно достаточно большим, чтобы оправдать это с учетом других функций? Есть ли огромная выгода в добавлении функций основного языка, который уже (в основном) может выполняться через библиотеки и препроцессоры, такие как QT? Возможно, но необходимость намного менее актуальна, чем если бы таких библиотек не существовало. Тем не менее, для ваших конкретных предложений, я считаю, что запрет на создание шаблонов сделает его совершенно бесполезным. Например, вы не сможете использовать отражение в стандартной библиотеке. Какое отражение не позволит вам увидеть std::vector? Шаблоны - огромная часть С++. Функция, которая не работает с шаблонами, в принципе бесполезна.

Но вы правы, может быть реализована некоторая форма отражения. Но это будет серьезное изменение в языке. Как и сейчас, типы являются исключительно конструкцией времени компиляции. Они существуют в интересах компилятора, и ничего больше. После компиляции кода классов нет. Если вы растянете себя, вы можете утверждать, что функции все еще существуют, но на самом деле все, что есть, - это куча инструкций для ассемблера прыжка и много стека push/pop. Там не так много, когда добавляете такие метаданные.

Но, как я уже сказал, есть предложение об изменениях в модели компиляции, добавление автономных модулей, хранение метаданных для выбранных типов, позволяющих другим модулям ссылаться на них без необходимости связываться с #include s. Это хорошее начало, и, честно говоря, я удивлен, что стандартный комитет не просто выбрал это предложение за слишком большие перемены. Так может быть, через 5-10 лет?:)

Ответ 2

Отражение требует некоторых метаданных о типах, которые будут храниться где-нибудь, которые могут быть запрошены. Поскольку С++ компилируется в собственный машинный код и претерпевает значительные изменения из-за оптимизации, представление высокого уровня приложения в значительной степени теряется в процессе компиляции, следовательно, невозможно будет запросить их во время выполнения. Java и .NET используют представление очень высокого уровня в двоичном коде для виртуальных машин, что делает возможным этот уровень отражения. Однако в некоторых реализациях С++ есть что-то называемое Run Time Type Information (RTTI), которое можно рассматривать как усеченную версию отражения.

Ответ 3

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

С++ - это очень сложный макросборщик. Это НЕ (в традиционном смысле) язык высокого уровня, такой как С#, Java, Objective-C, Smalltalk и т.д.

Хорошо иметь разные инструменты для разных заданий. Если у нас есть только молотки, все вещи будут выглядеть как гвозди и т.д. Имея языки script, полезны для некоторых заданий, а рефлексивные OO-языки (Java, Obj-C, С#) полезны для другого класса заданий, и суперэффективные голые кости, близкие к машинным, полезны для еще одного класса заданий (С++, C, Assembler).

С++ делает удивительную работу по расширению технологии Assembler до невероятных уровней управления сложностью и абстракциями, чтобы сделать программирование более масштабными и более сложными задачами, намного более доступными для людей. Но это не обязательно язык, который лучше всего подходит для тех, кто приближается к своей проблеме с строго высокого уровня перспективы (Lisp, Smalltalk, Java, С#). Если вам нужен язык с этими функциями, чтобы наилучшим образом реализовать решение ваших проблем, то поблагодарите тех, кто создал такие языки для всех нас, чтобы использовать!

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

С#, Java, Objective-C все требуют гораздо большей, более богатой системы времени исполнения для поддержки их выполнения. Эта среда выполнения должна быть доставлена ​​в рассматриваемую систему - предустановлена ​​для поддержки работы вашего программного обеспечения. И этот уровень должен поддерживаться для различных целевых систем, настроенных на НЕКОТОРЫХ ДРУГИХ ЯЗЫКАх, чтобы заставить его работать на этой платформе. И этот средний уровень - этот адаптивный уровень между ОС хоста и вашим кодом - временем выполнения, почти всегда написан на языке, таком как C или С++, где эффективность - это №1, где разумное понимание точного взаимодействия между программным и аппаратным обеспечением может быть хорошо понимается и управляется с максимальным усилением.

Мне нравится Smalltalk, Objective-C и имеет богатую систему исполнения с отражением, метаданных, сборкой мусора и т.д. Удивительный код можно написать, чтобы воспользоваться этими возможностями! Но это просто более высокий уровень в стеке, слой, который должен опираться на более низкие уровни, которые сами должны в конечном итоге сидеть на ОС и аппаратном обеспечении. И нам всегда нужен язык, который лучше всего подходит для создания этого слоя: С++/C/Assembler.

Приложение: С++ 11/14 продолжают расширять возможности С++ для поддержки абстракций и систем более высокого уровня. Threading, synchronization, точные модели памяти, более точные описания абстрактных машин позволяют разработчикам С++ достигать многих абстракций высокого уровня, что некоторые из этих языков только высокого уровня используются для использования в эксклюзивном домене, производительность металла и отличную предсказуемость (т.е. минимальные подсистемы времени исполнения). Возможно, средства отражения будут выборочно включены в будущую ревизию С++ для тех, кто этого хочет, или, может быть, библиотека предоставит такие службы времени выполнения (может быть, сейчас есть один или начало одного в boost?).

Ответ 4

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

Ответ 5

Отражение может быть и было реализовано в С++ раньше.

Это не родная функция С++, поскольку она имеет большую стоимость (память и скорость), которые по умолчанию не должны устанавливаться языком - язык является "максимальной производительностью по умолчанию".

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

Ответ 6

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

Компилятор С++ не поддерживает ничего (ну, игнорируя RTTI), поэтому вы не получите отражения на этом языке. (Компиляторы Java и С# сохраняют только имена классов, методов и возвращаемые типы, поэтому вы получаете немного данных отражения, но вы не можете проверять выражения или структуру программы, а это значит, что даже на тех языках с включенным отражением информация, которую вы можете получить, довольно редкая, и, следовательно, вы действительно не можете много анализировать).

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

Если С++ может иметь:

  • данные члена класса для имен переменных, типов переменных и модификатора const
  • аргумент функции итератор (только позиция вместо имени)
  • данные о членах класса для имен функций, типа возврата и модификатора const
  • список родительских классов (в том же порядке, что и определено)
  • данные для членов шаблона и родительских классов; расширенный шаблон (что означает, что фактический тип будет доступен для API отражения, а не "информация о шаблоне о том, как туда добраться" ).

Этого было бы достаточно для создания очень простых в использовании библиотек в сущности беспримесной обработки данных, которая настолько распространена в современных приложениях для веб-сайтов и баз данных (все ормы, механизмы обмена сообщениями, синтаксические анализаторы xml/json, сериализация данных и т.д.).

Например, основная информация, поддерживаемая макросом Q_PROPERTY (часть Qt Framework) http://qt.nokia.com/doc/4.5/properties.html расширен, чтобы охватить методы класса, и e) - будет чрезвычайно полезным для С++ и для сообщества разработчиков программного обеспечения в целом.

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

Ответ 11

Отражение может быть необязательным, как директива препроцессора. Что-то вроде

#pragma enable reflection

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

Ответ 13

Отражение в С++, я считаю крайне важным, если С++ следует использовать в качестве языка для доступа к базе данных, обработки веб-сеанса/http и разработки графического интерфейса. Отсутствие рефлексии предотвращает ORM (например, Hibernate или LINQ), синтаксические анализаторы XML и JSON, которые инициализируют классы, сериализацию данных и многие другие тины (где исходные данные не должны использоваться для создания экземпляра класса).

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

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

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

Для меня эта проблема №1 (и наивные примитивы для прошивки - проблема № 2).

Ответ 14

Это в основном потому, что это "дополнительная опция". Многие люди выбирают С++ поверх таких языков, как Java и С#, чтобы они имели больший контроль над выходом компилятора, например. меньшая и/или более быстрая программа.

Если вы решите добавить отражение, есть различные доступные решения.