Как определить функции-члены класса [дубликаты]

A NullReferenceException бросается, когда мы пытаемся получить доступ к свойствам нулевого объекта или когда значение строки становится пустым, и мы пытаемся получить доступ к строковым методам.

Например:

  1. При использовании метода string пустой строки:
    string str = string.Empty;
    str.ToLower(); // throw null reference exception
    
  2. Когда свойство нулевого объекта доступно:
    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 
    
215
задан Lazer 21 June 2010 в 05:10
поделиться

30 ответов

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

Хотя есть большие ответы на этот вопрос, я не хочу использовать тонны макросов или полагаться на Boost. Boost - отличная библиотека, но есть много небольших проектов на C ++ 0x, которые проще и имеют более быстрое время компиляции. Есть также преимущества для того, чтобы украсить класс извне, например, обернуть библиотеку C ++, которая еще не поддерживает C ++ 11. Это fork CAMP, используя C ++ 11, что больше не требует Boost .

4
ответ дан Nick 22 August 2018 в 08:27
поделиться

Здесь вы можете найти другую библиотеку: http://www.garret.ru/cppreflection/docs/reflect.html Он поддерживает два способа: получение информации о типе из отладочной информации и предоставление возможности программисту предоставить эта информация.

Я также заинтересовался отражением для моего проекта и нашел эту библиотеку, я еще не пробовал ее, но пробовал другие инструменты от этого парня, и мне нравится, как они работают: -)

3
ответ дан alariq 22 August 2018 в 08:27
поделиться

Существует еще одна новая библиотека для отражения в C ++, называемая RTTR (Reflection of Run Time Reflection, см. также github ).

Интерфейс аналогично отражению в C #, и он работает без RTTI.

7
ответ дан bluish 22 August 2018 в 08:27
поделиться

