Автоматическое создание оператора потока для struct/class

Есть ли инструмент для автоматического генерации ostream < оператора для структуры или класса?

Ввод (взятый из Одна функция отладки-печати для управления всеми ними):

typedef struct ReqCntrlT    /* Request control record */
{
  int             connectionID;
  int             dbApplID;
  char            appDescr[MAX_APPDSCR];
  int             reqID;
  int         resubmitFlag;
  unsigned int    resubmitNo;
  char            VCIver[MAX_VCIVER];
  int             loginID;
}   ReqCntrlT;

Вывод:

std::ostream& operator <<(std::ostream& os, const ReqCntrlT& r) 
{
   os << "reqControl { "
      << "\n\tconnectionID: " << r.connectionID 
      << "\n\tdbApplID: " << r.dbApplID 
      << "\n\tappDescr: " << r.appDescr
      << "\n\treqID: " << r.reqID
      << "\n\tresubmitFlag: " << r.resubmitFlag
      << "\n\tresubmitNo: " << r.resubmitNo
      << "\n\tVCIver: " << r.VCIver
      << "\n\tloginID: " << r.loginID
      << "\n}";
   return os; 
}

Любой инструмент будет в порядке, скрипты Python/Ruby будут предпочтительнее.

Ответ 1

Что нужно для этого - это инструмент, который может точно анализировать С++, перечислять различные классы/структуры, определять и генерировать ваши "сериализации" на основе каждого класса/структуры, а затем парковать сгенерированный код в "правильном" место "(предположительно та же область, в которой была найдена структура). Ему нужен полный препроцессор для обработки расширений директив в реальном коде.

Наш DMS Software Reengineering Toolkit с его С++ 11 front end может сделать это. DMS позволяет создавать пользовательские инструменты, предоставляя общий синтаксический анализ/построение АСТ, построение таблицы символов, поток и пользовательский анализ, преобразование и восстановление исходного кода. Передняя панель С++ позволяет DMS анализировать С++ и строить точные таблицы символов, а также печатать модифицированные или новые АСТ обратно в компилируемую исходную форму. DMS и его передний конец С++ были использованы для выполнения массивных преобразований на С++-коде.

Вы должны объяснить DMS, что вы хотите сделать; представляется простым перечислить записи таблиц символов, спросить, содержат ли объявления типа struct/class, область определения декларации (записано в записи таблицы символов), построить AST, составив образцы синтаксиса поверхности, а затем применить преобразование для вставки построенного AST.

Требуются шаблоны синтаксиса основной поверхности, такие как для слотов и для тела функции:

 pattern ostream_on_slot(i:IDENTIFIER):expression =
   " << "\n\t" << \tostring\(\i\) << r.\i "; -- tostring is a function that generates "<name>"

 pattern ostream_on_struct(i:IDENTIFIER,ostream_on_slots:expression): declaration =
   " std::ostream& operator <<(std::ostream& os, const \i& r) 
     { os << \tostring\(\i\) << " { " << \ostream_on_slots << "\n}";
       return os; 
     }

Нужно составлять отдельные деревья для ostream_on_slot:

 pattern compound_ostream(e1:expression, e2:expression): expression
     = " \e1 << \e2 ";

С помощью этих шаблонов прямо перечислять слоты struct, строить ostream для тела и вставлять их в общую функцию для структуры.

Ответ 2

Есть два основных способа сделать это:

  • с помощью внешнего инструмента синтаксического анализа (например, Python script, подключенного к привязкам Clang)
  • с использованием техники метапрограммирования

.. и, конечно, их можно смешать.

У меня недостаточно знаний о привязках Clang Python, чтобы ответить на их использование, поэтому я сосредоточусь на метапограмме.


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

Чтобы легко смешивать метапрограммирование и работу во время работы, проще привести библиотеку в игру: Boost.Fusion.

Если вы настроите свою структуру таким образом, чтобы ее атрибуты были описаны в терминах последовательности Boost.Fusion, вы можете автоматически применить множество алгоритмов в последовательности. Здесь лучше всего ассоциировать последовательность.

Поскольку мы говорим о метапрограммировании, карта связывает тип с типизированным значением.

Затем вы можете перебрать эту последовательность, используя for_each.


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

// Can be created using Boost.Preprocessor, but makes array types a tad difficult
DECL_ATTRIBUTES((connectionId, int)
                (dbApplId, int)
                (appDescr, AppDescrType)
                ...
                );

который является синтаксическим сахаром для объявления карты Fusion и связанных с ней тегов:

struct connectionIdTag {};
struct dbApplIdTag {};

typedef boost::fusion::map<
    std::pair<connectionIdTag, int>,
    std::pair<dbApplIdTag, int>,
    ...
    > AttributesType;
AttributesType _attributes;

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

// 1. A predicate:
struct Predicate {
    template <typename T, typename U>
    void operator()(std::pair<T, U> const&) const { ... }
};

// 2. The for_each function
for_each(_attributes, Predicate());

Ответ 3

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

Сначала вы можете использовать инструмент c/С++ analysis и использовать его для извлечения дерева разбора из исходного кода. Затем, когда у вас есть дерево разбора, вам просто нужно искать структуры. Для каждой структуры вы можете теперь генерировать перегрузку operator<<, которая сериализует поля структуры. Вы также можете генерировать десериализатор.

Но это зависит от того, сколько у вас структур: за один десяток лучше написать операторы вручную, но если у вас несколько сотен структур, вы можете написать генератор операторов сериализации (de).

Ответ 4

Я понял ваш вопрос двумя способами.

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

typedef struct ReqCntrlT    /* Request control record */
{
  int             connectionID;
  int             dbApplID;
  char            appDescr[MAX_APPDSCR];
  int             reqID;
  int         resubmitFlag;
  unsigned int    resubmitNo;
  char            VCIver[MAX_VCIVER];
  int             loginID;

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & connectionID;
        ar & reqID;
        ...
    }
}   ReqCntrlT;

Подробнее см. в руководстве: http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/index.html

Если вы пытаетесь "написать" код, просто указав имя параметра. Затем вы должны взглянуть на регулярные выражения в python или perl, например. Главным дефолтом этого решения является то, что вы "автономно" своей структуры, т.е. Должны запускать его каждый раз, когда вы что-то меняете.

Benoit.