[SOLVED - my bad]crash with std::vector in c++14

I just updated a project to use C++14 (in Xcode), and code I had working fine before crashes now. I have a class like this:

struct ControlRecord
{
int controlIndex;
Control* pControl;
};
typedef std::vector<ControlRecord> ControlsVector;

This class does *not* take ownership of the pointer pControl.

My assumption has always been that calling push_back() on this vector pushes a shallow copy of the object onto the back of the vector, and when the object is later destroyed, it does not matter that it has a pointer member, because the object it points to is completely managed elsewhere. (The only issue would be if I used the pointer after the object it pointed to had been moved or deleted, which does not happen here.)

However, my code crashes when I call push_back on a member vector of type ControlsVector.

I have this code that is called every time I create a Control object:

int MyContainer::addControl( Control* pControl, int index )
{
ControlRecord newControl
newControl.controlIndex = index;
newControl.pControl = pControl;
myControlsVector.push_back(newControl);
}

My code is crashing when I call push_back(). Why?

If I were mismanaging the ControlRecord or the Control* pointers somewhere, I would expect a crash when using the items in the vector, not when adding copies of local objects to a member vector.

The object pointed to has a lifetime (allocated elsewhere in my audio plugin code) that lasts until my class is destroyed, so I am not putting an invalid pointer into that structure.

I tried to read up on C++14 and c++11 vectors, but there was a lot there that I don't understand, especially related to "move semantics" vs. "copy semantics", and to "allocators". It's too much to wrap my old brain around in a short period of time. I just need to know what I am doing wrong that was not wrong previously.

Does std::vector no longer simply do a shallow copy when storing objects such as this?

Any thoughts/ideas how I can fix this?
Last edited on
You have the ideas correct, push_back would make a copy using the default copy constructor or default copy operator (depending on the implementation).

This is likely one of those situations where something else has been corrupted, and it manifests at a point removed from the actual problem. We'd have to see a working, compilable version that exhibits the problem to be of help.

Before you tell us that it's too much code, what I'm telling you to do is to create a test for this. A simple main function that uses these classes, and just does what demonstrates the problem.

A Stroustrup points out, this exercise usually answer the question by itself. Either the problem won't happen in the test (which tells you the problem isn't in the code you're testing), or you'll see it when you create the test program.

Testing in this way is a standard engineering approach to modern software, so as you progress to increasingly ambitious targets, this is a standard method to employ anyway. Might as well do it with this one.

If you can recreate the problem in an example small enough to post here, we'll likely see what that is.

Oh, and the issues of "move semantics" is something you can get to later. The basic use of the containers is exactly what you're expecting - those are for objects that can't be copied. Allocators have been there a long time, but most people don't bother to use them.


I can't spot anything in that code that's a dead giveaway as to what's causing this issue. First off, did you clean and fully rebuild the project after updating?

Second, when you say "crash", crash in what way? Uncaught exception? Segmentation fault?

-Albatross
Nah, there’s a dead giveaway right in the first post y’all missed: that their pointer. In a record. In a vector.

You will get failure (crashes) if you try to free that thing more than once, or if you free one ControlRecord and try to access the freed memory from another ControlRecord.

Whether or not you manage *pControl is irrelevant if the routines you are passing objects two get two duplicate ControlRecords.

This is where I would be looking for failure.
Thanks, @Niccolo,

I am not accessing the vector at all until later in the running of this audio plugin software. At this point, I am merely using a dynamic_cast<Control*> on a pointer given to me by code that is scanning XML and creating objects, and if the dynamic_cast returns a valid pointer, then I use this code to add it to a list of Control objects (as opposed to non-Control objects that might also be created by the XML scan). If the object pointed to could not be dynamic_cast<Control*>, then a test for NULL prevents this code from being executed.

Nothing is deleted until the host app closes my audio plugin's UI.

