2016-03-26 2 views
1

Этот фрагмент кода должен анализировать запросы, извлекать путь URI, затем извлекать каждый отдельный заголовок и его значение, а затем передавать все эти извлеченные переменные в std::vector <const char*> cva, которые в конечном итоге будут переданы в std::vector<std::vector<const char*>> nv (config.nv.push_back(std::move(cva));)C++ векторы, const char *, scope scope и lifetime

у меня есть несколько проблем здесь:

  • кажется, что синтаксический анализ правильно происходит, но, как я храню/передавая содержание извлеченной переменной не работает должным образом или из-за неправильного понимания как хранятся переменные ieved и их область действия в C++ (скорее всего, с тех пор, как я едва начал изучать C++), или какой-то большой проблемой, о которой я не знаю.
  • Я добавил несколько операторов print, которые показывают, что мой синтаксический анализ сделан правильно.
  • Однако петли for (в конце) имеют разный выход, чем ожидалось.

я

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

namespace { 
std::vector<std::string> explode(const std::string& str, const char& ch) { 
std::string next; 
std::vector<std::string> result; 

// For each character in the string 
for (std::string::const_iterator it = str.begin(); it != str.end(); it++) { 
    // If we've hit the terminal character 
    if (*it == ch) { 
     // If we have some characters accumulated 
     if (!next.empty()) { 
      // Add them to the result vector 
      result.push_back(next); 
      next.clear(); 
     } 
    } else { 
     // Accumulate the next character into the sequence 
     next += *it; 
    } 
} 
if (!next.empty()) 
    result.push_back(next); 
return result; 
} 
} 

int main() { 
// this is an example of how my reqlines looks like 
    std::vector<std::string> reqlines; 
    reqlines.push_back("https://endpoint/test1"); 
    reqlines.push_back("https://endpoint/test2\theader1:1234\tcookie:abcd"); 
    reqlines.push_back("https://endpoint/test3\theader1:5678"); 
    reqlines.push_back("https://endpoint/test4"); 
    reqlines.push_back("https://endpoint/test5"); 

    std::vector<std::string> paths; 
    std::vector<std::string> extraheaders; 
    std::vector<std::vector<std::string> > tokenized; 
    int count = 0; // keeps track of each request number so I can access its corresponding extra headers from extraheaders vector 
    bool cond = true; 

    if (cond){ 
    // creating which will be used to store my requests paths as well as extra headers 
    // This has to be a const char * vector since it will be used by an external library which requires such type 
    std::vector<const char *> cva; 

    for (auto &req : reqlines){ 
      unsigned int pos = req.find_first_of("\t", 0); 
      if (pos == -1){ 
        paths.push_back(req); 
        extraheaders.push_back(" "); 
      } else { 
        paths.push_back(req.substr(0, pos)); 
        extraheaders.push_back(req.substr(pos+1, std::string::npos)); 
      } 
    } 

    for (auto &path : paths){ 
      cva.push_back(":path"); 
      cva.push_back(path.c_str()); // adding the URI path into cva variable 

      // explode function which returns a std::vector<std::string> when passing an std::string to it 
      tokenized.push_back(explode(extraheaders[count], '\t')); // extracting the vector<std::string> of all extra headers 

    //  if (tokenized[count][0].compare(" ") == 0){ 
    //    printf(" %d element is empty is skipped \n"); 
    //  }else { 

      for (auto &tok : tokenized[count]){ // looping through extra headers of request number "count", parsing header name/value and adding it to cva 
       printf(" %d tok %s\n", __LINE__, tok.c_str()); 
       printf(" %d tok address %d\n", __LINE__, &tok); 
       unsigned int pos = tok.find_first_of(":", 0); 
       if (pos == -1) 
        printf(" %d there are no headers \n", __LINE__); 
       else { 
        printf("header name: %s\n", (tok.substr(0, pos)).c_str()); 
        printf("header value: %s\n", (tok.substr(pos+1, std::string::npos)).c_str()); 
        cva.push_back((tok.substr(0, pos)).c_str()); 
        cva.push_back((tok.substr(pos+1, std::string::npos)).c_str()); 
       } 
      } 
      cva.push_back(":version"); // adding version header 
      cva.push_back("HTTP/1.1"); // adding version header number 
      cva.push_back(nullptr); // adding nullptr (which is how nv is expecting cva to be terminated) 
      count++; 

      // passing the cva content to nv 
      //config.nv.push_back(std::move(cva)); 
    } 

    // Below are the printing statement to check the values of the different variables I created and populated 
    // above, my problem is that the population process puts some values into my variables however printing 
    // the content of the those variables shows different values from what I am expecting 

      std::cout << "   " << std::endl << std::endl; 
      std::cout << "Printing cva" << std::endl; 
      for (auto &elem : cva){ 
        if (elem == nullptr) 
          std::cout << static_cast<void*>(nullptr) << std::endl; 
        else 
          std::cout << &elem << " " <<elem << std::endl; 
      } 
      std::cout << "   " << std::endl << std::endl; 
      std::cout << "Printing paths" << std::endl; 
      for (auto &path : paths){ 
        std::cout << &path << " " << path << std::endl; 
      } 
      std::cout << "   " << std::endl << std::endl; 
      std::cout << "Printing headers" << std::endl; 
      for (auto &hed : extraheaders) { 
        std::cout << &hed << " "<<hed << std::endl; 
      } 
    } 
} 

