1

The following code gives a segmentation fault:

#include <iostream>
#include <fstream>
#include "binItr.h"
#include <boost/multi_array.hpp>

using namespace std;

int main(){
   const char * xifile = "results/feretxiG1155V0P5T231K10.bin";

   const uint pSize = 5;
   const uint T = 231;

   ifstream xiFileId(xifile, ios::binary);

   typedef boost::multi_array<uint, 2> array_type;
   array_type xi(boost::extents[T][pSize + 1]);

   //the ii_t class in the following line is taken from http://stackoverflow.com/questions/1855704/c-binary-file-i-o-to-from-containers-other-than-char-using-stl-algorithms written by http://stackoverflow.com/users/14065/loki-astari

   ii_t<uint> xi_in(xiFileId);

   copy(xi_in, ii_t<uint>(), xi.data());
   return 0;
}

The input binary file contains unsigned int data and its size as reported by ls -l is 231*(5+1)4 = 5544 bytes. I tried reading the file and storing the data in a vector and found that vector size is 231(5+1) = 1386. Analysis of the core file using gdb gives the following output.

    Program terminated with signal 6, Aborted.

    #0  0x00007fb71130ea75 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64   ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
   in ../nptl/sysdeps/unix/sysv/linux/raise.c

    (gdb) bt
    #0  0x00007fb71130ea75 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
    #1  0x00007fb7113125c0 in abort () at abort.c:92
    #2  0x00007fb7113484fb in __libc_message (do_abort=<value optimized out>, fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:189
    #3  0x00007fb7113525b6 in malloc_printerr (action=3, str=0x7fb711425cd8 "double free or corruption (!prev)", ptr=<value optimized out>) at malloc.c:6266
    #4  0x00007fb711358e83 in __libc_free (mem=<value optimized out>) at malloc.c:3738
    #5  0x00000000004018c4 in __gnu_cxx::new_allocator<unsigned int>::deallocate (this=0x7fffc618d2f8, __p=0x2295290) at /usr/include/c++/4.4/ext/new_allocator.h:95
    #6  0x000000000040152f in boost::multi_array<unsigned int, 2ul, std::allocator<unsigned int> >::deallocate_space (this=0x7fffc618d290) at /usr/include/boost/multi_array.hpp:484
    #7  0x0000000000401077 in boost::multi_array<unsigned int, 2ul, std::allocator<unsigned int> >::~multi_array (this=0x7fffc618d290, __in_chrg=<value optimized out>) at /usr/include/boost/multi_array.hpp:468
    #8  0x0000000000400d4e in main () at segTest.cpp:30

Any suggestions? Thanks.

AusCBloke
  • 18,014
  • 6
  • 40
  • 44
suresh
  • 1,109
  • 1
  • 8
  • 24
  • Nothing seems wrong as far as I can see, but can you give more information about ii_t? Also, have you tried running it through valgrind? – Vaughn Cato Nov 04 '11 at 04:28
  • I had tried valgrind and it didnt mention any memory leaks. ii_t is from http://stackoverflow.com/questions/1855704/c-binary-file-i-o-to-from-containers-other-than-char-using-stl-algorithms. Finally I solved the problem by not using ii_t :) – suresh Nov 04 '11 at 04:55
  • If you comment out the copy() line, does it still segfault? – fileoffset Nov 04 '11 at 04:57
  • @fileoffset yea, you are right. It was coming from copy - but because of the ii_t class. When I removed it - the seg fault vanished - though it took so much of my time :) – suresh Nov 04 '11 at 04:58
  • OK. Michael seems to have fixed the problem. **BUT** this ii_t class is not designed to read complex classes like `boost::multi_array`. Complex classes have constructors and this iterator writes over the object using read(). This destroys the original content. This class is only designed for use with **POD** types (ie anything that does **not have constructor** and if a structure non of its members have constructors). – Martin York Nov 04 '11 at 15:38
  • As this seems to be working (ie reading) then I think your seirializing code is also wrong. Are you using write() to write an object of type `boost::multi_array`. This is wrong and will not work generically. You need to use the stream operator << to correctly serialize the object and the >> operator to de-serialize the object. – Martin York Nov 04 '11 at 15:44
  • @LokiAstari I didnt understand your last comment. Could you please elaborate? Thanks – suresh Nov 04 '11 at 20:57
  • You can use read/write to serialize complex objects to a file. Doing this does not serialize the complex parts of the structure. – Martin York Nov 04 '11 at 22:33

