буст-вариант может вызвать деструктор для неактивного варианта? - PullRequest
0 голосов
/ 09 декабря 2018

Я использую код C через свою оболочку C ++ для него, в частности, я обертываю union с boost::variant, проблема в том, что clang сообщает статическому анализатору «Назначенное значение является мусором или неопределенным» в очень конкретном случае - boost ::вариант был создан с типом int (статический анализатор clang описан через "take branch true"), никогда не изменялся, но затем с помощью анализа статического анализатора clang boost::variant desctructor может вызывать деструктор не для типа int.

Вот мой код:

#include <boost/variant.hpp>
#include <cstdint>
#include <iostream>
#include <string>

extern "C" {

struct CFooString {
  const char *data;
  uintptr_t len;
  uintptr_t capacity;
};

void cfoo_string_free(struct CFooString str);

struct CResultObjectString {
  uint8_t is_ok;
  union {
    int ok;
    struct CFooString err;
  } data;
};

struct CResultObjectString c_func(int);
}

class MyString final : private CFooString {
public:
  explicit MyString(const CFooString &o) noexcept {
    data = o.data;
    len = o.len;
    capacity = o.capacity;
  }
  MyString() = delete;
  MyString(const MyString &) = delete;
  MyString &operator=(const MyString &) = delete;
  MyString(MyString &&o) noexcept {
    data = o.data;
    len = o.len;
    capacity = o.capacity;

    reset(o);
  }
  MyString &operator=(MyString &&o) noexcept {
    free_mem();
    data = o.data;
    len = o.len;
    capacity = o.capacity;

    reset(o);
    return *this;
  }
  ~MyString() noexcept { free_mem(); }
  std::string to_std_string() const { return std::string(data, len); }
  size_t size() const noexcept { return this->len; }
  bool empty() const noexcept { return this->len == 0; }

private:
  void free_mem() noexcept {
    if (data != nullptr) {
      cfoo_string_free(*this);
      reset(*this);
    }
  }
  static void reset(MyString &o) noexcept {
    o.data = nullptr;
    o.len = 0;
    o.capacity = 0;
  }
};

boost::variant<int, MyString> f1() {
  CResultObjectString ret = c_func(17);
  return ret.is_ok != 0 ? boost::variant<int, MyString>{ret.data.ok}
                        : boost::variant<int, MyString>{MyString{ret.data.err}};
}

int main() {
  auto ret = f1();
  if (boost::get<int>(&ret) != nullptr) {
    int p = boost::get<int>(std::move(ret));
    std::cout << "we get : " << p;
  } else {
    auto err = boost::get<MyString>(std::move(ret));
    std::cout << err.to_std_string();
  }
}

scan-build g ++ -std = c ++ 11 -pedantic -Wall -Wextra -c test.cpp сообщает:

scan-build g++ -std=c++11 -pedantic -Wall -Wextra -c test.cpp
scan-build: Using '/usr/bin/clang-7' for static analysis
test.cpp:39:9: warning: Assigned value is garbage or undefined
    len = o.len;
        ^ ~~~~~
test.cpp:61:7: warning: Passed-by-value struct argument contains uninitialized data (e.g., field: 'len')
      cfoo_string_free(*this);
      ^~~~~~~~~~~~~~~~~~~~~~~

Еслипрочитайте описание статического анализатора clang, он думает, что после создания boost::variant<int, MyString>{ret.data.ok} возможно, что будет вызван MyString::~MyString.

Это что-то не так с моим кодом или boost::variant или clang static analyzer?

...