Похоже, что C ++ все еще не имеет этой функции. И C ++ 11 тоже откладывает отражение ((

Поиск некоторых макросов или создание собственных. Qt также может помочь с отражением (если он может быть использован).

2
ответ дан Bohdan 22 August 2018 в 08:27
поделиться

И я бы хотел пони, но пони не свободны. :-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI - это то, что вы собираетесь получить. Отражение, о котором вы думаете, - полностью описательные метаданные, доступные во время выполнения - просто не существует для C ++ по умолчанию.

55
ответ дан Brad Wilson 22 August 2018 в 08:27
поделиться
  • 1
    Я второй Брэд. Шаблоны C ++ могут быть довольно мощными, и существует богатый опыт в отношении различных типов поведения «отражение», таких как «дополнительная» библиотека, типы типов, C ++ RTTI и т. Д., Которые могут решить многие проблемы, для решения которых решена. Итак, Ник, какова твоя цель здесь? – Aaron 17 September 2008 в 00:19
  • 2
    Будьте внимательны для пони! Я бы отдал дважды, так как ваш ответ тоже заслуживает этого, но, к сожалению, я получаю только один, поэтому побеждают пони. :-) – Franci Penov 11 November 2008 в 04:57
  • 3
    Я действительно не понимаю, почему это умный ответ. Я уже сказал, что мне нужны ссылки на библиотеки и т. Д., Чтобы реализовать это. Отражение / интроспекция - для различной системы, позволяющей доступ к скрипту, сериализация и т. Д. – Nick 30 March 2009 в 16:10
  • 4
    @Nick: Он уже ответил на это. Этого не может быть сделано, данных не существует, поэтому библиотека не может реализовать его для вас. – jalf 3 August 2009 в 11:58

Я хотел бы объявить о существовании инструментария автоматической интроспекции / отражения «IDK». Он использует мета-компилятор, такой как Qt, и добавляет метаинформацию непосредственно в объектные файлы. Утверждается, что он прост в использовании. Нет внешних зависимостей. Он даже позволяет автоматически отражать std :: string, а затем использовать его в скриптах. Посмотрите IDK

2
ответ дан Bryan 22 August 2018 в 08:27
поделиться

Проект Root Reflex поддерживает это.

См. https://root.cern.ch/how/how-use-reflex

0
ответ дан Buğra Gedik 22 August 2018 в 08:27
поделиться

Существует два вида плавания reflection.

  1. Проверка путем повторения элементов типа, перечисления его методов и т. д. Это невозможно с C ++.
  2. Проверка, проверяя, имеет ли тип класса (класс, структура, объединение) метод или вложенный тип, получен из другого определенного типа. Это возможно с помощью C ++ с помощью template-tricks. Используйте boost::type_traits для многих вещей (например, проверка целостности типа). Для проверки существования функции-члена используйте Возможно ли написать шаблон для проверки существования функции? . Для проверки наличия определенного вложенного типа используйте plain SFINAE .

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

  • Meta Compiler, как Qt Meta Object Compiler, который переводит ваш код, добавляя дополнительную метаинформацию.
  • A Framework, состоящий из макросов, которые позволяют добавлять требуемая метаинформация. Вам нужно будет указать инфраструктуре все методы, имена классов, базовые классы и все, что им нужно.

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

96
ответ дан Community 22 August 2018 в 08:27
поделиться
  • 1
    C ++ сделан со скоростью, но философия не «как можно быстрее», вместо этого это так », вы не платите за него, если вы его не используете. & quot; Я считаю, что язык может реализовать интроспекцию таким образом, который соответствует этой философии, на C ++ ее просто не хватает. – Joseph Garvin 8 July 2009 в 20:34
  • 2
    @ Джозеф: Как это должно быть сделано? Это потребует сохранения всех этих метаданных. Это означает, что вы должны заплатить за это, даже если вы его не используете. (Если вы не отметили отдельные типы как «поддерживающие отражение», но тогда мы почти спустились туда, где мы могли бы также использовать существующий макроопределение. – jalf 3 August 2009 в 12:00
  • 3
    @jalf: Только метаданные, которые могут потребоваться. Если рассматривать только отражение во времени компиляции, это тривиально. Например. функция времени компиляции members<T>, которая возвращает список всех членов T. Если мы хотим иметь отражение во время выполнения (т. е. RTTI, смешанное с отражением), компилятор все равно будет знать все отраженные базовые типы. Скорее всего, members<T>(T&) никогда не будет создан для T = std :: string, поэтому RTTI для std :: string или его производных классов не обязательно должен быть включен. – MSalters 3 August 2009 в 12:22
  • 4
    Библиотека рефлексов (упомянутая ниже) добавляет отражение к C ++ без замедления существующего кода: root.cern.ch/drupal/content/reflex – Joseph Lisee 4 May 2011 в 20:37
  • 5

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

В настоящее время я изучаю, когда мне кажется, методы, которые используют inherit_linearly, чтобы сделать определение отражающие типы намного проще. На самом деле я довольно далеко, но у меня все еще есть способы пойти. Изменения в C ++ 0x, скорее всего, будут очень полезны в этой области.

2
ответ дан Crazy Eddie 22 August 2018 в 08:27
поделиться

Попробуйте посмотреть на этот проект http://www.garret.ru/cppreflection/docs/reflect.html добавлены отражения на C ++. Он добавил метаданные к классам, которые вы затем можете использовать.

2
ответ дан Damian 22 August 2018 в 08:27
поделиться

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

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

Где ENUMERATE_MEMBERS - это макрос, который описан ниже (UPDATE):

Предположим, что мы определили функцию сериализации для int и std :: string:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

И у нас есть общая функция рядом с «секретным макросом»;)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

Теперь вы можете написать

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Итак, имея макрос ENUMERATE_MEMBERS в определении структуры, вы можете создавать сериализацию, сравнение, хэширование и другие материалы без касания оригинального типа, единственное требование - внедрять метод «EnumerateWith» для каждого типа, который не перечислим, за каждый перечислитель (например, BinaryWriter). Обычно вам нужно реализовать 10-20 «простых» типов для поддержки любого типа в вашем проекте.

Этот макрос должен иметь нулевые служебные данные для создания / уничтожения структуры во время выполнения, а код T .EnumerateWith () должен генерироваться по требованию, что может быть достигнуто путем создания его встроенной функции шаблона, поэтому единственные накладные расходы во всей истории - это добавить ENUMERATE_MEMBERS (m1, m2, m3 ...) к каждой структуре, тогда как реализация определенного метода для каждого типа элемента является обязательным в любом решении, поэтому я не предполагаю, что это накладные расходы.

UPDATE: существует очень простая реализация макроса ENUMERATE_MEMBERS (однако его можно было бы немного расширить для поддержки наследование из перечислимой структуры)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

И вам не нужна сторонняя библиотека для этих 15 строк кода;)

