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

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

321 голосов
/ 11 декабря 2008

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

  1. Почему комитет по языку C ++ не пошел на реализацию рефлексии на языке? Является ли отражение слишком сложным в языке, который не работает на виртуальной машине (например, Java)?

  2. Если кто-то реализует рефлексию для C ++, какие будут проблемы?

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

Ответы [ 14 ]

606 голосов
/ 11 декабря 2008

Есть несколько проблем с отражением в C ++.

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

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

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

  • А потом есть шаблоны. Шаблоны в C ++ не похожи дженерики на других языках. каждый создание шаблона создает новый тип. 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>, который действительно существует в метаданных. С другой стороны, если программа на самом деле никогда не использует этот шаблон класса итератора, его тип никогда не будет создан, и поэтому компилятор не будет генерировать класс в первую очередь. И уже слишком поздно создавать его во время выполнения, так как для этого требуется доступ к исходному коду.

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

Edit: В ответ на комментарии:

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

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

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

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

38 голосов
/ 11 декабря 2008

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

15 голосов
/ 06 ноября 2009

На всех языках не следует пытаться включить все функции любого другого языка.

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

Хорошо иметь разные инструменты для разных работ. Если у нас есть только молотки, все будет выглядеть как гвозди и т. Д. Наличие языков сценариев полезно для некоторых заданий, а отражающие ОО-языки (Java, Obj-C, C #) полезны для другого класса заданий и супер эффективные базовые языки, близкие к машине, полезны для еще одного класса заданий (C ++, C, Assembler).

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

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

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

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

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

11 голосов
/ 11 декабря 2008

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

9 голосов
/ 31 августа 2009

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

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

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

8 голосов
/ 11 декабря 2008

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

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

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

6 голосов
/ 11 декабря 2008

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

3 голосов
/ 14 августа 2012

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

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

2 голосов
/ 25 августа 2012

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

#pragma enable reflection

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

2 голосов
/ 06 ноября 2009

Согласно Алистеру Кокберну, подтип не может быть гарантирован в отражающей среде .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...