Как бороться с Boost Spirit X3, вызывающим «фиаско порядка инициализации stati c» в Visual Studio 2019? - PullRequest
0 голосов
/ 22 января 2020

Я работаю над нетривиальным синтаксическим анализатором в C ++ поверх boost :: spirit :: x3. Я делю свой код разбора на логические единицы, некоторые из которых являются зависимостями друг от друга. Например, одна единица является анализатором выражений, который также предоставляет синтаксический анализатор идентификатора. Многие высокоуровневые конструкции syntacti c целевого языка включают выражения и идентификаторы, поэтому этот модуль часто является зависимостью. Я разбил код на тройки файлов , как рекомендует документация . Если есть единицы foo , bar и quux , у меня есть файлы вроде:

parser
    foo.h
    foo_def.h
    foo.cpp
    bar.h
    bar_def.h
    bar.cpp
    quux.h
    quux_def.h
    quux.cpp

, где .h расширяется BOOST_SPIRIT_DECLARE; _def.h расширяется BOOST_SPIRIT_DEFINE, а .cpp расширяется BOOST_SPIRIT_INSTANTIATE. Я столкнулся с постоянной проблемой, из-за которой мой код выдает ошибки при запуске из-за фиаско инициализации stati c в коде Spirit (строка 160 из ... / x3 / nonterminal / rule.hpp), но только при выполнении отладочной сборки.

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

  1. Только в отладочных сборках используйте объявление прагмы, чтобы изменить порядок инициализации stati c. (Изменение порядка элементов в файле .vcxproj не повлияло на порядок инициализации) В Visual Studio, если я добавлю #pragma init_seg(lib) в файл. cpp моего анализатора выражений, проблема исчезнет.

  2. Только в отладочных сборках при необходимости включайте лишние _def.h файлы, например, если quux зависит от bar выше, то включение bar_def.h в quux.cpp исправит проблема, но побеждает цель разбить анализатор на несколько файлов.

Мне вообще интересно, каково состояние этой проблемы? Кажется, это известная проблема, но она существует уже давно, и поэтому я не должен ожидать, что она будет исправлена ​​на уровне Духа? Есть ли лучший способ структурировать мой код так, чтобы эта проблема не возникала? Мне было интересно, возможно ли, например, создать все парсеры и их дочерние парсеры как переменные stati c в области действия функции, создаваемой при первом использовании, но в литературе нет примеров, подобных этому, поэтому я не уверен, что это так. возможный?

1 Ответ

1 голос
/ 22 января 2020

Обычный способ - иметь функции, возвращающие локальную статику по ссылке.

Функционально-локальные статики

  • имеют stati c «время жизни» (продолжительность хранения), но будут инициализированы только при первом использовании (уклонение от фиаско)
  • Начиная с C ++ 11, вы даже можете полагаться на инициализацию для обеспечения безопасности потоков

Итак, вы можете иметь в заголовке:

my_rule_type const& my_rule();

И в cpp, что определяет правило:

my_rule_type const& my_rule() {
     static const my_rule_type s_my_rule = ns::my_rule;
     return s_my_rule;
}

ОБНОВЛЕНИЕ

На самом деле пример документации не заботится о возврате ссылок, вместо этого каждый раз возвращает копию:

parser::employee_type employee()
{
    return parser::employee;
}

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

...