1
ответ дан Evgeny Mamontov 22 August 2018 в 08:27
поделиться

Что вы пытаетесь сделать с отражением? Вы можете использовать типы типа Boost типа и typeof в качестве ограниченной формы отражения во времени компиляции. То есть вы можете проверить и изменить основные свойства типа, переданного в шаблон.

13
ответ дан Ferruccio 22 August 2018 в 08:27
поделиться

РЕДАКТИРОВАТЬ: Обновлена ​​неработающая ссылка по состоянию на февраль, 7, 2017.

Я думаю, никто не упомянул об этом:

В CERN они используют полную систему отражения для C ++:

CERN Reflex . Кажется, он работает очень хорошо.

7
ответ дан Germán Diago 22 August 2018 в 08:27
поделиться

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

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

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

Наш DMS Software Reengineering Toolkit - это обобщенная технология компилятора, параметризованная явными определениями langauge. Он имеет определения langauge для C, C ++, Java, COBOL, PHP, ...

Для версий C, C ++, Java и COBOL он обеспечивает полный доступ к деревьям синтаксического анализа и информации таблицы символов. Эта информация таблицы символов включает в себя данные, которые вы, скорее всего, захотите от «отражения». Если вы хотите перечислить некоторый набор полей или методов и сделать что-то с ними, DMS может использоваться для преобразования кода в соответствии с тем, что вы находите в таблицах символов произвольным образом.

4
ответ дан Ira Baxter 22 August 2018 в 08:27
поделиться

Я бы рекомендовал использовать Qt .

Существует лицензия с открытым исходным кодом, а также коммерческая лицензия.

13
ответ дан Jérôme 22 August 2018 в 08:27
поделиться
  • 1
    Я посмотрел на это, но он использует макросы, а исходный код требует синтаксического анализа для генерации метаданных. Я бы хотел избежать этого дополнительного шага. Я бы предпочел использовать библиотеку C ++ или простые макросы. Спасибо за эту идею. – Nick 3 August 2009 в 11:47
  • 2
    QT или другая библиотека, реализующая подобный подход, - это лучшее, что вы собираетесь получить – jalf 3 August 2009 в 11:59
  • 3
    Платите во время компиляции или платите во время выполнения - в любом случае вы платите! – Martin Beckett 20 June 2010 в 19:28

RTTI не существует для C ++.

Это просто неправильно. Фактически, сам термин RTTI был придуман стандартом C ++. С другой стороны, RTTI не очень далеко продвигает отражение.

35
ответ дан Konrad Rudolph 22 August 2018 в 08:27
поделиться

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

Это зависит от вас, чтобы отобразить это во временное отражение, конечно, и это будет не слишком легко, но это возможно в этом направлении, в то время как оно не будет обратным:)

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

6
ответ дан Matthieu M. 22 August 2018 в 08:27
поделиться

