I have a C++ structure defined as follows:
typedef struct event{
int id;
string name;
//int arg0;
QByteArray data;
bool operator<(const event& e) const
{
return id < e.id;
}
bool operator==(const event& e) const
{
return id == e.id;
}
}Event;
I also have a map defined as follows:
map<string, set<Event>> mapOfEventsByString;
When I want to see if an Event is associated to a given string I use this line of code:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
{
//do stuff
}
Problem: Sometimes (and I mean that 9/10 times I can run the entire application with the exact same data set without any problems), I get a segmentation fault here:
bool operator<(const event& e) const
{
return id < e.id; <------- GIVES SEGMENTATION FAULT SOMETIMES
}
After many attempts at reproducing the error while debugging, I managed to pinpoint the segfault to that line. In that scenario e.id is filled with data and id says: "no such value".
Help?
Thank you
Without a backtrace we are only guessing, but this is a strong indication that the member id doesn't exist, so you're accessing memory you shouldn't.
If the member id doesn't exist, then your operator< call is broken. Given that it is invoked by the below highlighted part of your code:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^^^^^^^^^^^^
that suggests to me that the below highlighted expression does not assuredly refer to a valid object:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^^^^^
The only way that can happen is if the below highlighted dereference operation is invalid:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^
which occurs when the find fails, returning mapOfEventsByString.end() (which cannot be dereferenced):
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I think if you actually check for find success you'll see that, 1 out of 10 times, aString was not found in mapOfEventsByString.
Let's do this, instead:
const auto it = mapOfEventsByString.find(aString);
if (it != mapOfEventsByString.end() && it->second.count(event) == 1) {
// do stuff
}
Now you can put a breakpoint in for when it == mapOfEventsByString.end() and investigate why the find is failing. Good luck!
Related
I am trying to make a YamlConfig class with yaml-cpp. One of its main features is that, in the style of Bukkit, a Minecaft API, its users can easily reference different nodes in a tree of maps (e.g. a map that contains maps that contains maps, but to varying depths) via a string like "map1.map2.map3.keyoffinalvalue". I wrote the seek function in the following minimal example to do it, but even though it is marked const, the string that is printed out each time it is invoked is different and seems to be just the map that contains the final value on the previous invocation. This demonstrates a problem that m_rootNode seems to be changing. What is going on?
Originally, this function wasn't const (and I will need to make it non-const after debugging) and I thought that due to horrific API design that YAML::Node acted like some sort of reference instead of a well-behaved value-like type as is standard in C++ (surprising the user of an API is generally horrific API design). But, that is not consistent with the function being marked const. As such, I now have no idea what is going on. I tried to find similar issues via my search engine as well, but nothing that came up was remotely relevant beyond just being part of the same YAML library.
#include <yaml-cpp/yaml.h>
#include <string>
#include <string_view>
#include <vector>
#include <iostream>
class YamlConfig{
public:
YamlConfig(const std::string &yaml);
YAML::Node seek(std::string_view key, bool create) const;
private:
YAML::Node m_rootNode;
static std::vector<std::string> split(std::string_view input, char delimeter);
};
YamlConfig::YamlConfig(const std::string &yaml){
m_rootNode = YAML::Load(yaml);
}
YAML::Node YamlConfig::seek(std::string_view key, bool create) const {
auto splitKey = split(key, '.');
YAML::Node current = m_rootNode;
YAML::Emitter emitter;
emitter << current;
std::cout << emitter.c_str() << std::endl;
for(const auto &keySegment : splitKey){
if(current.IsMap()){
current = current[keySegment];
if( (!current) && (!create) ){
throw std::runtime_error("Invalid YAML key due to attempting to descend in to non-existent node: " + keySegment);
}
}else{
throw std::runtime_error("Invalid YAML key due to attempting to descend in to non-map node: " + std::string(key));
}
}
return current;
}
std::vector<std::string> YamlConfig::split(std::string_view input, char delimeter) {
std::vector<std::string> output;
auto baseit = input.begin();
for(auto it=input.begin();it!=input.end();++it){
if(*it == delimeter){
output.emplace_back(baseit, it);
baseit = it+1;
if(*baseit == delimeter){
throw std::invalid_argument("Double delimiter found in string \"" + std::string(input) + "\"");
}
}
}
output.emplace_back(baseit, input.end());
return output;
}
int main(){
const std::string yaml = "engine:\n view-distance: 16\n fullscreen: false\n";
std::cout << yaml << std::endl;
YamlConfig yamlConfig(yaml);
std::cout << yamlConfig.seek("engine.view-distance", false).as<std::string>() << std::endl;
std::cout << yamlConfig.seek("engine.view-distance", false).as<std::string>() << std::endl;
return 0;
}
This code, when compiled, produces the following output without my comments:
engine: //this is the printout of the string in main
view-distance: 16
fullscreen: false
engine: //this is the first printout of the root node, good
view-distance: 16
fullscreen: false
16 //this is the printout of the value that was retrieved from the yaml data
view-distance: 16 //This is the second printout of the "root" node. It looks like the root node is now the engine node, changed via a const function What is going on?
fullscreen: false
terminate called after throwing an instance of 'std::runtime_error' //this is an artifact of the root node seemingly changing, and is consistent with it changing to be the engine node
what(): Invalid YAML key due to attempting to descend in to non-existent node: engine
Aborted (core dumped)
Compile command:
clang++ --std=c++17 -lyaml-cpp yaml.cpp -o yaml
A quick look at the API reveals these lines:
mutable detail::shared_memory_holder m_pMemory;
mutable detail::node* m_pNode;
The mutable modifier tells us that even a const function on this node may change these values. That is concerning, but actually not the problem. As we can see, YAML::Node is only a reference to the actual node. Digging further, we find the implementation of the assignment operator:
inline Node& Node::operator=(const Node& rhs) {
if (is(rhs))
return *this;
AssignNode(rhs);
return *this;
}
/* snip */
inline void Node::AssignNode(const Node& rhs) {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
rhs.EnsureNodeExists();
if (!m_pNode) {
m_pNode = rhs.m_pNode;
m_pMemory = rhs.m_pMemory;
return;
}
m_pNode->set_ref(*rhs.m_pNode);
m_pMemory->merge(*rhs.m_pMemory);
m_pNode = rhs.m_pNode;
}
So as we can see, assigning a YAML::Node will modify the referenced node which is your problem. This works even though your function is const since you can still modify the referenced data from a const pointer.
The question is, how is the API supposed to be used? I don't really know. the operator[] returns a value, not a reference, so you cannot use pointers; and there is no find function which would return an iterator that could be used.
A, admittedly horrible, workaround would be:
auto tmp = current[keySegment]; // get next node
current.~Node(); // destruct node reference (not the referenced node)
new (¤t) Node(tmp); // call copy constructor with placement new to assign
// tmp to current. necessary since current is invalid at this point.
Alternatively, you could implement seek recursively to avoid re-assigning current.
You probably know situations like this where you just want to assign to a (const) variable with an expression which might fail (throw) (e.g.container.at()) which forces you to write boiler plate code:
void foo(const string &key) {
auto it = data_store.find(key);
if (it == data_store.end()) {
return;
}
const auto & element = it->second;
...
go on with `element`...
...
}
In Python you could write code like this:
def foo(name):
try:
element = data_store[key]
except KeyError:
return
..
go on with `element`
..
.. with is less noisy because you don't introduce that useless extra it just for checking existence.
If C++'s try would not introduce a variable scope you could just use at():
void foo(const string &key) {
try {
const auto & element = data_store.at(key);
} catch (const out_of_range &) {
return;
}
...
go on with `element`...
...
}
What's the way to go here if you don't want to abandon constness and keep your code clean?
If lambdas only could have a try/catch body you could write
void foo(const string &key) {
const auto & element = [&] () -> T try {
return data_store.at(key);
} catch () {
return;
} ();
...
go on with `element`...
...
}
Some answers to similar questions suggest try/catch blocks around all the code:
void foo(const string &key) {
try {
const auto & element = data_store.at(key);
...
go on with `element`...
...
} catch (const out_of_range &) {
return;
} catch (some other exception) {
...
} catch (some other exception) {
...
}
}
But I don't like this because of three reasons:
there's no visual correlation between at() and it's catch block
there might be code that also need you to handle out_of_range
you have to write nested code
Which (nice, short and clean) alternatives do you know?
There are three good options on this thread, there's not really any other option.
Those cases assume we are initializing an object; for initializing a reference as you are, apply the techniques to std::reference_wrapper, or a pointer.
BTW I would not discount your first code sample so quickly. It's simpler than all the other options, and it is a common recommendation in C++ to only use exceptions for exceptional conditions -- things you do not expect to be a normal part of the function's contract. It's not idiomatic to use them as a shortcut.
In other words, if the function design is to do nothing if the lookup fails, then throw-catching is an unnecessary complication to the function. You've just written an even uglier version of C-style error handling.
The whole point of the at() accessor is that your function can be kept simple by not catching -- the exception can be left to propagate up to a more general error handler.
I keep getting this error and I'm not sure how to correct it as I am given no errors in my code editors. I have looked up similar issues, but I am still having trouble to understand how to apply the solutions here. I've tried altering my code for several hours now, but to no avail. Any help would be appreciated. I have provided my .h and .cpp files below.
ErrorMessage.h
#ifndef SICT_ERRORMESSAGE_H
#define SICT_ERRORMESSAGE_H
#include <iostream>
namespace sict {
class ErrorMessage {
char* message_; //pointer that holds the address of the message stored in current object
public:
explicit ErrorMessage(const char* errorMessage = nullptr); //receive address of a C-style nullterminate string holding an error message
ErrorMessage(const ErrorMessage& em) = delete; //deleted copy constructor that prevents copying of an ErrorMessage object
ErrorMessage& operator=(const ErrorMessage& em) = delete; //deleted assignment operator that prevents assignment of ErrorMessage object to current object
virtual ~ErrorMessage(); //deallocates any memory that has been dynamically allocated by the current object
void clear(); //clears any message stored by current object and initialize object to safe, empty state
bool isClear() const; //return true if object is in a safe, empty state
void message(const char* str); //stores a copy of the C-style string pointed to by str
const char* message() const; //return address of the message stored in current object
};
//helper operator
std::ostream& operator<<(std::ostream& os, const ErrorMessage& err);
}
#endif
ErrorMessage.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include "ErrorMessage.h"
namespace sict {
ErrorMessage::ErrorMessage(const char* errorMessage) {
if(errorMessage == nullptr) {
message_ = nullptr;
}
else {
message(errorMessage);
}
}
ErrorMessage::~ErrorMessage() {
delete [] message_;
}
void ErrorMessage::clear() {
delete [] message_;
message_ = nullptr;
}
bool ErrorMessage::isClear() const {
if(message_ == nullptr) {
return true;
}
else {
return false;
}
}
void ErrorMessage::message(const char* str) {
delete [] message_;
message_ = new char[strlen(str) + 1];
strcpy(message_, str);
}
const char* ErrorMessage::message() const {
return message_;
}
std::ostream& operator<<(std::ostream& os, const ErrorMessage& err) {
if(!err.isClear()) {
os << err.message();
}
return os;
}
}
It's not surprising your code made it through editor syntax checks and compilation - it's valid code. It's just got an incorrect pointer somewhere.
This may mean that your'e accidentally dereferencing something , or perhaps passing a value somewhere you should be passing a pointer. You should get a compile time warning about that kind of stuff.
Another possibility is that you're failing to initialize some pointer, and its value happens to be 0xb75....
Clearly, neither you nor I are not likely to guess from whence this error originates. As Sam Varshavchik pointed out in a comment, you don't even know if the code you posted is the source of the error. Even if you guess your way through this one ( or perhaps keenly observe, Sam ), it's just plain silly to try to write C++ that way.
What you need is a debugger. A debugger is a program you run your program within, and it keeps track of the program's state so that when you have a memory violation, the debugger can produce a backtrace showing where in your source code the error occurred. You also have to compile your program with debugging support, so that the debugger has markers it can use to refer back to the source code.
It's a process far beyond the scope of your question, but one that's easy to learn about once you know what you're going for. Look for one that integrates with your IDE, if possible, as you're leveraging your development environment heavily. It's not unlikely that you already have it set up- you might just need to use it. Search for C++ debugging in the context of your editor first - if it turns up nothing, consider searching under your compiler suite, whatever that may be ( if your'e using open source, you're probably using gcc, and the matching debugger is gdb ).
You're about to gain a far more accurate understanding of what it is to program C / C++. Good luck.
I am getting the execption error in the following piece of code. Any suggestions on what might be causing it ?
Error : Invalid deque <T> subscript
typedef boost::shared_ptr<HistObj> shared_hist_def;
typedef std::deque<shared_hist_def> vector_def;
typedef boost::shared_ptr<vector_def> shared_vector_def;
typedef boost::unordered_map<int,shared_vector_def> in_map_def;
typedef boost::shared_ptr<in_map_def> shared_inner_map_def;
Domain::shared_hist_def& Domain::GetSpecificHistoricalTuple(const std::string& symb,const int& tframe,const int& val)
{
Domain::shared_inner_map_def tshare = stat_History_base[symb];
shared_vector_def tmp = tshare->at(tframe);
try
{
Domain::shared_hist_def safe_tuple = tmp->at(val);
return safe_tuple;
}
catch (std::exception &ex)
{
std::string a = ex.what();
__debugbreak();
}
}
More information:
The above method is a static method. And the program is multithreaded.Any chance that this error occurs because multiple threads access it. I had that assumption but then think that function parameters above could never be the same at one time.
Your val parameter seems to be too high (greater or equal to the number of elements in the deque), or maybe it's negative.
I was trying to use an STL list in C++ and I arrived into a strange exception that I'm not able to understand.
The list is defined as list<ASTNode*> m_stats; and ASTNode* is a class. When I try to add elements by calling
ASTNode *node = new ASTNode();
m_stats.push_back(node);
it throws the following exception:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000004
0x91c20fe7 in std::_List_node_base::hook()
I tried to debug with gdb and inspected value inserted, is it not null and it is exactly what it should be..
the backtrace is:
#0 0x91c20fe7 in std::_List_node_base::hook ()
#1 0x0000a9fb in std::list<ASTNode*, std::allocator<ASTNode*> >::_M_insert (this=0x180344, __position={_M_node = 0x0}, __x=#0xbffff094) at stl_list.h:1152
#2 0x0000aa27 in std::list<ASTNode*, std::allocator<ASTNode*> >::push_front (this=0x180344, __x=#0xbffff094) at stl_list.h:743
#3 0x0000aa45 in ASTStatements::addStatement (this=0x180340, stat=0x180410) at ast.h:277
Am I missing something?
EDIT: added class source
class ASTStatements : public ASTNode
{
list<ASTNode*> m_stats;
public:
ASTStatements() {}
ASTStatements(list<ASTNode*> stats)
{
std::copy(stats.begin(), stats.end(), m_stats.begin());
}
ASTStatements(const ASTStatements &other)
{
std::copy(other.m_stats.begin(), other.m_stats.end(), m_stats.begin());
}
ASTStatements &operator= (const ASTStatements &other)
{
if (&other != this)
{
std::copy(other.m_stats.begin(), other.m_stats.end(), m_stats.begin());
}
}
ASTStatements *clone()
{
return new ASTStatements(*this);
}
u8 type()
{
return 0;
}
const char *generateASM()
{
list<ASTNode*>::iterator it = m_stats.begin();
while (it != m_stats.end())
{
((ASTNode*)*it)->generateASM();
++it;
}
}
void addStatement(ASTNode *stat)
{
m_stats.push_front(stat);
}
u8 typeCheck()
{
return 0;
}
};
I used it in a bison grammar file to handle multiple statements (didn't find a better way to handle a generic list of items in a non terminal) in this way:
statements:
statement { if ($$ == null) $$ = new ASTStatements(); ((ASTStatements*)$$)->addStatement($1); } statements { $$->generateASM(); }
;
Thanks in advance
Your constructors and assignment statements are wrong. When you call std::copy, there must already be enough space at the destination iterator to accommodate everything you're copying into it. The list won't grow by itself. The error message you're getting suggests you're overwriting some memory, and that's likely exactly what happens when you try to copy into a list that isn't big enough. (Formally, the behavior is undefined.)
You could use a std::back_insert_iterator, which is an iterator adapter that appends items to the underlying container instead of overwriting the current location. Create one using the std::back_inserter helper function from the <algorithm> header:
std::copy(stats.begin(), stats.end(), std::back_inserter(m_stats));
Better yet, though, is to skip all the copying and let the list's own constructors and assignment operators take care of it for you, as they're designed to:
ASTStatements(list<ASTNode*> stats)
: m_stats(stats)
{ }
ASTStatements& operator=(const ASTStatements& other)
{
m_stats = other.m_stats;
}
There was most likely something you did to your list that occurred before the lines of code you posted. I'd suggest looking at all places m_stats is used and double check your use of it. You could post your uses of m_stats here and we could try helping you some more.