Есть несколько проблем с отражением в C ++.
Нужно добавить много работы, и комитет 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 ++, и это просто не нужно так часто, как есть на других языках.
Изменить:
В ответ на комментарии:
cdleary: Да, символы отладки делают нечто подобное в том смысле, что они хранят метаданные о типах, используемых в исполняемом файле. Но они также страдают от проблем, которые я описал. Если вы когда-нибудь пытались отладить сборку релиза, вы поймете, что я имею в виду. Существуют большие логические пробелы, когда вы создали класс в исходном коде, который был выделен в конечном коде. Если бы вы использовали рефлексию для чего-то полезного, вам нужно, чтобы оно было более надежным и последовательным. Как таковые, типы будут исчезать и исчезать почти каждый раз, когда вы компилируете. Вы меняете крошечную деталь, и компилятор решает изменить, какие типы будут встроенными, а какие нет, как ответ. Как извлечь из этого что-нибудь полезное, когда вы даже не гарантируется, что наиболее важные типы будут представлены в ваших метаданных? Тип, который вы искали, возможно, был там в последней сборке, но теперь его нет. А завтра кто-нибудь проверит небольшое невинное изменение на маленькую невинную функцию, которая делает тип достаточно большим, чтобы он не стал полностью встроенным, поэтому он вернется снова. Это все еще полезно для отладочных символов, но не намного больше этого. Я бы не хотел пытаться генерировать код сериализации для класса в соответствии с этими условиями. но не намного больше чем это. Я бы не хотел пытаться генерировать код сериализации для класса в соответствии с этими условиями. но не намного больше чем это. Я бы не хотел пытаться генерировать код сериализации для класса в соответствии с этими условиями.
Эван Теран: Конечно, эти проблемы могут быть решены. Но это возвращается к моей точке № 1. Это заняло бы много работы, и у комитета C ++ есть много вещей, которые они считают более важными. Является ли выгода от того, что какое-то ограниченное отражение (и оно будет ограничено) в C ++ действительно достаточно велико, чтобы оправдать сосредоточение на этом за счет других функций? Действительно ли есть огромное преимущество в добавлении функций основного языка, которые уже (в основном) могут быть сделаны через библиотеки и препроцессоры, такие как QT? Возможно, но необходимость гораздо менее актуальна, чем если бы таких библиотек не было. Хотя для ваших конкретных предложений, я считаю, что запрет на использование шаблонов сделает его совершенно бесполезным. Например, вы не сможете использовать отражение в стандартной библиотеке. Какое отражение не будетstd::vector
? Шаблоны являются огромной частью C ++. Функция, которая не работает с шаблонами, в основном бесполезна.
Но вы правы, некоторая форма отражения может быть реализована. Но это было бы серьезным изменением в языке. Как и сейчас, типы являются исключительно конструкцией времени компиляции. Они существуют для пользы компилятора и ничего больше. После того , как код был скомпилирован, там нет никаких классов. Если вы растягиваете себя, вы можете утверждать, что функции все еще существуют, но на самом деле все, что есть, - это набор инструкций для ассемблера прыжков и множество push / pop стеков. При добавлении таких метаданных не так много всего.
Но, как я уже сказал, есть предложение об изменениях в модели компиляции, добавлении автономных модулей, хранении метаданных для выбранных типов, что позволяет другим модулям ссылаться на них без необходимости связываться с #include
s. Это хорошее начало, и, честно говоря, я удивлен, что стандартный комитет не просто выбросил предложение о том, что оно слишком велико. Так, может быть, через 5-10 лет? :)
export
иvector<bool>
.typeinfo
«sname()
функция должна возвращать имя , которое было набранный программистом , а не что - то неопределенным. И дайте нам строковый преобразователь для счетчиков. Это действительно важно для сериализации / десериализации, создания заводов и т. Д.Отражение требует хранения метаданных о типах, которые можно запрашивать. Поскольку C ++ компилируется в собственный машинный код и претерпевает значительные изменения в связи с оптимизацией, представление высокого уровня приложения в значительной степени теряется в процессе компиляции, следовательно, запросить их во время выполнения будет невозможно. Java и .NET используют представление очень высокого уровня в двоичном коде для виртуальных машин, что делает возможным этот уровень отражения. Однако в некоторых реализациях C ++ есть нечто, называемое информацией о типе времени выполнения (RTTI), которое можно рассматривать как упрощенную версию отражения.
источник
Все языки не должны пытаться включить все функции любого другого языка.
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 ++ для тех, кто этого хочет - или, возможно, библиотека будет предоставлять такие сервисы времени выполнения (может быть, есть один сейчас или его начало в ускорении?).
источник
Если вы действительно хотите понять конструктивные решения, связанные с C ++, найдите копию The Annotated C ++ Reference Manual от Эллиса и Страуструпа. Он НЕ соответствует последнему стандарту, но он проходит через оригинальный стандарт и объясняет, как все работает и как часто, как они так поступили.
источник
Отражение для языков, которые его имеют, зависит от того, сколько исходного кода компилятор желает оставить в вашем объектном коде, чтобы включить отражение, и сколько имеется механизмов анализа для интерпретации этой отраженной информации. Если компилятор не хранит весь исходный код, рефлексия будет ограничена в его способности анализировать доступные факты об исходном коде.
Компилятор C ++ ничего не держит (ну, игнорируя RTTI), поэтому вы не получите отражения в языке. (Компиляторы Java и C # хранят только классы, имена методов и возвращаемые типы, поэтому вы получаете немного данных об отражении, но не можете проверить выражения или структуру программы, а это означает, что даже в этих языках с «отражением» информация, которую вы можете получить, довольно скудна, и, следовательно, вы действительно не можете много анализировать).
Но вы можете выйти за пределы языка и получить полный потенциал отражения. Ответ на другое обсуждение переполнения стека при отражении в C обсуждает это.
источник
Отражение может быть и было реализовано в C ++ раньше.
Это не нативная функция c ++, потому что она требует больших затрат (память и скорость), которые не должны устанавливаться языком по умолчанию - язык ориентирован на «максимальную производительность по умолчанию».
Поскольку вы не должны платить за то, что вам не нужно, и, как вы сами говорите, в редакторах нужно больше, чем в других приложениях, то это должно быть реализовано только там, где вам это нужно, а не «принудительно» для всего кода ( вам не нужно размышлять обо всех данных, с которыми вы будете работать в редакторе или другом подобном приложении).
источник
Причина, по которой C ++ не имеет отражения, состоит в том, что для этого потребуется, чтобы компиляторы добавляли символьную информацию в объектные файлы, например, какие члены имеют тип класса, информацию о членах, о функциях и обо всем. По сути, это сделает бесполезными включаемые файлы, так как информация, отправленная объявлениями, будет затем считываться из этих объектных файлов (затем модулей). В C ++ определение типа может встречаться в программе несколько раз путем включения соответствующих заголовков (при условии, что все эти определения одинаковы), поэтому необходимо будет решить, куда поместить информацию об этом типе, так же, как назвать ее осложнение здесь. Еще одна сильная сторона - агрессивная оптимизация, выполняемая компилятором C ++, который может оптимизировать десятки экземпляров шаблонов классов. Это возможно, но поскольку C ++ совместим с C,
источник
Существует множество случаев использования рефлексии в C ++, которые не могут быть адекватно решены с помощью конструкций времени компиляции, таких как метапрограммирование шаблона.
N3340 предлагает богатые указатели как способ отражения в C ++. Среди прочего, он решает проблему не оплаты функции, если вы ее не используете.
источник
По словам Алистера Кокберна, подтип не может быть гарантирован в отражающей среде .
Отражение больше относится к системам скрытой типизации. В C ++ вы знаете, какой у вас тип, и знаете, что с ним можно сделать.
источник
Отражение может быть необязательным, как директива препроцессора. Что-то вроде
#pragma enable reflection
Таким образом, мы можем получить лучшее из обоих миров, без этих прагматических библиотек, которые будут создаваться без размышлений (без каких-либо накладных расходов, как обсуждалось), тогда индивидуальный разработчик будет решать, хотят ли они скорости или простоты использования.
источник
Если бы C ++ мог иметь:
const
модификатораconst
модификатораЭтого было бы достаточно для создания очень простых в использовании библиотек суть обработки типов данных, которая так распространена в современных веб-приложениях и приложениях баз данных (все формы, механизмы обмена сообщениями, парсеры xml / json, сериализация данных и т. Д.).
Например, основная информация, поддерживаемая
Q_PROPERTY
макросом (часть Qt Framework), http://qt.nokia.com/doc/4.5/properties.html, расширенная для охвата методов класса и e), будет чрезвычайно полезна для C ++ и для Сообщество программного обеспечения в целом.Конечно, рефлексия, на которую я ссылаюсь, не будет охватывать семантического значения или более сложных вопросов (таких как номера строк исходного кода комментариев, анализ потока данных и т. Д.), Но я также не думаю, что они необходимы, чтобы быть частью языкового стандарта.
источник
некоторые хорошие ссылки на отражение в C ++, которые я только что нашел:
Рабочий документ стандарта C ++: аспекты отражения в C ++
Простой пример отражения с использованием шаблонов
источник
Отражение в C ++, я считаю, крайне важно, если C ++ будет использоваться в качестве языка для доступа к базе данных, обработки веб-сеансов / http и разработки GUI. Отсутствие отражения не позволяет ORM (таким как Hibernate или LINQ), синтаксическим анализаторам XML и JSON создавать экземпляры классов, сериализацию данных и многие другие значения (когда для создания экземпляра класса необходимо использовать изначально не типированные данные).
Переключатель времени компиляции, доступный разработчику программного обеспечения в процессе сборки, может быть использован для устранения этой проблемы «вы платите за то, что используете».
Мне разработчик прошивки не нуждается в отражении для чтения данных с последовательного порта - тогда нормально не использовать коммутатор. Но как разработчик базы данных, который хочет продолжать использовать C ++, я постоянно сталкиваюсь с ужасным, сложным в обслуживании кодом, который отображает данные между членами данных и конструкциями базы данных.
Ни Boost-сериализация, ни другой механизм на самом деле не решают отражения - это должно быть сделано компилятором - и как только это будет сделано, C ++ снова будет изучен в школах и использован в программном обеспечении, которое имеет дело с обработкой данных.
Для меня это проблема № 1 (а примитивы с наивным потоком - проблема № 2).
источник
Это в основном потому, что это «необязательный дополнительный». Многие люди предпочитают C ++ языкам, таким как Java и C #, чтобы они могли лучше контролировать вывод компилятора, например, небольшую и / или более быструю программу.
Если вы решите добавить отражение, существуют различные решения .
источник