-2

I've searched endlessly on SE for a logical explanation for why this is happening. It is probably something very simple that I've overlooked, however I cannot spot it and would really appreciate some assistance with this.

Last week I implemented a class to read the output of a system call from a .ini file and then find and store the required information into custom objects that are then stored in a vector inside a Config class. It is a Singleton config class storing a unique_ptr for each instance of my custom class that is created.

The thing is, when I implemented this last week on my laptop, I had zero issues reading and writing to my member vector and was able to get it working exactly how I needed it. Since pulling to my desktop computer, this vector, and any STL container that I use as a member of my class, throws a segmentation fault when I try to do anything on it, even get it's size.

I've tried to shorten the code below to only include sections that actually use this vector. I have replaced my config with A, and custom class with T, and no matter where I try to use my member container, or any other test STL containers that I add to the class, I get a segfault.

For the record, I am using Qt with C++11.

Update: This example breaks on line 50 of c.cpp when debugging, and anywhere that tries to call the vector.

Debug points to this line in stl_vector.h

  // [23.2.4.2] capacity
      /**  Returns the number of elements in the %vector.  */
      size_type
      size() const _GLIBCXX_NOEXCEPT
      /*-> this line */ { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }

main.cpp

#include "c.h"

int main(int argc, char *argv[])
{

    C *c = C::getInstance();

    delete c;

    return 0;
}

t.h - Class stores information from file

#include <string>

class T
{
public:
    T();

    bool Active();

    std::string getA();
    void setA(std::string);

private:

    std::string a;
};

t.cpp

#include "t.h"

T::T()
{

}

bool T::Active()
{
    if(a == "")
    {
        return false;
    }
    return true;
}

std::string T::getA()
{
    return this->a;
}

void T::setA(std::string newa)
{
    this->a = newa;
}

c.h - Class stores T objects and parses file for information

#include "t.h"
#include <QDebug>
#include <vector>
#include <algorithm>
#include <iostream>
#include <memory>
#include <sstream>
#include <fstream>

class C
{
public:

    static C* getInstance();

private:
    C();

    static C* instance;
    static bool init;

    std::vector<std::unique_ptr<T>> t_list;
    void readLines(const std::string&);
};

c.cpp

#include "c.h"

bool C::init = false;
C* C::instance = nullptr;

C::C()
{
    system("echo this is a test command > a.ini");
    instance->readLines("a.ini");
}

C* C::getInstance()
{
    if(!init)
    {
        instance = new C;
        init = true;
    }
    return instance;
}

