Take the tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm experiencing a strange problem in one of my projects. My code base depends on an external library, which contains a class named Dataset. The Dataset class privately inherits from std::vector<Sample> (where Sample is another custom class defined in the library).

Moreover, such a class exposes a Save member function in order to serialize the data composing the data set into a text file. The Save member function is defined as follows:

inline void Dataset::Save(string filename, ModalityType modality)
{
    ofstream log_file;
    if (modality == OVERWRITE) {
        log_file.open(filename.c_str());
    } else {
        log_file.open(filename.c_str(), ios::out | ios::app);
    }
    if (log_file.is_open()) {
        log_file << *(this);
    }
    log_file.close();
}

ofstream& operator<< (ofstream& out, Dataset& ds)
{
    unsigned int size = ds.size();
    unsigned int input_size = ds.GetInputSize();
    unsigned int output_size = ds.GetOutputSize();
    out << input_size << " " << output_size << endl;
    for (unsigned int i = 0; i < size; i++) {
        Sample* s = ds[i];
        for (unsigned int j = 0; j < input_size; j++) {
            out << s->GetInput(j) << " ";
        }
        for (unsigned int j = 0; j < output_size; j++) {
            out << s->GetOutput(j) << " ";
        }
        out << endl;
    }

    return out;
}

Both my code and the external library have been compiled under OS X 10.8.2 with clang 4.2 and flags -std=c++11 -stdlib=libc++. I need to do so, since my code base uses several C++11 facilities (e.g., random). Moreover, the library itself depends on boost, which in turn has been compiled with clang and C++11 support.

Everything compiles and works as expected by using the following Makefile:

CXX = clang++
CXXDIALECT = -std=c++11 -stdlib=libc++
DEFS = -DBOOST_NO_CXX11_NUMERIC_LIMITS
INCLUDE_DIRS = -I. -I/usr/local/include -I/usr/include -I/opt/local/include
LIB_DIRS = -L/usr/local/lib -L/opt/local/lib 
LIBS = -lfitted -lgsl -lgslcblas -lboost_thread -lboost_program_options -lboost_regex -lboost_system
CINCLUDE = $(INCLUDE_DIRS)
CXXFLAGS = -Os $(CXXDIALECT) $(CINCLUDE) $(DEFS)

tests := main.cpp
sources := $(filter-out $(tests), $(wildcard *.cpp))
objects :=  $(patsubst %.cpp,%.o,$(sources))

main: $(objects)
    $(CXX) $(CXXFLAGS) -o $@ [email protected] $(objects) $(LIBS) $(LIB_DIRS) -v

%.o : %.cpp
    $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@

where libfitted is the name of the external library.

Nevertheless, I'm developing my code under XCode 4.6.2. The problem is that everytime I try run/debug the code in XCode, the Dataset.save member function triggers the following error:

EXC_BAD_ACCESS

and an empty dataset.txt file is created on disk. Here's a couple of screenshots of the stack trace:

stack trace1 stack trace2

Click here and here to view them in full size.

As reported in the screenshots, the problem seems to be in the ofstream.flush() member function. Finally, I report the output of both make and xcodebuild

I really can't figure out why the same code, with the same compiler and libraries, executes correctly if compiled with the afore-mentioned Makefile, while it is not working if executed in XCode.

UPDATE #1: I just noticed that the code is debuggable in XCode (although not runnable) if I use as debugger GDB instead of LLDB, i.e., Dataset.save does not trigger the EXC_BAD_ACCESS error.

UPDATE #2: I recompiled the library with the -g -O0 flags so as to preserve debug symbols. The problem is that everytime a ofstream object is initialized in a Dataset's member function, the this pointer of the Dataset instance becomes NULL, i.e., the Dataset object is nullified. Consequently, each attempt to access any data member within the member function results in a EXC_BAD_ACCESS. This is one of the weirdest thing I've ever seen. Any idea on why this is happening?

Thanks.

share|improve this question
 
When code that runs when compiled in one compiler crashes when compiled with another, it's usually due to some undefined behavior. If you enable more compiler warnings, do you get any compiler warnings? They are usually a good indicator of (some) undefined behavior. –  Joachim Pileborg Jun 2 '13 at 11:12
 
It could also be that you have two versions of some source file, one that is used in the makefile, and another in the Xcode project, and that file contains some error in one of the versions. –  Joachim Pileborg Jun 2 '13 at 11:13
 
Thank you for your comment. The Makefile uses the same compiler (clang) and user-defined flags (-std=c++11 -stdlib=libc++) as XCode. Moreover, the source files are the same (I simply run the Makefile in the folder where the source files are stored). Regarding the warnings, in XCode many warning flags are active by default, and indeed I obtain several warnings. Nevertheless, all warning are about the boost library, rather than the classes composing the libfitted library per se. For the sake of completeness, I will add the output of both make and xcodebuild –  burton0 Jun 2 '13 at 11:23
 
I note that your Makefile version uses default (not hidden) visibility and that your Xcode build is using hidden visibility. I don't know that this difference is the problem, but it is the next thing I recommend investigating. In your Xcode project, try setting both -fvisibility=default and turning off -fvisibility-inlines-hidden. –  Howard Hinnant Jun 2 '13 at 13:24
 
Thank you for your comment. I modified both flags accordingly to your suggestion. Unfortunately, it still does not work. I will add a second screenshot of the stack trace so as to highlight that most probably the error occurs in the ofstream.flush() member function –  burton0 Jun 2 '13 at 13:40
show 7 more comments

1 Answer

The std::vector<...> class is not designed to be used as a base class. Using it as such leads to undefined behaviour (because of the lack of a virtual destructor, at least).

Item 35 in C++ Coding Standards (Sutter, Alexandrescu), named

Avoid inheriting from classes that were not designed to be base classes.

might well be of help here.

Item 7 of Effective C++ (Meyers) discuss the issue with this example:

class SpecialString: public std::string{
...
}

At first glance, this may look innocuous, but if anywhere in an application you somehow convert a pointer-to-SpecialString into a pointer-to-string and you then use delete on the string pointer, you are instantly transported to the realm of undefined behavior.

share|improve this answer
1  
Thank you for your answer. You are right when you say that inheriting from std::vector should be avoided. However, I'm privately inheriting from std::vector, meaning that I'm modeling a "implemented-in-terms-of" relationship, rather than a "is-a" relationship. –  burton0 Jul 4 '13 at 12:37
 
Yes, I noticed. The private inheritance solves the "design" problem of using an "is-a" relationship when it is not needed. On the other hand, because std::vector defines a non-virtual destructor, there is a technical problem which leads to undefined behaviour whenever you derive from it. –  Stefano Falasca Jul 4 '13 at 12:44
 
AFAIK, privately inheriting from std::vector solves also the technical problem, since private inheritance is the same as containment. –  burton0 Jul 4 '13 at 12:58
 
I'm not aware of anything like that. Can you point me out to some reference? It would be very interesting to know, to me at least. –  Stefano Falasca Jul 4 '13 at 13:09
1  
It may be better to rephrase my last statement as follows: private inheritance acts as composition. Only Dataset member functions can bind a Dataset object to a std::vector pointer. From the outside it is not possible to obtain a pointer to std::vector starting from a pointer of type Dataset. When the Dataset object is destroyed, the Dataset destructor will be called first, followed by the std::vector destructor. Here's a comparison between the two approaches: parashift.com/c++-faq/priv-inherit-like-compos.html –  burton0 Jul 4 '13 at 13:21
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.