1 Answers1

2

The problem is that the ii_t<> input iterator class from the referred SO answer is 'reading' one too many items because the wrapped istream doesn't return EOF until the the dereference of the iterator after the one that returned the last item in the file. The extra returned data item is corrupting the allocated memory block in the multi_array object.

If you change the ii_t<> class to the following, you should get better behavior:

template<typename T>
struct ii_t: public iterator<input_iterator_tag, void, void, void, void>
{
  ii_t(std::istream& str)
    :m_str(&str)
  {}
  ii_t()
    :m_str(NULL)
  {}
  ii_t& operator++()   {return *this;}  // increment does nothing.
  ii_t& operator++(int){return *this;}
  T& operator*()
  {
    // On the de-reference we actuall read the data into a local //// static ////
    // Thus we can return a reference
    static T result;
    m_str->read(reinterpret_cast<char*>(&result),sizeof(T));
    return result;
  }
  // If either iterator has a NULL pointer then it is the end() of stream iterator.
  // Input iterators are only equal if they have read past the end of stream.
  bool operator!=(ii_t const& rhs)
  {
      // we need to make sure we're not just about to hit EOF
      // if we already haven't
      if (m_str && m_str->good()) {
        char dummy;
        m_str->read(&dummy,1);
        if (m_str->good()) {
            m_str->putback(dummy);
        }
      }

      if (rhs.m_str && rhs.m_str->good()) {
        char dummy;
        rhs.m_str->read(&dummy,1);
        if (rhs.m_str->good()) {
            rhs.m_str->putback(dummy);
        }
      }

      bool lhsPastEnd  = (m_str == NULL)     || (!m_str->good());
      bool rhsPastEnd  = (rhs.m_str == NULL) || (!rhs.m_str->good());

      return !(lhsPastEnd && rhsPastEnd);
  } 

  private:
    std::istream*   m_str;
};

The relevant changes are in the bool operator!=(ii_t const& rhs) function, where, if necessary, a dummy read is performed (then undone) on the wrapped istream to determine if the istream is at the EOF.

Note that I'm making no claim that this is the best technique to handle the EOF situation, but it seems to work.

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Thank you so much. Verified that the segmentation violation is not present now. – suresh Nov 04 '11 at 06:54
  • OK. I see the problem. The std::copy is going to check the state of the stream after the ++ operation. But because this does nothing it will always be true. Thus when you the de-reference it (and it fails) it has no way to know that it has gone wrong and therefore the object used is now a copy of the last object. – Martin York Nov 04 '11 at 15:46
  • In this case we now have two array object that are using the same internal buffer. This leaves the user code open to double delete and other problems associated with a shared buffer. So as noted above on the OP question ii_t is not designed to work with non POD types. (NOTE: this does not change the fact the Michael has fixed the code for the POD type). – Martin York Nov 04 '11 at 15:47
  • @Loki: I don't think the problem is a user error - it's that after the last item is read from the file, EOF hasn't been set, so the iterator doesn't compare equal to the 'empty' iterator. That indicates that another dereferece is OK - but the read for that deref will return EOF instead of data (so the `static T result` is left unchanged). I don't think what I've added to `ii_t` is the best way to handle this, but it seemed easiest without completely changing `ii_t`. – Michael Burr Nov 04 '11 at 15:57
  • @Michael Burr: You are correct. I just needed some time to analyses the usage (and have deleted that comment). Your fix is good. ;-( – Martin York Nov 04 '11 at 18:18