Ошибка Boost property_tree: преобразование данных в тип "j" не удалось при получении элемента в файле .ini - PullRequest
1 голос
/ 28 мая 2020

У меня есть файл .ini, содержащий данные ниже

[SYSTEM]
num_of_vps = 1

И у меня есть этот код для чтения элемента в файле .ini. (uint определяется как typedef unsigned int uint)

boost::property_tree::ptree pt; 
boost::property_tree::ini_parser::read_ini(iniFilePath, pt);
hwCount = pt.get<uint>("SYSTEM.num_of_vps"); 

Я создал из файлов, включая приведенный выше код, и вызвал его в функции-оболочке в файле main.cc. Затем я получаю сообщение об ошибке ниже

terminate, вызванное после выброса экземпляра 'boost_1_68_0 :: exception_detail :: clone_impl > 'what (): преобразование данных в тип "j" не удалось выполнить трассировку стека

#12 0x00002aaab613fcd5 in abort () from /lib64/libc.so.6
#13 0x00002aaab9b29315 in __gnu_cxx::__verbose_terminate_handler () at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/vterminate.cc:95
#14 0x00002aaab9a9e8f6 in __cxxabiv1::__terminate (handler=<optimized out>) at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:47
#15 0x00002aaab9a9e941 in std::terminate () at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:57
#16 0x00002aaab9a9ea74 in __cxxabiv1::__cxa_throw (obj=<optimized out>, tinfo=0x2aaab9e1ff60 <typeinfo for boost_1_68_0::exception_detail::clone_impl<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> >>, dest=0x2aaab99bef18 <boost_1_68_0::exception_detail::clone_impl<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> >::~clone_impl()>) at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/eh_throw.cc:93
#17 0x00002aaab99bec82 in boost_1_68_0::throw_exception<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> > (e=...) at throw_exception.hpp:72
#18 0x00002aaab99be576 in boost_1_68_0::exception_detail::throw_exception_<boost_1_68_0::property_tree::ptree_bad_data> (x=..., current_function=0x2aaab9b45fc0 <boost_1_68_0::enable_if<boost_1_68_0::property_tree::detail::is_translator<boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int> >, unsigned int>::type boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<unsigned int, boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int> >(boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int>) const::__PRETTY_FUNCTION__> "typename boost_1_68_0::enable_if<boost_1_68_0::property_tree::detail::is_translator<Translator>, Type>::type boost_1_68_0::property_tree::basic_ptree<Key, Data, KeyCompare>::get_value(T"..., file=0x2aaab9b45830 "property_tree/detail/ptree_implementation.hpp", line=675) at throw_exception.hpp:89
#19 0x00002aaab99be01e in boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<unsigned int, boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int> > (this=0xbc86c8, tr=...) at property_tree/detail/ptree_implementation.hpp:673
#20 0x00002aaab99bd6a5 in boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<unsigned int> (this=0xbc86c8) at property_tree/detail/ptree_implementation.hpp:683
#21 0x00002aaab99bc545 in boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get<unsigned int> (this=0x7fffffff9470, path=...) at property_tree/detail/ptree_implementation.hpp:754
#22 0x00002aaab99bba83 in MyRT::DUTConfigFile::readIniFile (this=0xbc5d50, iniFilePath=...) at DUTConfigFile.cpp:231
#23 0x00002aaab99ba8d2 in MyRT::DUTConfigFile::DUTConfigFile (this=0xbc5d50, iniFilePath=..., configFilePath=...) at DUTConfigFile.cpp:26
#24 0x00002aaab99c0839 in setupMyConfigs () at SimXLInterface.cpp:83
#31 0x0000000000408847 in main ()

Я попробовал gdb, и он выдает исключение при преобразовании строки в uint с помощью istringstream. Ниже приведены два типа перехода.

template<class K, class D, class C>                                                                                                                    
template<class Type> inline                                                                                                                            
Type basic_ptree<K, D, C>::get_value() const
{
    return get_value<Type>(
        typename translator_between<data_type, Type>::type());
}

(gdb) p typeid(Type).name()
$2 = 0x2aaab5c33c91 <typeinfo name for unsigned int> "j"
(gdb) p typeid(data_type).name()
could not find typeinfo symbol for 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >'

Функция ниже в basic_ios.h выдает исключение __throw_bad_cast() (здесь this->_M_num_get = NULL)

const __num_get_type& __ng = __check_facet(this->_M_num_get);

Автоматизация внутренней сборки Makefile добавляет -D_GLIBCXX_USE_CXX11_ABI=0 для создания объектного файла по умолчанию

g++ -I/usr/myboost/boost/boost -Wall -std=c++14 -fopenmp -m64 -msse2 -fPIC \
  -D_GLIBCXX_USE_CXX11_ABI=0 -g -o *.o -fPIC -c *.cpp

.so команда построения

g++ -m64 -msse2 -pthread -shared -static-libstdc++ -static-libgcc  \
  -Wl,-znoexecstack -g -o runtime.so *.o -ldl -lrt -lz -fopenmp -lcrypto