The reason I suspected an issue with c++14 is that this exact same code has been executing fine in the field for a long time, and I am only seeing this happen after compiling under a newer OS, newer Xcode version, and using c++14 instead of simply c++ as the compiler language.

My fear was that perhaps now under c++14 I had to do something different that I am doing here.

It sounds like my assumptions were correct about it being ok to do what I am doing here, so I am going to assume something else, not shown in this code, is the real culprit. But I wanted to verify that my assumptions about using push_back on this structure was ok under c++14, and that vector hadn't changed this under the newer language rules. If this code is ok (aside from the typo returning int from a void function in what I posted), then I'm not going to bother trying to replicate the crash.

The crash is an Illegal Access exception that is thrown (if I recall the terminology), because some pointer passed into one of the layers of code inside of push_back is the value 0x01, which obviously is not a valid pointer value. I do not even begin to comprehend the multi-layer templated code that is within push_back() to try to figure out where it got that value from. It's not anything that I passed in to push_back(), I can tell for sure! The unhandled exception causes the audio host (ProTools) to crash to the desktop.

@Albatross, yes, I have done a Clean and Build, multiple times.

@Duthomhas, I am never getting two duplicate ControlRecords. That is a local variable, not a pointer, which a copy is made of when calling push_back(). There is no defined destructor for that ControlRecord struct, so the raw pointer is never deleted by me. It is simply abandoned when the ControlRecord is deleted. The original objects that those Control* pointers point to are deleted when the audio plugin's UI is closed by the host, not before. (And I clear my vector in my UI's destructor, without calling delete on those pointers myself, so there is never any chance for a conflict like that.)
@Duthomhas,

Nah, there’s a dead giveaway right in the first post y’all missed: that their pointer. In a record. In a vector.


I don't think so, if the OP's "promise" that this was merely a reference is correct, and it appears it may well be.

@hamoon1,

This tactic, of using a pointer given by some library or engine, is rather typical of physics engines, with which I do considerable work. It isn't a problem as long as they are only used as the reference intended by the providing engine/library, though sometimes special care to synchronize entity removal is required (which wouldn't really apply so much to an audio engine, but would if this is not processing audio, but entities of controls on a dialog, which is seems to be).

Now, however, you may have brought forward something of use. ProTools. If this is a ProTools plugin, there must be an accurate match between compiler settings, build discipline and the requirements of the plugin environment. I can't be sure, of course, but the behavior you've identified mimics those I've encountered making 3DS Max & various AutoDesk plugins. I've not worked with recent ProTools SDK's, so I don't have much to go on here. I know what is "touchy" about AutoDesk plugin products (and it was a bit different over the years as the versions advanced), but not for ProTools.

Plugins can be difficult to debug, so I should ask first if you're using Visual Studio under Windows. If so, it can attach to a running program to debug a plugin like this, and if you further include tools in the debug build to help check memory errors, you'll probably discover where the corruption is happening.

If it proves difficult to track down, at least the error appears to be predictably reproducible. Even though STL code is ugly to look at, you can set break points on the position you know fails in order to identify what is being corrupted. The objective there is to try to set a memory breakpoint, to see when that is being written at a time when it should not be.



Last edited on
Fair enough. (Sorry for my drive-by posting.)
Oh, no need for apology @Duthomhas, - it is a likely thing to look at rather directly, but it is also a known "idiom" of several "engines" to give raw pointers this way, and to say the least it is....uncomfortable to a modern C++ programmer.

It definitely should raise one's spidey senses. I dance with that devil when using PhysX and Bullet physics way too much. It only flames me now and then, and without much warning.
Last edited on
I found the problem. I had split my code into two versions, because I needed to support different versions of an audio SDK for VST3 vs. AAX plugins, and I wasn't including the correct base class' header file in my AAX code. While it compiled fine, the underlying class structure was different enough that it caused a corruption to occur in memory someplace, which just happened to show up when pushing onto the member's vector.

Thanks, everyone!
Topic archived. No new replies allowed.