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

c++ reflection templates sfinae

170666 просмотра

30 ответа

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

Автор: Nick Источник Размещён: 17.05.2019 02:41

Ответы (30)


215 плюса

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

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

Автор: Paul Fultz II Размещён: 31.07.2012 08:07

96 плюса

Есть два вида reflectionплавания.

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

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

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

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

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

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

Автор: Johannes Schaub - litb Размещён: 24.11.2008 02:30

55 плюса

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

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

Автор: Brad Wilson Размещён: 03.09.2008 11:04

37 плюса

Информация существует - но не в том формате, который вам нужен, и только если вы экспортируете свои классы. Это работает в 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. Мы обнаружили, что это действительно полезно, если у вас есть автоматические графические интерфейсы для классов, создание свойств в C ++, потоковая передача в и из XML и т. Д., И нет необходимости выводить из определенного базового класса. Если есть достаточно спроса, возможно, мы могли бы привести его в форму для выпуска.

Автор: Roderick Размещён: 19.08.2009 02:19

35 плюса

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

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

Автор: Konrad Rudolph Размещён: 03.09.2008 01:18

14 плюса

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

Автор: KeithB Размещён: 03.09.2008 03:00

13 плюса

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

Автор: Ferruccio Размещён: 03.09.2008 11:33

13 плюса

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

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

Автор: Jérôme Размещён: 03.09.2008 01:06

12 плюса

РЕДАКТИРОВАТЬ : CAMP больше не поддерживается; доступны две вилки:

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

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

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

Автор: philant Размещён: 20.06.2010 06:14

10 плюса

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

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

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

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

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

Автор: Michel Размещён: 27.11.2008 09:41

9 плюса

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

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

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

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

Удачи.

Автор: user4385 Размещён: 03.09.2008 12:13

8 плюса

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

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

Автор: Zack Размещён: 23.07.2014 06:34

7 плюса

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

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

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

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

Автор: Germán Diago Размещён: 29.10.2013 12:52

7 плюса

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

Есть несколько подходов к рефлексии:

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

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

Изменить 15/09/2018

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

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

Автор: Damian Dixon Размещён: 04.11.2012 08:15

6 плюса

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

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

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

Автор: Matthieu M. Размещён: 12.11.2010 05:05

6 плюса

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

Автор: Luis Размещён: 24.11.2008 02:20

4 плюса

Решение

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

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

Автор: Nick Размещён: 31.12.2015 08:41

4 плюса

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

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

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

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

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

Автор: Ira Baxter Размещён: 30.12.2010 11:02

3 плюса

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

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

Автор: alariq Размещён: 28.07.2011 08:45

3 плюса

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

Автор: Russell Standish Размещён: 06.04.2013 07:41

2 плюса

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

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

Автор: Crazy Eddie Размещён: 14.06.2010 03:59

2 плюса

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

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

Автор: Bohdan Размещён: 24.11.2011 08:23

2 плюса

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

Автор: Damian Размещён: 17.02.2012 10:03

2 плюса

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

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

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

Автор: Naore Azenkut Размещён: 02.02.2013 11:19

2 плюса

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

Автор: Jen Размещён: 12.03.2014 12:18

1 плюс

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

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

Автор: Evgeny Mamontov Размещён: 07.11.2016 11:37

0 плюса

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

Смотрите https://root.cern.ch/how/how-use-reflex

Автор: Buğra Gedik Размещён: 17.06.2016 02:15

0 плюса

Обновлено 24.2.2017

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

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

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

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 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://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. Если кто-то знает, как обойти это, дайте мне знать.

Автор: TarmoPikaro Размещён: 08.02.2015 09:54

0 плюса

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

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) - знание аргументов и типа функции.

Автор: JL2210 Размещён: 03.03.2019 08:30

0 плюса

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

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

for...(auto& member : object) {
    using member_type = std::remove_cvref_t<decltype(member)>;
    member = get_data<member_type>();
}
Автор: Guillaume Racicot Размещён: 22.03.2019 05:30
Вопросы из категории :
32x32