Я сделал что-то вроде того, что вы после этого, и хотя можно получить некоторый уровень отражения и доступ к функциям более высокого уровня, головная боль обслуживания может не стоить того. Моя система была использована для того, чтобы классы пользовательского интерфейса полностью отделены от бизнес-логики посредством делегирования, сходного с концепцией передачи и пересылки сообщений Objective-C. Способ сделать это - создать некоторый базовый класс, способный отображать символы (я использовал пул строк, но вы могли бы сделать это с перечислениями, если вы предпочитаете скорость и обработку ошибок во время компиляции по полной гибкости), чтобы использовать указатели (на самом деле не чистые указатели функций, но что-то похожее на то, что Boost имеет с Boost.Function - к которому у меня не было доступа в то время). Вы можете сделать то же самое для своих переменных-членов, если у вас есть общий базовый класс, способный представлять любое значение. Вся система была беззастенчивым разрывом кодирования и делегирования ключей, с несколькими побочными эффектами, которые, возможно, стоили того времени, необходимого для того, чтобы каждый класс, который использовал систему, соответствовал всем ее методам и членам с юридическими вызовами : 1) Любой класс может вызывать любой метод в любом другом классе без необходимости включать заголовки или писать поддельные базовые классы, чтобы интерфейс мог быть предопределен для компилятора; и 2) Геттеры и сеттеры переменных-членов были легко сделать потокобезопасными, поскольку изменение или доступ к их значениям всегда выполнялось с помощью 2 методов в базовом классе всех объектов.

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

Возможно более интересным для вас является тот факт, что вы также можете сбросить все методы и члены, определенные для класса, и в строковой форме не менее.

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

Что касается того, как реализовать что-то вроде этого: просто используйте общие и слабые указатели на какую-то общую базу (мой был очень образно назван «Объект», ) и получить для всех типов, которые вы хотите использовать. Я бы рекомендовал установить Boost.Function вместо того, чтобы делать это так, как я делал, что было с некоторыми обычным дерьмом и тонкой уродливыми макросами, чтобы обернуть вызовы указателей функций. Поскольку все отображается, проверка объектов - это всего лишь вопрос повторения всех ключей. Поскольку мои классы по существу были максимально близки к прямому ripoff Cocoa, насколько это возможно, используя только C ++, если вы хотите что-то подобное, я бы предложил использовать документацию Cocoa в качестве плана.

10
ответ дан Michel 22 August 2018 в 08:27
поделиться
  • 1
    Привет, @ Майкл; у вас все еще есть исходный код для этого, или вы избавились от него? Я бы хотел взглянуть на него, если вы не возражаете. – RandomDSdevel 3 November 2014 в 18:16
  • 2
    Упс, неправильно написано ваше имя! Нет wonder Я никогда не получал ответа ... – RandomDSdevel 1 November 2015 в 01:52

, хотя отражение не поддерживается вне коробки в c ++, это не так сложно реализовать. Я столкнулся с этой замечательной статьей: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

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

нижняя строка - отражение может окупиться, если сделано правильно, и это вполне возможно в c ++.

2
ответ дан Naore Azenkut 22 August 2018 в 08:27
поделиться

Я думаю, что вы можете найти интересную статью «Использование шаблонов для отражения на C ++» Доминика Филиона. Это в разделе 1.4 из Game Programming Gems 5 . К сожалению, у меня нет моей копии со мной, но ищите ее, потому что я думаю, что она объясняет, о чем вы просите.

5
ответ дан P i 22 August 2018 в 08:27
поделиться

Что вам нужно сделать, так это то, что препроцессор генерирует данные отражения в полях. Эти данные могут храниться как вложенные классы.

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

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

Далее мы определяем макрос REFLECTABLE для генерации данных о каждом поле (плюс само поле). Этот макрос будет вызываться следующим образом:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

Таким образом, используя Boost.PP , мы перебираем каждый аргумент и генерируем такие данные:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

Что это такое, генерирует константу fields_n, которая является числом отражаемых полей в классе. Затем он специализируется на field_data для каждого поля. Он также поддерживает класс reflector, поэтому он может обращаться к полям, даже если они являются частными:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

Теперь, чтобы перебирать поля, мы используем шаблон посетителя. Мы создаем диапазон MPL от 0 до количества полей и получаем доступ к данным поля по этому индексу. Затем он передает данные поля посетителю, предоставленному пользователю:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

Теперь на момент истины мы все вместе. Вот как мы можем определить класс Person, который можно отразить:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

Вот обобщенная функция print_fields, использующая данные отражения для итерации по полям:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

Пример использования print_fields с отражаемым классом Person:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

Какие выходы:

name=Tom
age=82

И вуаля, мы только что реализовали отражение в C ++, менее чем в 100 строк кода.

