Ошибка сегментации при воздействии строки в теговом объединении - PullRequest
0 голосов
/ 12 ноября 2018

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

//tagged union
union VariableValueType {
    std::int64_t  i;
    std::string   s;

    VariableValueType(void): i(0) {}
    ~VariableValueType(void) {}
};
enum class VariableType {Undefined, Integer, String};

class Variable
{
    VariableValueType _value;
    VariableType _type;

    //constructor from a string
    Variable(const VariableType type, const std::string& value)
     : _type(VariableType::Undefined)
    {
        set(type,value);
    }

    // setter
    void set(const VariableType type, const std::string& value)
    {
        switch(type)
        {
            case VariableType::Integer :
                _value.i = std::stoi(value);
                _type = VariableType::Integer;
                break;
            case VariableType::String :
                _value.s = value; //<-- crashes here!!!
                _type = VariableType::String;
                break;
        }
    }
};

Когда я вызываю set из другой точки кода с type=String, я получаю ошибку сегментации в строке влияния на строку.

#0  0x00007ffff7b70c73 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x00007ffff7b70f89 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00005555556c3fdd in Variable::set (this=0x5555559d7ae0, type=VariableType::String, value="1") at /home/.../Variable.cpp:118

Я не могу понять, что здесь происходит ...

Интересный факт: если я добавлю std::string foo = value; в качестве первой строки метода set, то ошибка сегментации исчезнет ...


Редактировать

Когда я спрашиваю GDB о значении _value.s, я получаю следующее сообщение:

error reading variable: Cannot create a lazy string with address 0x0, and a non-zero length.

Что я не понимаю ...

Ответы [ 2 ]

0 голосов
/ 12 ноября 2018

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

Здесь это будет выглядеть так:

// before assigning something else than a string
if(_type == VariableType::String) _value.s.~std::string();

// to assign a string if current type is *not* a string
new(&_value.s) std::string(value);
// to assign a string if current type is a string
_value.s = value;

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

  • Прямо доступно в C ++ 17 как std::variant
  • Доступно через Boost как boost::variant
  • Доступно через mapbox / вариант (Альтернатива повышения: более легкая библиотека)
0 голосов
/ 12 ноября 2018

Не используйте строку в объединении. Не используйте ничего с объектом, который не является POD (например, с конструктором / деструктором) в объединении.

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

Вместо этого используйте std::variant (или boost::variant, если у вас нет C ++ 17).

...