Переключатель C++ не скомпилирует с внешне определенной переменной, используемой в качестве случая

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

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

, Конечно, то же общее представление используется экстенсивно на языках с надлежащим отражением - просто instrospect класс, и воздействуйте на каждое поле в свою очередь. Выполнение его в препроцессоре C является хрупким, неразборчивым, и не всегда портативным. Таким образом, я упоминаю его с некоторым трепетом. Тем не менее, здесь это...

(РЕДАКТИРОВАНИЕ: Я вижу теперь, когда это подобно тому, что @Andrew Johnson сказал относительно 9/18; однако идея рекурсивно включая тот же файл берет идею немного далее.)

// file foo.h, defines class Foo and various members on it without ever repeating the
// list of fields.

#if defined( FIELD_LIST )
   // here's the actual list of fields in the class.  If FIELD_LIST is defined, we're at
   // the 3rd level of inclusion and somebody wants to actually use the field list.  In order
   // to do so, they will have defined the macros STRING and INT before including us.
   STRING( fooString )
   INT( barInt )   
#else // defined( FIELD_LIST )

#if !defined(FOO_H)
#define FOO_H

#define DEFINE_STRUCT
// recursively include this same file to define class Foo
#include "foo.h"
#undef DEFINE_STRUCT

#define DEFINE_CLEAR
// recursively include this same file to define method Foo::clear
#include "foo.h"
#undef DEFINE_CLEAR

// etc ... many more interesting examples like serialization

#else // defined(FOO_H)
// from here on, we know that FOO_H was defined, in other words we're at the second level of
// recursive inclusion, and the file is being used to make some particular
// use of the field list, for example defining the class or a single method of it

#if defined( DEFINE_STRUCT )
#define STRING(a)  std::string a;
#define INT(a)     long a;
   class Foo
   {
      public:
#define FIELD_LIST
// recursively include the same file (for the third time!) to get fields
// This is going to translate into:
//    std::string fooString;
//    int barInt;
#include "foo.h"
#endif

      void clear();
   };
#undef STRING
#undef INT
#endif // defined(DEFINE_STRUCT)


#if defined( DEFINE_ZERO )
#define STRING(a) a = "";
#define INT(a) a = 0;
#define FIELD_LIST
   void Foo::clear()
   {
// recursively include the same file (for the third time!) to get fields.
// This is going to translate into:
//    fooString="";
//    barInt=0;
#include "foo.h"
#undef STRING
#undef int
   }
#endif // defined( DEFINE_ZERO )

// etc...


#endif // end else clause for defined( FOO_H )

#endif // end else clause for defined( FIELD_LIST )
5
задан sth 8 December 2009 в 21:37
поделиться

7 ответов

что делает их лучше, чем написание собственной библиотеки?

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

  • вашему приложению придется взаимодействовать с машиной с прямым порядком байтов (sparc / powerpc) с машины с прямым порядком байтов (x86, Intel / amd). В вашей системе обмена сообщениями было некоторое допущение о порядке порядка следования байтов: пойдите и исправьте
  • вы разработали свое приложение так, чтобы оно не было двоичным протоколом / системой обмена сообщениями, и теперь оно работает очень медленно, потому что вы тратите большую часть своего времени на его анализ (количество сообщений увеличился и синтаксический анализ стал узким местом): адаптируйте его так, чтобы он мог передавать двоичную / фиксированную кодировку
  • вначале у вас было 3 машины внутри локальной сети, без заметных задержек, все попадает на каждую машину. появляется ваш клиент / босс / заостренный-дьявол-босс и сообщает вам, что вы установите приложение в WAN, которым вы не управляете, - а затем у вас начнутся сбои соединения, плохая задержка и т. д. вам нужно сохранить сообщение и повторить попытку отправки их позже: вернитесь к коду и вставьте все это (и наслаждайтесь)

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

  • некоторые сообщения являются критическими, и их прием / отправка требует надлежащего резервного копирования / сохранения /. Почему ты спрашиваешь ? для целей аудита

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