198
ответ дан Paul Fultz II 22 August 2018 в 08:27
поделиться
  • 1
    Престижность для того, чтобы показать, как реализовать отражение, а не говорить, что это невозможно. Это такие ответы, которые делают С. О. отличный ресурс. – fearless_fool 13 May 2014 в 17:00
  • 2
    Обратите внимание: если вы попытаетесь скомпилировать это в Visual Studio, вы получите сообщение об ошибке, потому что VS не обрабатывает расширение вариационного макроса должным образом. Для VS попробуйте добавить: #define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tuple и #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__)) и изменив определение TYPEOF (x) на: #define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,) – Phenglei Kai 27 November 2014 в 04:33
  • 3
    Я получаю ошибку «BOOST_PP_IIF_0» не называет тип. Вы можете помочь. – Ankit Zalani 4 December 2015 в 06:14
  • 4
  • 5
    См. Мой собственный ответ - stackoverflow.com/a/28399807/2338477 Я извлек и переупаковал все определения, и ускорить библиотеку не требуется. В качестве демо-кода я предоставляю сериализацию в xml и восстанавливаю из xml. – TarmoPikaro 24 February 2017 в 23:51

Отражение не поддерживается C ++ из коробки. Это печально, потому что это делает защитное испытание болью.

Существует несколько подходов к отражению:

  1. использует отладочную информацию (не переносимую).
  2. Погрузите свой код с помощью макросов / шаблонов или другого исходного подхода (выглядит уродливым)
  3. Измените компилятор, такой как clang / gcc, чтобы создать базу данных.
  4. Использовать подход Qt moc
  5. Boost Reflect
  6. Точное и плоское отражение

Первая ссылка выглядит наиболее многообещающий (использует mod's to clang), второй обсуждает ряд методов, третий - другой подход с использованием gcc:

  1. http://www.donw.org/rfl/
  2. https://bitbucket.org/dwilliamson/clreflect
  3. https://root.cern.ch/how / how-use-reflex

В настоящее время существует рабочая группа для отражения C ++. См. Новости для C ++ 14 @ CERN:

Редактировать 13/08/17: Поскольку исходный пост имел ряд потенциальных улучшений в отношении отражения. Ниже приводится более подробная информация и обсуждение различных методов и статуса:

  1. Статическое отражение в двух словах
  2. Статическое отражение
  3. Конструкция для статического отражения

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

Ниже приводится информация о текущем состоянии, основанном на обратной связи с последними встречами на C ++:

Редактировать 13/12/2017

Отражение похоже на перемещение к C ++ 20 или, более вероятно, к TSR.

5
ответ дан Pezo 22 August 2018 в 08:27
поделиться
  • 1
    Связь с кормой сломана. – j4n bur53 26 December 2015 в 00:48
  • 2
    ссылки на серну должны быть исправлены. Они часто ломаются, что является болью. – Damian Dixon 13 August 2017 в 09:49
  • 3
    Этот ответ касается только отражения во время компиляции? – einpoklum 29 October 2018 в 17:20
  • 4
    @einpoklum единственные текущие решения для отражения - это время компиляции, обычно с кодом мета-шаблона или макросами. Последний черновик TS выглядит так, как будто он должен работать во время выполнения, но вам нужно было собрать все библиотеки с правильным компилятором для хранения необходимых метаданных. – Damian Dixon 30 October 2018 в 09:36
  • 5
    @DamianDixon: Это не правда. Есть несколько библиотек отражений во время выполнения. Теперь, конечно, они довольно неуклюжи и либо согласны, либо требуют аннотаций компилятора, но они все еще существуют. Если, как я понимаю ваш комментарий, вы ссылались только на рефлексию во время компиляции, отредактируйте ваш ответ, чтобы сделать его более понятным. – einpoklum 30 October 2018 в 09:47

EDIT: CAMP больше не поддерживается; доступны две вилки:

  • Один из них также называется CAMP и основан на том же API.
  • Ponder является частичной перезаписью и будет предпочтительнее, поскольку она не требует Boost; он использует C ++ 11.

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

В текущей библиотеке Tegesoft используется Boost, но есть также fork , используя C ++ 11, что больше не требует Boost .

11
ответ дан philant 22 August 2018 в 08:27
поделиться

Простым способом является использование оператора dynamic_cast<>(), который при присвоении неправильного типа возвращает NULL, поэтому вы можете легко перейти к базовому конкретному классу, проверяя значение указателя, если оно не NULL, литье было выполнено, и вы получили тип объекта.

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

14
ответ дан RandomDSdevel 22 August 2018 в 08:27
поделиться
  • 1
    должен любить эти функции, которые всегда возвращают true;) Я предполагаю, что это не защищено от статических вопросов упорядочения инициализации? – paulm 16 March 2015 в 08:58

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

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