Если я изменю его для чтения типа std :: string, а затем конвертирую его в uint, он работает без исключения

std::string strHwCount = pt.get<std::string>("SYSTEM.num_of_vps");
hwCount = static_cast<uint>(std::stoul(strHwCount));

Я использую boost-1.68 и gcc-7.3. Связано ли это с макросом ABI, который я использую? Есть ли способ решить эту проблему без вышеуказанного обходного пути

ОБНОВЛЕНИЕ

Прежде всего я переношу проект с cmake на внутреннюю настраиваемую автоматизацию сборки (некоторая расширенная версия Makefile). В cmake это работало нормально. Основываясь на ответе sehe, я провел дальнейший анализ. В небольшом автономном примере эта ошибка не воспроизводится (с использованием как локальной строки, так и фактического чтения файла .ini).

При прохождении через gdb, в файле boost/property_tree/stream_translator.hpp, я мог найти значение чтения из файла ( изменен на num_of_vps = 4 в файле) как в string, так и в istringstream.

optional<Type> basic_ptree<K, D, C>
    ::get_value_optional(Translator tr) const
{
    return tr.get_value(data());
}

(gdb) p data()
$1 = "4"

boost_1_68_0::optional<E> get_value(const internal_type &v) {
    std::basic_istringstream<Ch, Traits, Alloc> iss(v);
    iss.imbue(m_loc);
    E e;
    customized::extract(iss, e);
    ....
}

(gdb) p v
$1 = "4"
(gdb) p iss.str()
$2 = "4"

Внутри вышеупомянутой функции customized::extract(iss, e) есть преобразование string в unsigned int с использованием basic_istream

static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
    s >> e;
    if(!s.eof()) { s >> std::ws; }
} 

Внутри этого оператора >> есть функция _M_extract, а внутри этой __check_facet функция выдает исключение (путем проверки NULL для this->_M_num_get)

basic_istream<_CharT, _Traits>::
_M_extract(_ValueT& __v) {
  __try {
    const __num_get_type& __ng = __check_facet(this->_M_num_get);
    ...

inline const _Facet&
__check_facet(const _Facet* __f) {
  if (!__f)
    __throw_bad_cast();
  return *__f;
}

Сценарий трассировки стека выше

#0  std::istream::_M_extract<unsigned int>(unsigned int&) (__f=0x0) at gcc-7.3.0/objdir/x86_64-centos-linux/libstdc++-v3/include/bits/basic_ios.h:49
#1  std::istream::operator>>(unsigned int&) (this=0x7fffffff9980, __n=@0x7fffffff997c: 0)
#2  boost_1_68_0::property_tree::customize_stream::extract(std::istream&, unsigned int&) (s=..., e=@0x7fffffff997c: 0)
#3  boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int>::get_value(std::string const&) (this=0x7fffffff9b58, v="4")

Я мог видеть такое же поведение в другом месте, которое мы используем std::stringstream . Переменная ss отображается пустой, даже если оператор << добавляет строки

std::stringstream ss;
ss << std::setw(8) << std::setfill('0') << std::hex << firmId;

У меня было беспокойство по поводу того, что флаг ABI вызывает это, но мне удалось удалить его, и проблема все еще существует . Я искал такие проблемы istream, но не нашел ничего полезного.

Ответы [ 2 ]

3 голосов
/ 28 мая 2020

Ваш ввод поврежден. Скорее всего, он использует кодировку или кодовые точки, которых вы не ожидали, но не выделяются для человеческого читателя.

Live On Coliru

#include <boost/property_tree/ini_parser.hpp>
#include <iostream>

int main() {
    std::istringstream iss(R"([SYSTEM]
num_of_vps = 1)");
    boost::property_tree::ptree pt;
    read_ini(iss, pt);

    uint hwCount = pt.get<uint>("SYSTEM.num_of_vps"); 

    std::cout << "hwCount: " << hwCount << "\n";
}

Печатает

hwCount: 1

Однако, если вы измените ввод, например,

    std::istringstream iss(R"([SYSTEM]
num_of_vps = 1)");

(обратите внимание, что пробел после = теперь < > 160, \240, U+00A0 NO-BREAK SPACE, ^KNS, &nbsp;), он печатает:

Live On Coliru

Печать

terminate called after throwing an instance of 'boost::wrapexcept<boost::property_tree::ptree_bad_data>'
  what():  conversion of data to type "j" failed
0 голосов
/ 08 июня 2020

С настройкой команды g cc я смог запустить исполняемый файл с .so со всеми sstream использованиями. Ранее команда связывания g cc была

g++ -m64 -msse2 -pthread -shared -static-libstdc++ -static-libgcc  \
  -Wl,-znoexecstack -g -o runtime.so *.o -ldl -lrt -lz -fopenmp -lcrypto

Удаление -static-libstdc++ и динамическое связывание libstdc++.so работало

g++ -m64 -msse2 -pthread -shared -static-libgcc -Wl,-znoexecstack -o runtime.so *.o \
  -fPIC -ldl -lrt -lz -fopenmp -lcrypto -Wl,-rpath,/depot/gcc-7.3.0/lib64
...