void C::readLines(const std::string &path)
{
    T* new_t;
    std::ifstream file(path.c_str());

    if(!file.is_open())
    {
        qDebug() << "Unable to open " << path.c_str();
    }

    std::ofstream o("test.txt");
    std::string line;
    while(std::getline(file, line))
    {
        // Split string before searching
        std::stringstream ss(line);
        std::string seg;
        std::vector<std::string> split;
        std::string left, right;

        // Search patterns
        size_t find_a = line.find("a");

        size_t del = line.find(':');

        if(find_a != std::string::npos)
        {

            o << "test_Size: " << t_list.size() << std::endl;


            if(new_t->Active())
            {
                T* temp = new_t;
                std::unique_ptr<T> move_t(temp);
                t_list.push_back(std::move(move_t));
            }

            o << "test: " << t_list.size() << std::endl;

            std::string n;

            // Check if previous ahas any null elements

            // Split string to find a
            n = line.substr(line.find("a "));
            n = n.substr(n.find(" ", +2));

            new_t->setA(n);

        }

        else
        {
            continue;
        }
    }

    // Add last a
    T* t = new_t;
    std::unique_ptr<T> move_t(t);
    //t_list.push_back(std::move(move_t));

    o << "a: " << t_list.back().get()->getA() << std::endl;
    o << t_list.size() << std::endl;

    o.close();
    file.close();

}
  • 1
    "I've tried to shorten the code below to only include sections that actually use this vector." -- this accomplishes nothing useful. Just because your code crashes while it does something with a vector doesn't mean that there's a bug with how the code uses a vector. C++ does not work this way. The bug can be anywhere in your program, which is why a [mcve], as explained in stackoverflow.com's [help], is required in order for anyone to be able to reproduce your problem. Unless someone can copy exactly the shown code in your [mcve], and observe the same bug, it's unlikely that anyone can help you. – Sam Varshavchik May 23 '18 at 23:18
  • What's an `Adapter`? What's `T`? – Tas May 23 '18 at 23:19
  • @SamVarshavchik I removed redundant code that repeats the logic in the readLines function to find "a", it is minimal and complete since getInstance is the only point of entry from my main function. If you want me to include the class T then I will, however it makes literally no appearance up to the point at which this error occurs, if I try to access the vector in the constructor of T it still happens. –  May 23 '18 at 23:27
  • @Tas I've edited it now since that was an accident, T is the class that contains the information read in from the .ini file and is then stored in a unique_ptr and moved to the vector. –  May 23 '18 at 23:27
  • The nature of undefined behaviour is that code which causes a crash is not necessarily the code where a crash occurs. For example, one section of code might do an invalid operation with a pointer (undefined behaviour). The actual consequence of that is overwriting some area of memory that is used by completely unrelated code. Some time later, that unrelated code is executed, and the overwritten memory is used in a calculation, and then results in a crash. That's why it is necessary to provide a [mcve]. Just presenting the code where the crash occurs does not help find the cause. – Peter May 23 '18 at 23:46
  • @Peter I've now changed it. The code I originally put up excluded the T class that the vector was storing, but it is irrelevant in regards to the error itself. The error is caused by the vector and the C object (not clang) that is accessing it, everything that happens here is happening inside the C class, the getInstance() method is called from main, which triggers this logic. Providing the full implementation which I've now done won't affect the error, it will however allow you to debug it if you wish to help me and so I've included it now, thanks in advance if you do :) –  May 24 '18 at 01:08
  • Compiler say "warning: 'new_t' may be used uninitialized in this function" and highlights: `if(new_t->Active())`. Never ignore compiler warnings until after you have justified them. They are your first line of defence against trivial (and sometimes not so trivial) logic errors. Anyway, you are still putting garbage in the `vector`. – user4581301 May 24 '18 at 01:26
  • style note: the `_t` suffix is often used to denote a datatype, so using this for the name of anything else may result in confusion among readers. – user4581301 May 24 '18 at 01:30
  • 1
    I think wintermute's answer is relevant. Also, no need to use `std::move()` for the argument of `push_back()`.. – Peter May 24 '18 at 02:11
  • @user4581301 Thanks, noted. Even after adding the initialisation it doesn't stop the error. It is any action that calls the vector which is causing the exception, none this is even before new_t gets added. –  May 24 '18 at 02:31
  • @Peter I modified my code according to his comment, however the issue has nothing to do with the data types being stored, the problem arises when I try to access a member variable from within the class, in this case it is a vector. There is no other class or method trying to access this vector, removing mentions of the vector fixes the issue, using local variables fixes the issue. I cannot access members without a segfault and that is what I need to be doing. –  May 24 '18 at 02:39
  • Please update the code with how you are initializing `new_t` and any other modifications you made. There is still plenty of room for crouching tigers and hidden dragons. There are almost countless ways pointers can get messed up, smart or not. Wintermute's suggestion of ditching them entirely is highly recommended. – user4581301 May 24 '18 at 03:02
  • By the way, it looks like `C` is a lazy-loaded singleton. The first answer to [C++ Singleton design pattern](https://stackoverflow.com/questions/1008019/c-singleton-design-pattern) gives a rundown on the Meyers Singleton, a much easier and safer (even thread-safe in C++11 or later) way to write one. Note zero pointers are involved. – user4581301 May 24 '18 at 03:09
  • `new_t` is obviously being used before it is initialized. Kaboom! There's your bug. – Sam Varshavchik May 24 '18 at 11:59
  • @SamVarshavchik That's not the cause of this bug, I've said this already: The issue has nothing to do with the data types being stored. Remove any instance of an object being added and simply call the vector from anywhere, including .size() inside the constructor and it segfaults. I have said this a few times and yet everyone is still focusing on the way I'm using my T class. –  May 25 '18 at 07:23
  • The only thing anyone can comment on is the code that's shown. As shown, this is dereferencing an initialized pointer, undefined behavior, and a bug. Nobody knows, except you, what other changes were made with the alleged crash still allegedly still occurring, and because of that nothing further can be said about it. – Sam Varshavchik May 25 '18 at 11:50
  • @SamVarshavchik Well you're obviously not one of those trying to help and hence didn't bother debugging it, since you'd have noticed the problem lies in any call to the vector, even before I try to add new_t to it, it is written into the example too so you don't really have an excuse there. Also it turns out that you were wrong anyway, something further was said about it and I managed to fix the problem without even touching new_t, but thanks for offering no help and simply criticising the way I wrote my post, it's good to know that some people are here to not help others. –  May 25 '18 at 23:43

2 Answers2

2

UPDATE after code change:

I see two things now: One is that new_t in C::readlines is never initialized, so this could break when new_t->Active() is called a bit later in the function. However, I believe that the main problem you're running into is in C::C(), where it says

instance->readLines("a.ini");

At this point in the execution, C::instance is not yet initialized -- you're only just constructing the object that would later be assigned to it. Because of this, this in the readlines call is invalid, and any attempt to access object members will cause UB. This latter problem can be fixed by just calling

readLines("a.ini");

in which case the currently constructed object (that will later be instance) is used for this. I have no idea what you want to happen for the first, though, so all I can say is: If you want to have a vector<unique_ptr<T>>, you will have to create objects of type T with either new T() or (arguably preferrably) std::make_unique<T>() and put them in there.

I'll also say that this is a rather ugly way to implement a singleton in C++. I mean, singletons are never really pretty, but if you're going to do it in C++, the usual way is something like the accepted answer of C++ Singleton design pattern .

Old answer:

The problem (if it is the only one, which I cannot verify because you didn't provide an MCVE) is in the lines

T move_t = new_T;
std::unique_ptr<Adapter> ptr_t(&move_t); // <-- particularly this one

m_ts.push_back(std::move(ptr_t));

You're passing a pointer to a local object into a std::unique_ptr, but the whole purpose of std::unique_ptr is to handle objects allocated with new to avoid memory leaks. Not only will the pointer you pass into it be invalid once the scope surrounding this declaration is left, even if that weren't the case the unique_ptr would attempt to delete an object that's not on the heap at the end of its lifecycle. Both problems cause undefined behavior.

To me, it looks as though you really want to use a std::vector<T> instead of std::vector<std::unique_ptr<T>>, but that's a design issue you'll have to answer yourself.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • @MattIan Once you cause undefined behavior, anything can happen. – Barmar May 23 '18 at 23:32
  • @Wintermute I have tried refactoring to store the objects themselves, and also pointers to the objects, I have also tried a test vector and then adding numbers in both the initializer list and the constructor, they all cause a segfault. I believe it is something above specifically using the vector itself but I don't know what. –  May 23 '18 at 23:41
  • It is possible that there are other bugs in the code, but I have no way of knowing that. My advice is to run the code in a debugger to see where it breaks and in what state the objects involved are. It can also help to run the code in valgrind or to pass various `-fsanitize=stuff` or `-fstack-protector` options to the compiler. In order for me to help you here, however, you'll have to trim the code down to a minimal example that I can just copy&paste out of my browser and throw into a compiler and debugger. It's entirely possible that in doing so you'll find the error yourself, too. – Wintermute May 23 '18 at 23:53
  • @Wintermute You should be able to copy and paste it now, it gets the same error and points to the same line in st_vector.h as before. –  May 24 '18 at 00:55
  • @Wintermute I was going to write an answer to my own question last night, but I didn't get a chance to do so. For the record, I did try not using this->instance and it still threw the segfault. I managed to fix it by writing a public function read() and calling it from a separate class, since for this implementation that is where this object will be kept anyway. –  May 25 '18 at 07:34
  • Well, if you only fixed one of the errors it's not a surprise that it'd still throw a segfault, although I'd expect the segfault to be thrown in a different place (when accessing objects in the vector rather than just the vector). It is the nature of undefined behavior that its results are sometimes difficult to predict -- broken code may even sometimes appear to work until you change something completely unrelated. Frankly, I don't see why you're so set on ignoring these issues. – Wintermute May 25 '18 at 08:12
  • @Wintermute It's not throwing a segfault anymore, and have not had any issues since. This example does not include the checks I have in place because they are completely irrelevant to the problem I was having, considering the example throws the fault before new_t is even used, I don't see why everyone was so hellbent on ignoring that. Simply debugging the program would've showed that, and while everyone was too busy calling out an uninitialised T object, moving the instance() call fixed all segfaults. I do appreciate the feedback, especially on Singleton design, thanks. –  May 26 '18 at 23:23
  • Well, the uninitialized `T` pointer (not a T object, because the program never creates any) is still a UB-causing problem, and with UB-causing bugs like this all bets about the behavior of the program are off. It's not unreasonable to say "well, as long as this is in there, you have no guarantees about anything," although it's not the way I would argue. If it interests you, my take is that as long as all that was in there, it wasn't really a minimal example, and stripping it down to a minimal example would have exposed the error more clearly and have gotten you more pertinent suggestions. – Wintermute May 27 '18 at 00:12
  • I'm trying to be nice here, but I also have to be frank: In the first iteration, you slapped an incomplete example into your question that no one had any way of running through a debugger. In the second, you slapped in a grand total of five different files and several hundred lines of code in there after people asked for a **minimal** verifiable example. The point of MCVEs is both to help you narrow down where the error could be and to make it easy for others to reproduce it and understand your code within a few minutes. This was not making it easy, and so you didn't get lots of effort. Sorry. – Wintermute May 27 '18 at 00:18
  • @Wintermute Uh, the example is close to 100 lines of code, not several hundreds, and took me less than 1 minute to copy and paste. I also said many times that the error is related to the member vector, NOT the objects it is storing. It's hard to justify why every single person ignored me every time I say that and then say it's reasonable for you to call out unrelated issues instead of looking at the problem I've asked for help with. I gave an MCVE of my problem because I genuinely wanted insight, there's no justifying ignorance. Still, I do thank you for your help, but I resolved it myself. –  Jun 01 '18 at 13:52
0

Answering my own question here. I am trying to call a member variable from within the constructor of the object that holds it, so the vector I am trying to access is not yet instantiated and doesn't exist in memory. That is what causes the Segmentation fault to occur, I am trying to access memory that is not allocated yet, hence any call acting on any member of my C class was causing this issue.

I fixed this problem by adding a public function to the class that then calls the private readLines() function. I call that public function from the object that will take ownership of it, and since this occurs after it has been instantiated, the memory is accessible and the problem disappears.

  • This is incorrect. Once the constructor body is entered, all member variables are initialized. The problem you had wasn't that the member vector of the singleton object you were constructing wasn't initiialized; as I pointed out in my answer, it was that you weren't *using* it. Instead of calling `this->readLines()`, you were calling `instance->readLines()`, at a time when the `instance` pointer was not yet initialized (more precisely: still zero-initialized, i.e. `nullptr`). What you did likely works because it postpones the `readLines` call until after the `instance` pointer is initialized. – Wintermute Jun 01 '18 at 21:22