Это заставляет компилятор строить данные определения класса в DLL / Exe.

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

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

Это эффективно делает:

instance_ptr->Foo(1.331);

Функция Invoke (this_pointer, ...) имеет переменные аргументы. Очевидно, вызывая функцию таким образом, вы обходите такие вещи, как const-безопасность и т. Д., Поэтому эти аспекты реализованы как проверки времени выполнения.

Я уверен, что синтаксис может быть улучшен, и это только работает на Win32 и Win64. Мы обнаружили, что это действительно полезно для создания автоматических интерфейсов GUI для классов, создания свойств на C ++, потоковой передачи в XML и т. Д. И т. Д., И нет необходимости выводить из определенного базового класса. Если есть достаточный спрос, возможно, мы могли бы выбить его в форму для выпуска.

36
ответ дан Roderick 22 August 2018 в 08:27
поделиться
  • 1
    Я думаю, вы имеете в виду __declspec(dllexport), и вы можете получить информацию из файла .map, если вы включите создание таких во время сборки. – Orwellophile 10 October 2017 в 13:17

Проверьте Classdesc http://classdesc.sf.net . Он обеспечивает отражение в виде дескрипторов класса, работает с любым стандартным компилятором C ++ (да, как известно, он работает с Visual Studio, а также с GCC), и не требует аннотации исходного кода (хотя существуют некоторые прагмы для обработки сложных ситуаций ). Он находится в разработке уже более десяти лет и используется в ряде промышленных проектов.

3
ответ дан Russell Standish 22 August 2018 в 08:27
поделиться
  • 1
    Добро пожаловать в Stack Overflow. Хотя этот ответ по теме, важно указать, что вы являетесь автором этого программного обеспечения, чтобы было ясно, что это не беспристрастная рекомендация :-) – Matthew Strawbridge 6 April 2013 в 09:07

Обновлено 24.2.2017

Раньше я анализировал поддержку использования # define и, как это рекомендуется в некоторых веб-статьях - я попал в определения, которые на Visual C ++ не работали одинаково по сравнению с используемым определением в gcc (например, в интернете это довольно часто называют «прогулкой по MSVC»). Кроме того, вы не сможете легко понять, что происходит за механизмом разработки / макрорасширения - довольно сложно отладить каждое расширение макросов.