Вы можете реализовать это самостоятельно, но не тратьте на это много времени: вы, вероятно, все равно замените его позже.

)

  • некоторые сообщения являются критическими, и их прием / отправка требует надлежащего резервного копирования / сохранения /. Почему ты спрашиваешь ? для целей аудита
  • И многие другие варианты использования, которые я забыл ...

    Вы можете реализовать это самостоятельно, но не тратьте на это много времени: вы, вероятно, все равно замените его позже.

    )

  • некоторые сообщения являются критическими, и их прием / отправка требует надлежащего резервного копирования / сохранения /. Почему ты спрашиваешь ? для целей аудита
  • И многие другие варианты использования, которые я забыл ...

    Вы можете реализовать это самостоятельно, но не тратьте на это много времени: вы, вероятно, все равно замените его позже.

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

    Ограничения в стандарте запрещают использование, например:

    void f(int a, int b)
    {
        const int c = b;
    
        switch (a)
        {
        case c:
            //...
        }
    }
    

    В вашем примере, когда компилятор компилирует test.cpp , у него нет способа определить, какой инициализатор может быть в x_def.cpp . Вы могли бы сделать:

    const int test_int = (int)time();
    

    Очевидно, что ни в одном из этих примеров нельзя определить значение const int во время компиляции, что является целью для интегральных константных выражений.

    void f(int a, int b)
    {
        const int c = b;
    
        switch (a)
        {
        case c:
            //...
        }
    }
    

    В вашем примере, когда компилятор компилирует test.cpp , у него нет способа определить, какой инициализатор может быть в x_def.cpp . Вы могли бы сделать:

    const int test_int = (int)time();
    

    Очевидно, что ни в одном из этих примеров нельзя определить значение const int во время компиляции, что является целью для интегральных константных выражений.

    void f(int a, int b)
    {
        const int c = b;
    
        switch (a)
        {
        case c:
            //...
        }
    }
    

    В вашем примере, когда компилятор компилирует test.cpp , у него нет способа определить, какой инициализатор может быть в x_def.cpp . Вы могли бы сделать:

    const int test_int = (int)time();
    

    Очевидно, что ни в одном из этих примеров нельзя определить значение const int во время компиляции, что является целью для интегральных константных выражений.

    5
    ответ дан 18 December 2019 в 13:15
    поделиться

    Метки case должны быть константами времени компиляции. Это означает, что компилятор должен иметь возможность заменять значение во время компиляции. Хотя ваши значения постоянны, компилятор не может узнать их значения, по крайней мере, до времени компоновки.

    4
    ответ дан 18 December 2019 в 13:15
    поделиться

    Я не могу воспроизвести это на тривиальном примере с помощью VC ++ 2008:

    test.cpp:

    extern const int n;
    int main() {
        switch (0) {
        case n: break;
        }
    }
    

    test2.cpp:

    extern const int n = 123;
    

    скомпилировать с:

    cl.exe test.cpp test2.cpp
    

    вывод:

    test.cpp (4): ошибка C2051: выражение case не константа

    1
    ответ дан 18 December 2019 в 13:15
    поделиться

    VC ++ правильный, а g ++ неправильный. Метка case должна быть интегральным константным выражением (§6.4.2 / 2), а константная переменная целочисленного типа, инициализированная константным выражением, является константным выражением (§5.19 / 1).

    Edit: в основном для Павла и его предложения о возможном DR. §5.19 / 2 был полностью переписан. C ++ 0x добавляет совершенно новую концепцию constexpr , которая значительно расширяет то, что считается константным выражением. Например, в соответствии с текущим стандартом что-то вроде:

    int x() { return 10; }
    const int y = x();
    

    y не является постоянным выражением. Мы все легко можем видеть, что он (косвенно) инициализируется литералом 10 , но компилятор по-прежнему не может разрешить его как постоянное выражение. По новому стандарту это ' Можно будет обозначить x () как constexpr , а y будет постоянным выражением.

    Как это сформулировано в N2960, §5.19 / 2 говорит, что выражение является постоянным выражением, если оно не использует что-либо из следующего списка. Затем он дает список длиной в страницу, но использование переменной const , которая не инициализирована в текущем модуле компиляции, не кажется одним из них. [Изменить: см. Ниже - прочитав выпуск 721 CWG, я передумал.]

    Что касается VC ++, то есть правильности, а g ++ - неправильности, я имел в виду только этот очень конкретный аспект. Нет никаких сомнений в том, что оба «неправильны», если вы говорите о правильности каждой части стандарта. Я сомневаюсь, что кто-то вообще работает над реализацией экспорта для любого из них.

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

    Редактировать: Я немного поискал и обнаружил проблему 721 основной рабочей группы. Вопрос Джейма довольно близко соответствует рассматриваемому («Однако для этого не требуется, как предположительно должно, чтобы инициализация происходила в той же единице перевода и предшествовала константному выражению ...»). Предлагаемое постановление добавляет фразу: «... с предшествующей инициализацией ...». По крайней мере, когда я ее читал, это означает, что комитет согласился с тем, что в соответствии с текущим стандартом код должен быть принят, но в соответствии с новым стандартом это не разрешено.

    Эта формулировка была согласована в июле этого года, но не (пока?) не появился в N2960, который, я считаю, является самым последним проектом C ++ 0x.

    3
    ответ дан 18 December 2019 в 13:15
    поделиться

    Компилятор MS здесь немного шалит. Когда вы компилируете инициализацию константы и оператор case с использованием константы в одном модуле компиляции, она вычисляет значение константы во время компиляции.

    Как только вы пытаетесь использовать extern const вне модуля компиляции, в котором он инициализирован (т. е. файл cpp, содержащий инициализацию, или любой из файлов, которые он включает), компилятор отключит примерно такую ​​же ошибку. Фред Ларсон прав, компилятор не должен знать постоянное значение до времени компоновки, и, следовательно, оно не должно быть приемлемым в качестве константы переключения, это просто компилятор MS немного обманывает.

    Решение вашего проблема заключается в использовании макросов, есть ли причина, по которой вы не

    1
    ответ дан 18 December 2019 в 13:15
    поделиться

    Вот более простой тест:

    test_int.cpp:

    const int test_int = 10;
    

    main.cpp:

    #include <iostream>
    using std::cout;
    using std::endl;
    
    extern const int test_int;
    
    int main() {
        cout << test_int << endl;
        return 0;
    }
    

    В G ++ я получаю неопределенную ссылку. Однако то же самое в C работает. Согласно http://gcc.gnu.org/ml/gcc/2005-06/msg00325.html , переменная const неявно имеет внутреннюю связь в C ++. Похоже, что это не так в C.

    0
    ответ дан 18 December 2019 в 13:15
    поделиться

    Я использую "gcc (SUSE Linux) 4.3.2" и имею похожий эффект, но это все еще немного странно.

    Мои определения:

    namespace operations{
       const cOpDummy OpDummy();
       const cInitOperator InitOperator();
    };
    
    const unsigned long ulNumberOfOperations = 2;
    
    const cOperation * arrayOperations[] = {
       & (operations::OpDummy),
       & (operations::InitOperator)
    };
    

    А объявления extern в другом файле:

    extern const unsigned long ulNumberOfOperations;
    extern const cOperation * arrayOperations[];
    

    Забавная вещь: Компилятор выдает только для "ulNumberOfOperations" "undefined reference to ulNumberOfOperations", но с "arrayOperations[]" все в порядке. Мой обходной путь - объявить "ulNumberOfOperations" не константой.

    0
    ответ дан 18 December 2019 в 13:15
    поделиться
    Другие вопросы по тегам:

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