Ожидаемый результат должен быть:

Printing cva 
:path 
endpoint:port/test1 
:version 
HTTP/1.1 
0 
:path 
endpoint:port/test2 
:header1 
1234 
:cookie 
abcd 
:version 
HTTP/1.1 
0 
:path 
endpoint:port/test3 
:header1 
5678 
:version 
HTTP/1.1 
0 
:path 
endpoint:port/test4 
:version 
HTTP/1.1 
0 
:path 
endpoint:port/test5 
:version 
HTTP/1.1 
0 

Фактический выход:

Printing cva 
    :path 
    endpoint:port/test1 
    :version 
    HTTP/1.1 
    0 
    :path 
    endpoint:port/test2 
    header1 // for test2 request, header1 value is 1234 
    5678 
    header1 // header 1 should not be printed twice 
    5678 
      // Missing cookie header and value 
    :version 
    HTTP/1.1 
    0 
    :path 
    endpoint:port/test3 
    header1 
    5678 
    :version 
    HTTP/1.1 
    0 
    :path 
    endpoint:port/test4 
    :version 
    HTTP/1.1 
    0 
    :path 
    endpoint:port/test5 
    :version 
    HTTP/1.1 
    0 

ответ

2
   cva.push_back((tok.substr(0, pos)).c_str()); 
       cva.push_back((tok.substr(pos+1, std::string::npos)).c_str()); 

Эта часть не определена.

cva - это вектор const char *, нативные указатели. Вы вызываете объект временного объекта. Этот временный объект std::string немедленно выходит из области действия и уничтожается в конце выражения, после чего const char * к его внутреннему содержимому более недействителен. Этот указатель на внутреннее содержимое уничтоженного объекта добавляется к вектору cva, а затем делается попытка его распечатать, следовательно, неопределенное поведение.

+0

Спасибо за разъяснение. Зная, что cva vactor 'const char *' используется другой внешней библиотекой, и я не могу ее изменить. Каков наилучший подход к обходу этого неопределенного поведения. то есть я должен скопировать содержимое '(tok.substr (0, pos)). c_str()' и '(tok.substr (pos + 1, std :: string :: npos)). c_str()' в другой переменные, а затем передать эти переменные в 'cva.push_back()'. или есть лучший, более элегантный способ справиться с этим вариантом использования? –

+0

Да, вам нужно скопировать фактическую строку в отдельный вектор, и ** после ** все строки скопированы, постройте отдельный вектор 'const char *'. –

+0

Это сработало! Благодарю. Поскольку я изучаю C++, это нормально, если я опубликую решение, которое я выяснил, чтобы получить некоторые отзывы об этом и какие лучшие практики? Есть, конечно, некоторые вещи, которые сделают код более элегантным и более эффективным, и я хотел бы получить такую ​​обратную связь от экспертов C++! –