This code is taken from IncludeOS github page. I modified it a bit so that it compiles without other header files. find
function from IncludeOS is a bit too verbose, so I want to simplify it. But after modification, the code behaves differently from what I expected.
Here is a short explanation. This code is used to parse HTTP headers. Header fields are name-value pairs. It's represented as vector<pair<string, string>>
. find
function is used to find the location of a field name in the header, and has_field
is used to check whether a specific field name exists in the header.
In main
function, four elements are appended to fields. six
shouldn't be found in fields.But has_field
returns true.
I tried to track the error with gdb
. But I was lost in the sea of outputs. I did find a somewhat interesting message.
std::__uninitialized_copy<false>::__uninit_copy<__gnu_cxx::__normal_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const*, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char<, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char<, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >>>>, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >*> (__first={first = "one", second = "1"}, __last=
{first = <error reading variable: Cannot create a lazy string with address 0x0, and a non-zero length.>, second = ""}, __result=0x61bf00)
I used clang
sanitizer to find out what's wrong. Only memory sanitizer shows interesting reports. Running,
clang++ -std=c++17 -O1 -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer main.cc
/a.out
reports,
Uninitialized value was created by an allocation of 'ref.tmp' in the stack frame of function '_ZNSt4pairINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES5_EC2IRA6_KcRA2_S8_Lb1EEEOT_OT0_'`.
When optimization level is set to -O3
, however, nothing shows up.
#include <algorithm>
#include <iostream>
#include <vector>
#include <experimental/string_view>
using Headers = std::vector<std::pair<std::string, std::string>>;
using string_view = std::experimental::string_view;
Headers::const_iterator find(Headers fields, const string_view field) {
if (field.empty()) return fields.cend();
//-----------------------------------
return
std::find_if(fields.cbegin(), fields.cend(), [field](const auto _) {
return std::equal(_.first.cbegin(), _.first.cend(), field.cbegin(), field.cend(),
[](const auto a, const auto b) { return std::tolower(a) == std::tolower(b); });
});
}
bool has_field(Headers fields, const string_view field)
{
return find(fields, field) != fields.cend();
}
int main()
{
Headers fields;
fields.emplace_back("one", "1");
fields.emplace_back("two", "2");
fields.emplace_back("three", "3");
fields.emplace_back("four", "4");
std::string s = "six";
if (has_field(fields, s))
std::cout << s << " is in " << "fields" << std::endl;
return 0;
}
fields
vector by value and thushas_field
compares iterators into different copies of the vector. – Bo Persson