Существует несколько способов обойти сложности определения расширения, одним из подходов является включение флага компилятора «/ P» (только для предварительного процесса в файл) - после этого вы можете сравнить, как открывается ваш определение вверх. (Раньше я также активно использовал stringfy operator (#))

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

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/MacroHelpers.h

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

Я вспомнил рабочий пример кода и поместил его в качестве проекта sourceforge, можно скачать здесь:

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/

Демо-код выглядит следующим образом:

#include "CppReflect.h"
using namespace std;


class Person
{
public:
    REFLECTABLE( Person,
        (CString)   name,
        (int)       age
    )

};

class People
{
public:
    REFLECTABLE( People,
        (CString) groupName,
        (vector<Person>)  people
    )
};


void main(void)
{
    People ppl;
    ppl.groupName = "Group1";

    Person p;
    p.name = L"Roger";
    p.age = 37;
    ppl.people.push_back(p);
    p.name = L"Alice";
    p.age = 27;
    ppl.people.push_back( p );
    p.name = L"Cindy";
    p.age = 17;
    ppl.people.push_back( p );

    CStringA xml = ToXML( &ppl );
    CStringW errors;

    People ppl2;

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLE define использует имя класса + имя поля с offsetof - для определения того, в каком месте в памяти находится определенное поле. Я попытался подобрать терминологию .NET, насколько это возможно, но C ++ и C # отличаются друг от друга, поэтому это не от 1 до 1. Модель отражения всего C ++ находится в классах TypeInfo и FieldInfo для обеспечения времени, возможно расширить поддержку и метода , но я решил сохранить все просто.

Я использовал парсинг pugi xml для извлечения демонстрационного кода в xml и восстановления его обратно из xml.

Таким образом, вывод, созданный демо-код выглядит следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

Также возможно включить любую поддержку 3-го партийного класса / структуры через класс TypeTraits и спецификацию частичного шаблона - определить свой собственный класс TypeTraitsT, в аналогичном способ CString или int - см. пример кода в

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/TypeTraits.h#l65

template <>
class TypeTraitsT<CString> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    { 
        CString* s = (CString*)pField;
        return *s;
    }

    virtual void FromString( void* pField, const wchar_t* value )
    { 
        CString* s = (CString*)pField;
        *s = value;
    }
};

template <>
class TypeTraitsT<int> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    {
        int* p = (int*) pField;
        return std::to_string(*p).c_str();
    }

    virtual void FromString( void* pField, const wchar_t* value )
    {
        int* p = (int*)pField;
        *p = _wtoi(value);
    }
};

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

0
ответ дан TarmoPikaro 22 August 2018 в 08:27
поделиться

Два отражённых решения, о которых я знаю из моих дней C ++:

1) Используйте RTTI, который предоставит вам бутстрап, чтобы вы могли создать свое отражение-поведение, если сможете получить все ваши классы, чтобы получить базовый класс «объект». Этот класс может предоставить некоторые методы, такие как GetMethod, GetBaseClass и т. Д. Что касается того, как эти методы работают, вам нужно вручную добавить некоторые макросы для украшения ваших типов, которые за кулисами создают метаданные в типе, чтобы предоставлять ответы на GetMethods и т. Д.

2) Другим вариантом, если у вас есть доступ к объектам компилятора, является использование DIA SDK . Если я правильно помню, это позволит вам открыть pdbs, который должен содержать метаданные для ваших типов C ++. Этого может быть достаточно, чтобы сделать то, что вам нужно. На этой странице показано, как вы можете получить все базовые типы класса, например.

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

Удачи.

9
ответ дан user4385 22 August 2018 в 08:27
поделиться
  • 1
    Это лукавый и гигантский взлом, с предложенным там предложением DIA SDK. – Sqeaky 21 March 2012 в 05:57

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

Вы можете попробовать http: // www. extreme.indiana.edu/reflcpp/

-2
ответ дан vlad 22 August 2018 в 08:27
поделиться
[g0] Вам нужно посмотреть, что вы пытаетесь сделать, и удовлетворяет ли RTTI вашим требованиям. Я реализовал свое собственное псевдо-отражение для некоторых очень специфических целей. Например, я однажды хотел иметь возможность гибко настраивать то, что будет выводить симуляция. Потребовалось добавить некоторый шаблонный код к классам, которые будут выводиться: [/g0] [f1] [g1] Первый вызов добавляет этот объект в систему фильтрации, которая вызывает метод [f3], чтобы выяснить, какие методы доступны. [/g1] [g2] Затем, в файле конфигурации, вы можете сделать что-то вроде этого: [/g2] [f2] [g3] Посредством некоторой магии шаблонов, включающей [f4], это преобразуется в серию вызовов методов в время выполнения (когда файл конфигурации читается), так что это довольно эффективно. Я бы не советовал делать это, если вам действительно не нужно, но, когда вы это делаете, вы можете делать действительно классные вещи. [/G3]
14
ответ дан RandomDSdevel 5 November 2018 в 05:57
поделиться
  • 1
    должен любить эти функции, которые всегда возвращают true;) Я предполагаю, что это не зависит от статических проблем с порядком инициализации? – paulm 16 March 2015 в 08:58
Другие вопросы по тегам:

Похожие вопросы: