Tutorial :Unexplained out_of_range in string::substr



Question:

I have been getting a really annoying error about an std::out_of_range when calling substr. The exact error is

terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr

I'm absolutely sure that tmp_request has a length greater then 1. No matter what I pass to substrâ€"1, 2, or bodyposâ€"it always throws that error. I'm using g++ on Unix.

Only interesting thing I can include is the string has multiple "\r\n", including one "\r\n\r\n".

In one cpp file:

std::string tmp_request, outRequest;    tmp_request = SS_Twitter->readData();  outRequest = SS_Twitter->parse(tmp_request);  

In another:

 std::string parse(const std::string &request)   {    std::map<std::string,std::string> keyval;    std::string outRequest;    if(request[0]=='P')    {     if(request.find("register")!=std::string::npos)     { //we have a register request      size_t bodypos = request.find("username");      if(bodypos==std::string::npos)       {       HttpError(400,"Malformed HTTP POST request. Could not find key username.",request);       }      else      {       std::string body = request.substr(bodypos);       StringExplode(body,"&", "=",keyval);       outRequest = "doing stuff";      }       }  

Update:

std::string request2("P\r\nregister\r\nusername=hello\r\n\r\n");    std::string body = request2.substr(4);  

That throws the same error. Now I know this is perfectly valid and correct code, but it's still throwing the error. //removed source link


Solution:1

I modified your sample slightly to decrease amount of indentation used.
There are 5 "test cases" and none causes any problem. Could you please provide a sample request to reproduce the problem you're having.

EDIT: Forgot to mention: if this sample as it is (with commented-out bits) doesn't produce that error, your best bet is that you have a bug in your StringExplode function. You could post its source, to get a more helpful advice.

EDIT2: In your StringExplode, change results[tmpKey] = tmpKey.substr(found+1); to results[tmpKey] = tmpResult[i].substr(found+1);. Change int found to size_t found, and remove all of if (found > 0), that will fix your mysterious out_of_range. You were substr-ing a wrong string. Just in case, here's the code with a fix:

void StringExplode(std::string str, std::string objseparator, std::string keyseperator,                     std::map <std::string, std::string> &results)  {      size_t found;      std::vector<std::string> tmpResult;      found = str.find_first_of(objseparator);      while(found != std::string::npos)      {          tmpResult.push_back(str.substr(0,found));          str = str.substr(found+1);          found = str.find_first_of(objseparator);      }      if(str.length() > 0)      {          tmpResult.push_back(str);      }        for(size_t i = 0; i < tmpResult.size(); i++)      {          found = tmpResult[i].find_first_of(keyseperator);          while(found != std::string::npos)          {                  std::string tmpKey = tmpResult[i].substr(0, found);                  results[tmpKey] = tmpResult[i].substr(found+1);                  found = tmpResult[i].find_first_of(keyseperator, found + results[tmpKey].size());          }        }  }  

Initial test code:

#include <iostream>  #include <map>  #include <string>    std::string parse(const std::string &request)  {      std::map<std::string,std::string> keyval;      std::string outRequest;        if(request[0] != 'P')          return outRequest;        if(request.find("register") == std::string::npos)          return outRequest;        //we have a register request      size_t bodypos = request.find("username");      if(bodypos==std::string::npos)      {          // HttpError(400,"Malformed HTTP POST request. Could not find key username.",request);          // you said HttpError returns, so here's a return          return outRequest;      }        std::string body = request.substr(bodypos);      // StringExplode(body,"&", "=",keyval);      outRequest = "doing stuff";        return outRequest;  }    int main()  {        std::string request("P\r\nregister\r\nusername=hello\r\n\r\n");      std::cout << "[" << parse(request) << "]\n";        request = "Pregisternusername=hello\r\n\r\n";      std::cout << "[" << parse(request) << "]\n";        request = "Pregisternusername=hello";      std::cout << "[" << parse(request) << "]\n";        request = "registernusername=hello";      std::cout << "[" << parse(request) << "]\n";        request = "";      std::cout << "[" << parse(request) << "]\n";        return 0;  }  

This outputs, predictably:

[doing stuff]
[doing stuff]
[doing stuff]
[]
[]


Solution:2

Are you sure that it's failing on that substr and not on a substr call within the HttpError or StringExplode functions? If you haven't already, you should run this through a debugger so that you can see exactly where it's throwing the exception. Alternatively, you could add a:

std::cout << "calling substr" << std::endl;  

line immediately before you call substr, and a similar line immediately afterwards, so that it would look like:

std::cout << "calling substr" << std::endl;  std::string body = request.substr(bodypos);  std::cout << "finished calling substr" << std::endl;    StringExplode(body,"&", "=",keyval);  outRequest = "doing stuff";  

If that substr really is throwing the exception, then you'll know because the program will print "calling substr" without a matching "finished calling substr". If it prints the pair of debug messages, though, or none at all, then something else is throwing the exception.


Solution:3

One fairly obvious thing wrong with your code:

int k = read(ns, buf, sizeof(buf)-1);  buf[k] = '\0';  

You are not checking that read() succeeded - it returns -1 on failure which will cause all sorts of memory corruption problems if it occurs.

Also:

char * buf2 = const_cast<char *>(reply.c_str());  write(ns,buf2,sizeof(buf2));  

You are taking the size of the pointer - you want the length of the output string:

write(ns, buf2, reply.size() );  

And you should once again test that write succeeded and that it wrote as many bytes as you requested, though this shouldn't directly cause the substr() error.


Solution:4

Looks like you need an else after

if(bodypos==std::string::npos)  {      HttpError(...);  }  

otherwise you are calling substr with bodypos = npos


Solution:5

You might consider using the (unsigned) type std::string::size_type instead of int.

Why are you casting the result of find to an int here: int(request.find("register"))!=std::string::npos


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »