Как я могу добавить отражение в приложение на C ++?

StackOverflow https://stackoverflow.com/questions/41453

Вопрос

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

Это было полезно?

Решение 17

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

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

Другие советы

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

Во-первых, чтобы упростить и очистить запись в препроцессоре, мы будем использовать типизированное выражение.Типизированное выражение - это просто выражение, которое заключает тип в круглые скобки.Поэтому вместо того, чтобы писать 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
)

Таким образом, используя Повышение.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;
    };
};

Теперь для перебора полей мы используем шаблон visitor.Мы создаем диапазон 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 строках кода.

Существует два вида reflection плавает вокруг.

  1. Проверка путем перебора элементов типа, перечисления его методов и так далее.

    Это невозможно с C ++.
  2. Проверка путем проверки того, имеет ли тип класса (class, struct, union) метод или вложенный тип, является производным от другого конкретного типа.

    Такого рода вещи возможны с помощью C ++, использующего template-tricks.Использование boost::type_traits для многих вещей (например, для проверки того, является ли тип целым).Для проверки существования функции-члена используйте Можно ли написать шаблон для проверки существования функции? .Чтобы проверить, существует ли определенный вложенный тип, используйте plain СФИНАЭ .

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

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

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

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

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

  

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

Это просто неправильно. На самом деле, сам термин «RTTI» был придуман стандартом C ++. С другой стороны, RTTI не идет слишком далеко в реализации рефлексии.

Информация существует, но не в том формате, который вам нужен, и только в том случае, если вы экспортируете свои классы. Это работает в 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-safety и т. Д., Поэтому эти аспекты реализуются как проверки во время выполнения.

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

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

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

Первый вызов добавляет этот объект в систему фильтрации, которая вызывает метод BuildMap () , чтобы выяснить, какие методы доступны.

Затем в файле конфигурации вы можете сделать что-то вроде этого:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

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

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

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

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

Редактировать: ЛАГЕРЬ больше не поддерживается ;доступны две вилки:

  • Один из них также называется ЛАГЕРЬ тоже и основан на том же API.
  • Обдумать это частичная перезапись, и она должна быть предпочтительнее, поскольку не требует повышения ;он использует C ++ 11.

ЛАГЕРЬ является лицензированной библиотекой MIT (ранее LGPL), которая добавляет отражение в язык C ++.Это не требует определенного этапа предварительной обработки при компиляции, но привязка должна быть выполнена вручную.

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

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

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

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

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

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

Два похожих на рефлексию решения, которые я знаю по моим дням в C ++:

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

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

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

Удачи.

В C ++ есть еще одна новая библиотека для отражения, которая называется RTTR (Отражение типа времени выполнения, см. также github ).

Интерфейс похож на отражение в C # и работает без каких-либо RTTI.

Редактировать:Обновлена неработающая ссылка по состоянию на 7 февраля 2017 года.

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

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

Рефлекс ЦЕРНа.Кажется, это работает очень хорошо.

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

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

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

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

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

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

Редактировать 13.08.17:Со времени публикации первоначального поста был достигнут ряд потенциальных улучшений в области рефлексии.Ниже приводится более подробная информация и обсуждение различных методов и статуса:

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

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

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

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

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

Редактировать 15/09/2018

Проект ТЗ был разослан национальным органам для голосования.

Текст можно найти здесь: https://github.com/cplusplus/reflection-ts

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

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

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

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

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

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

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

Наш инструментарий реинжиниринга программного обеспечения DMS является обобщенной технологией компилятора, параметризованной явным языком определения. Он имеет языковые определения для C, C ++, Java, COBOL, PHP, ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Где ENUMERATE_MEMBERS - это макрос, который будет описан позже(ОБНОВЛЕНИЕ):

Предположим, мы определили функцию сериализации для 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 ...) к каждой структуре, в то время как реализация определенного метода для каждого типа элемента является обязательной в любом решении, поэтому я не считаю это накладными расходами.

Обновить:Существует очень простая реализация макроса 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 строк кода ;)

С C ++ 20 вы получили операторы расширения , которые позволяют выполнять итерации по типам агрегатов:

struct my_type {
    double data;
    std::string another_data;
    int last_data;
};

auto object = my_type{};

for...(auto& member : object) {
    using member_type = std::remove_cvref_t<decltype(member)>;
    member = get_data<member_type>();
}

Если вы ищете относительно простое отражение C ++ - я собрал из различных источников макро / определения и прокомментировал их, как они работают. Вы можете скачать заголовок файлы отсюда:

https://github.com/tapika/TestCppReflect/blob/master /MacroHelpers.h

набор определений плюс функциональность поверх него:

https://github.com/tapika/TestCppReflect/blob/master /CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect. каст https://github.com/tapika/TestCppReflect/blob/master/TypeTraits. ч

Пример приложения также находится в репозитории git, здесь: https://github.com/tapika/TestCppReflect/

Я частично скопирую это здесь с объяснением:

#include "CppReflect.h"
using namespace std;


class Person
{
public:

    // Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
    // form , like this:

    REFLECTABLE( Person,
        (CString)   name,
        (int)       age,
...
    )
};

void main(void)
{
    Person p;
    p.name = L"Roger";
    p.age = 37;
...

    // And here you can convert your class contents into xml form:

    CStringW xml = ToXML( &p );
    CStringW errors;

    People ppl2;

    // And here you convert from xml back to class:

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

}

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

Я использовал pugi xml parser для извлечения демонстрационного кода в 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>

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

https://github.com/tapika/TestCppReflect/blob /master/TypeTraits.h#L195

Это решение применимо для Windows / Visual Studio. Можно портировать его на другие ОС / компиляторы, но этого еще не сделали. (Спросите меня, действительно ли вам нравится решение, я могу вам помочь)

Это решение применимо для сериализации одним выстрелом одного класса с несколькими подклассами.

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

https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel

Более подробную информацию можно найти в видео на YouTube:

Отражение типа среды выполнения C ++ https://youtu.be/TN8tJijkeFE

Я пытаюсь объяснить немного глубже, как будет работать отражение c ++.

Пример кода будет выглядеть, например, так:

https://github.com/tapika/cppscriptcore/blob /master/SolutionProjectModel/testCppApp.cpp

c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;

Но каждый шаг здесь на самом деле приводит к вызову функции Использование свойств C ++ с __ declspec (property (get =, put ...) .

, который получает полную информацию о типах данных C ++, именах свойств C ++ и указателях экземпляров классов в форме пути, и на основе этой информации вы можете генерировать xml, json или даже сериализовать эту информацию через Интернет.

Примеры таких виртуальных функций обратного вызова можно найти здесь:

Если вы объявите указатель на функцию, подобную этой:

int (*func)(int a, int b);

Вы можете назначить место в памяти этой функции следующим образом (требуется libdl и dlopen )

#include <dlfcn.h>

int main(void)
{
    void *handle;
    char *func_name = "bla_bla_bla";
    handle = dlopen("foo.so", RTLD_LAZY);
    *(void **)(&func) = dlsym(handle, func_name);
    return func(1,2);
}

Чтобы загрузить локальный символ с помощью косвенного обращения, вы можете использовать dlopen в вызывающем двоичном файле ( argv [0] ).

Единственное требование для этого (кроме dlopen () , libdl и dlfcn.h ) - знание аргументов и типа функция.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top