Может ли указатель C ++ указывать на статический член массива строковых литералов? - PullRequest
0 голосов
/ 15 февраля 2019

Все работает в Visual Studio 2017, но я получаю ошибки компоновщика в GCC (6.5.0).

Вот пример кода, который изолирует мою проблему:

#include <iostream>

struct Foo{
    static constexpr const char* s[] = {"one","two","three"};
};

int main(){
    std::cout << Foo::s[0] << std::endl;  //this works in both compilers
    const char* const* str_ptr = nullptr;
    str_ptr = Foo::s;                     //LINKER ERROR in GCC; works in VS
    std::cout << str_ptr[1] << std::endl; //works in VS
    return 0;
}

В GCCЯ получаю undefined reference to 'Foo::s'.Мне нужно, чтобы инициализация Foo::s оставалась в объявлении структуры, поэтому я использовал constexpr.Есть ли способ ссылаться на Foo::s динамически, то есть с указателем?

Дополнительная справочная информация

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

Моя фактическая кодовая база - это тысячи строк, и она работает в Visual Studio, но вот пример с развернутой игрушкой:

#include <iostream>
#include <string>
#include <vector>

//A struct for an arbitrary parameter
struct Parameter {
    std::string paramName;
    int max_value;
    int value;
    const char* const* str_ptr = nullptr;
};

//Structure of parameters - MUST BE DEFINED IN ONE PLACE
struct Param_FavoriteIceCream {
    static constexpr const char* n = "FavoriteIceCream";
    enum { vanilla, chocolate, strawberry, NUM_MAX };
    static constexpr const char* s[] = { "vanilla","chocolate","strawberry" };
};
struct Param_FavoriteFruit {
    static constexpr const char* n = "FavoriteFruit";
    enum { apple, banana, grape, mango, peach, NUM_MAX };
    static constexpr const char* s[] = { "apple","banana","grape","mango","peach" };
};

int main() {
    //Set of parameters - determined at compile-time
    std::vector<Parameter> params;
    params.resize(2);

    //Configure these parameters objects - determined at compile-time
    params[0].paramName = Param_FavoriteIceCream::n;
    params[0].max_value = Param_FavoriteIceCream::NUM_MAX;
    params[0].str_ptr = Param_FavoriteIceCream::s; //!!!! LINKER ERROR IN GCC !!!!!!

    params[1].paramName = Param_FavoriteFruit::n;
    params[1].max_value = Param_FavoriteFruit::NUM_MAX;
    params[1].str_ptr = Param_FavoriteFruit::s; //!!!! LINKER ERROR IN GCC !!!!!!

    //Set values by parsing files - determined at run-time
    std::string param_string = "FavoriteFruit"; //this would be loaded from a file
    std::string param_value = "grape"; //this would be loaded from a file
    for (size_t i = 0; i < params.size(); i++) {
        for (size_t j = 0; j < params[i].max_value; j++) {
            if (params[i].paramName == param_string
            && params[i].str_ptr[j] == param_value) {
                params[i].value = j;
                break;
            }
        }
    }
    return 0;
}

Как видите, задействованы перечисления и массивы строк, и они должны совпадать, поэтому в целях обслуживания мне нужно хранить их в одном месте.Кроме того, поскольку этот код уже написан и будет использоваться как в среде Windows, так и в Linux, чем меньше исправление, тем лучше.Я бы предпочел не переписывать тысячи строк только для того, чтобы компилировать их в Linux.Спасибо!

Ответы [ 3 ]

0 голосов
/ 15 февраля 2019

Программа действительна на C ++ 17.Программа не действует в C ++ 14 или более ранних стандартах.Стандартный режим GCC 6.5.0 по умолчанию - C ++ 14.

Чтобы программа соответствовала C ++ 14, вы должны определить статический член (ровно в одной единице перевода).Начиная с C ++ 17, объявление constexpr является неявным определением встроенной переменной, поэтому отдельное определение не требуется.

Решение 1. Обновите свой компилятор и используйте стандарт C ++ 17 (или более позднюю версию, если вы избудущее), который имеет встроенные переменные.Встроенные переменные были внедрены после GCC 7.

Решение 2. Определите переменную вне определения класса ровно в одной единице перевода (инициализация остается в объявлении).

0 голосов
/ 15 февраля 2019

Для C ++ 98, C ++ 11 и C ++ 14 вам необходимо явно указать компилятору, где происходит инициализация Foo :: s (см. Ниже), и вы готовы идти вперед.

struct Foo{
    static const char* s[];
};
const char* Foo::s[] = {"one","two","three"};

И, как объяснено в одном из комментариев, ваша инициализация в порядке с C ++ 17.

0 голосов
/ 15 февраля 2019

Похоже, вы не используете C ++ 17, а до C ++ 17 это неопределенное поведение, которое мне действительно не нравится.У вас нет определения для s, но вы используете ODR it, что означает, что вам нужно иметь определение.

Чтобы определить это, вы должны определить его в файле .cpp.

В C ++ 17 это будет действительный код.Я не знаком с MSVC, поэтому не уверен, почему он там работает нормально - будь то, что он скомпилирован как C ++ 17, или потому, что это просто другое проявление неопределенного поведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...