During our compiler's intermediate code generation phase, and more specifically while testing the arithmetic expressions and assignment rules, I noticed that although the respective quads are constructed successfully, when printing them out sometimes we'll get a bad_alloc exception. After tracing it, it looks like it's cause by the printQuads() method and specifically the following string access of key:
if(q.result != nullptr && q.result->sym != nullptr) {
cout << "quad " << opcodeStrings[q.op] << " inside if key check for" << opcodeStrings[q.op] << endl;
resultKey = q.result->sym->key;
}
I'll try to include the code that's relevant instead of dumping 500 lines of code here.
So, below you can see our assignmentexpr and basic arithmetic expression rules and actions:
expr: assignexpr
| expr PLUS expr
{
bool isExpr1Arithm = check_arith($1);
bool isExpr2Arithm = check_arith($3);
if(!isExpr1Arithm || !isExpr2Arithm)
{
//string msg = !isExpr1Arithm ? "First operand isn\'t a number in addition!" : "Second operand isn\'t a number in addition!";
yyerror(token_node, "Both addition operands must be numbers!");
} else
{
double result = $1->numConst + $3->numConst;
$$ = newexpr(arithmetic_e);
$$->sym = newtemp(scope);
$$->numConst = result;
emit(add, $1, $3, $$, nextquadlabel(), yylineno);
}
}
| expr MIN expr
{
bool isExpr1Arithm = check_arith($1);
bool isExpr2Arithm = check_arith($3);
if(!isExpr1Arithm || !isExpr2Arithm)
{
//string msg = !isExpr1Arithm ? "First operand isn\'t a number in subtraction!" : "Second operand isn\'t a number in subtracion!";
yyerror(token_node, "Both suctraction operands must be numbers!");
} else
{
double result = $1->numConst - $3->numConst;
$$ = newexpr(arithmetic_e);
$$->sym = newtemp(scope);
$$->numConst = result;
emit(sub, $1, $3, $$, nextquadlabel(), yylineno);
}
}
| expr MUL expr
{
bool isExpr1Arithm = check_arith($1);
bool isExpr2Arithm = check_arith($3);
if(!isExpr1Arithm || !isExpr2Arithm)
{
//string msg = !isExpr1Arithm ? "First operand isn\'t a number in subtraction!" : "Second operand isn\'t a number in subtracion!";
yyerror(token_node, "Both multiplication operands must be numbers!");
} else
{
double result = $1->numConst * $3->numConst;
$$ = newexpr(arithmetic_e);
$$->sym = newtemp(scope);
$$->numConst = result;
emit(mul, $1, $3, $$, nextquadlabel(), yylineno);
}
}
| expr DIV expr
{
bool isExpr1Arithm = check_arith($1);
bool isExpr2Arithm = check_arith($3);
if(!isExpr1Arithm || !isExpr2Arithm)
{
//string msg = !isExpr1Arithm ? "First operand isn\'t a number in subtraction!" : "Second operand isn\'t a number in subtracion!";
yyerror(token_node, "Both division operands must be numbers!");
} else
{
if($3->numConst == 0) {
yyerror(token_node, "division by 0!");
} else {
double result = $1->numConst / $3->numConst;
$$ = newexpr(arithmetic_e);
$$->sym = newtemp(scope);
$$->numConst = result;
emit(div_op, $1, $3, $$, nextquadlabel(), yylineno);
}
}
}
| expr MOD expr
{
bool isExpr1Arithm = check_arith($1);
bool isExpr2Arithm = check_arith($3);
if(!isExpr1Arithm || !isExpr2Arithm)
{
//string msg = !isExpr1Arithm ? "First operand isn\'t a number in subtraction!" : "Second operand isn\'t a number in subtracion!";
yyerror(token_node, "Both modulus operands must be numbers!");
} else
{
if($3->numConst == 0) {
yyerror(token_node, "division by 0!");
} else {
double result = fmod($1->numConst,$3->numConst);
$$ = newexpr(arithmetic_e);
$$->sym = newtemp(scope);
$$->numConst = result;
emit(mod_op, $1, $3, $$, nextquadlabel(), yylineno);
}
}
}
...
assignexpr: lvalue ASSIGN expr { if ( isMemberOfFunc )
{
isMemberOfFunc=false;
}
else{ if ( islocalid==true ){
islocalid = false;
}else{
if ( isLibFunc($1->sym->key) ) yyerror(token_node,"Library function \"" + $1->sym->key + "\" is not lvalue!");
if (SymTable_lookup(symtab,$1->sym->key,scope,false) && isFunc($1->sym->key,scope)) yyerror(token_node,"User function \"" + $1->sym->key + "\" is not lvalue!");
}
}
if($1->type == tableitem_e)
{
// lvalue[index] = expr
emit(tablesetelem,$1->index,$3,$1,nextquadlabel(),yylineno);
$$ = emit_iftableitem($1,nextquadlabel(),yylineno, scope);
$$->type = assignment;
} else
{
emit(assign,$3,NULL,$1,nextquadlabel(),yylineno); //lval = expr;
$$ = newexpr(assignment);
$$->sym = newtemp(scope);
emit(assign, $1,NULL,$$,nextquadlabel(),yylineno);
}
}
;
The printQuads method is the following:
void printQuads() {
unsigned int index = 1;
cout << "quad#\t\topcode\t\tresult\t\targ1\t\targ2\t\tlabel" <<endl;
cout << "-------------------------------------------------------------------------------------------------" << endl;
for(quad q : quads) {
string arg1_type = "";
string arg2_type = "";
cout << "quad before arg1 type check" << endl;
if(q.arg1 != nullptr) {
switch (q.arg1->type) {
case const_bool:
arg1_type = "\'" + BoolToString(q.arg1->boolConst) + "\'";
break;
case const_string:
arg1_type = "\"" + q.arg1->strConst + "\"";
break;
case const_num:
arg1_type = to_string(q.arg1->numConst);
break;
case var:
arg1_type = q.arg1->sym->key;
break;
case nil_e:
arg1_type = "nil";
break;
default:
arg1_type = q.arg1->sym->key;
break;
}
}
cout << "quad before arg2 type check" << endl;
if(q.arg2 != nullptr) {
switch (q.arg2->type) {
case const_bool:
arg2_type = "\'" + BoolToString(q.arg2->boolConst) + "\'";
break;
case const_string:
arg2_type = "\"" + q.arg2->strConst + "\"";
break;
case const_num:
arg2_type = to_string(q.arg2->numConst);
break;
case nil_e:
arg2_type = "nil";
break;
default:
arg2_type = q.arg2->sym->key;
break;
}
}
string label = "";
if(q.op == if_eq || q.op == if_noteq || q.op == if_lesseq || q.op == if_greatereq
|| q.op == if_less || q.op == if_greater || q.op == jump) label = q.label;
string resultKey = "";
cout << "quad before key check" << endl;
if(q.result != nullptr && q.result->sym != nullptr) {
cout << "quad " << opcodeStrings[q.op] << " inside if key check for" << opcodeStrings[q.op] << endl;
resultKey = q.result->sym->key;
}
cout << "quad after key check" << endl;
cout << index << ":\t\t" << opcodeStrings[q.op] << "\t\t" << resultKey << "\t\t" << arg1_type << "\t\t" << arg2_type << "\t\t" << label << "\t\t" << endl;
index++;
}
}
The quads variable is just a vector of quads. Here is the quad struct:
enum expr_t {
var,
tableitem_e,
user_func,
lib_func,
arithmetic_e,
assignment,
newtable_e,
const_num,
const_bool,
const_string,
nil_e,
bool_e
};
struct expr {
expr_t type;
binding* sym;
expr* index;
double numConst;
string strConst;
bool boolConst;
expr* next;
};
struct quad {
iopcode op;
expr* result;
expr* arg1;
expr* arg2;
unsigned int label;
unsigned int line;
};
The binding* is defined as follows and is a symbol table binding:
enum SymbolType{GLOBAL_, LOCAL_, FORMAL_, USERFUNC_, LIBFUNC_, TEMP};
struct binding{
std::string key;
bool isactive = true;
SymbolType sym;
//vector<binding *> formals;
scope_space space;
unsigned int offset;
unsigned int scope;
int line;
};
Here are the emit(), newtemp & newexpr() methods:
void emit(
iopcode op,
expr* arg1,
expr* arg2,
expr* result,
unsigned int label,
unsigned int line
){
quad p;
p.op = op;
p.arg1 = arg1;
p.arg2 = arg2;
p.result = result;
p.label = label;
p.line = line;
currQuad++;
quads.push_back(p);
}
binding *newtemp(unsigned int scope){
string name = newTempName();
binding* sym = SymTable_get(symtab,name,scope);
if (sym== nullptr){
SymTable_put(symtab,name,scope,TEMP,-1);
binding* sym = SymTable_get(symtab,name,scope);
return sym;
}else return sym;
}
string newTempName(){
string temp = "_t" + to_string(countertemp) + " ";
countertemp++;
return temp;
}
expr* newexpr(expr_t exprt){
expr* current = new expr;
current->sym = NULL;
current->index = NULL;
current->numConst = 0;
current->strConst = "";
current->boolConst = false;
current->next = NULL;
current->type = exprt;
return current;
}
unsigned int countertemp = 0;
unsigned int currQuad = 0;
Symbol table cpp file:
#include <algorithm>
bool isHidingBindings = false;
/* Return a hash code for pcKey.*/
static unsigned int SymTable_hash(string pcKey){
size_t ui;
unsigned int uiHash = 0U;
for (ui = 0U; pcKey[ui] != '\0'; ui++)
uiHash = uiHash * HASH_MULTIPLIER + pcKey[ui];
return (uiHash % DEFAULT_SIZE);
}
/*If b contains a binding with key pcKey, returns 1.Otherwise 0.
It is a checked runtime error for oSymTable and pcKey to be NULL.*/
int Bucket_contains(scope_bucket b, string pcKey){
vector<binding> current = b.entries[SymTable_hash(pcKey)]; /*find the entry binding based on the argument pcKey*/
for (int i=0; i<current.size(); i++){
binding cur = current.at(i);
if (cur.key==pcKey) return 1;
}
return 0;
}
/*epistrefei to index gia to bucket pou antistixei sto scope 'scope'.Se periptwsh pou den uparxei
akoma bucket gia to en logw scope, ean to create einai true dhmiourgei to antistoixo bucket sto
oSymTable kai epistrefei to index tou.Diaforetika epistrefei thn timh -1.*/
int indexofscope(SymTable_T &oSymTable, unsigned int scope, bool create){
int index=-1;
for(int i=0; i<oSymTable.buckets.size(); i++) if (oSymTable.buckets[i].scope == scope) index=i;
if ( index==-1 && create ){
scope_bucket newbucket;
newbucket.scope = scope;
oSymTable.buckets.push_back(newbucket);
index = oSymTable.buckets.size()-1;
}
return index;
}
/*If there is no binding with key : pcKey in oSymTable, puts a new binding with
this key and value : pvvValue returning 1.Otherise, it just returns 0.
It is a checked runtime error for oSymTable and pcKey to be NULL.*/
int SymTable_put(SymTable_T &oSymTable, string pcKey,unsigned int scope, SymbolType st, unsigned int line){
int index = indexofscope(oSymTable,scope, true);
if(index==-1) cerr<<"ERROR"<<endl;
scope_bucket *current = &oSymTable.buckets.at(index);
if ( Bucket_contains(*current, pcKey) && st != FORMAL_ && st != LOCAL_) return 0; /*If the binding exists in oSymTable return 0.*/
binding newnode;
newnode.key = pcKey;
newnode.isactive = true;
newnode.line = line;
newnode.sym = st;
newnode.scope = scope;
current->entries[SymTable_hash(pcKey)].push_back(newnode);
return 1;
}
/*Pairnei ws orisma to oSymTable kai to scope pou theloume na apenergopoihsoume.
An to sugkekrimeno scope den uparxei sto oSymTable epistrefei -1.Diaforetika 0*/
void SymTable_hide(SymTable_T &oSymTable, unsigned int scope){
isHidingBindings = true;
for(int i=scope; i >= 0; i--) {
if(i == 0) return;
int index = indexofscope(oSymTable,i,false);
if(index == -1) continue;
scope_bucket *current = &oSymTable.buckets.at(index);
for (int i=0; i<DEFAULT_SIZE; i++) {
for (int j=0; j<current->entries[i].size(); j++) {
if(current->entries[i].at(j).sym == LOCAL_ || current->entries[i].at(j).sym == FORMAL_)
current->entries[i].at(j).isactive = false;
}
}
}
}
void SymTable_show(SymTable_T &oSymTable, unsigned int scope){
isHidingBindings = false;
for(int i=scope; i >= 0; i--) {
if(i == 0) return;
int index = indexofscope(oSymTable,i,false);
if(index == -1) continue;
scope_bucket *current = &oSymTable.buckets.at(index);
for (int i=0; i<DEFAULT_SIZE; i++) {
for (int j=0; j<current->entries[i].size(); j++) {
if(current->entries[i].at(j).sym == LOCAL_ || current->entries[i].at(j).sym == FORMAL_)
current->entries[i].at(j).isactive = true;
}
}
}
}
bool SymTable_lookup(SymTable_T oSymTable, string pcKey, unsigned int scope, bool searchInScopeOnly){
for(int i=scope; i >= 0; i--) {
if(searchInScopeOnly && i != scope) break;
int index = indexofscope(oSymTable,i,false);
if(index == -1) continue;
scope_bucket current = oSymTable.buckets[index];
for(vector<binding> entry : current.entries) {
for(binding b : entry) {
if(b.key == pcKey && b.isactive) return true;
else if(b.key == pcKey && !b.isactive) return false;
}
}
}
return false;
}
binding* SymTable_lookupAndGet(SymTable_T &oSymTable, string pcKey, unsigned int scope) noexcept{
for ( int i=scope; i >= 0; --i ){
int index = indexofscope(oSymTable,i,false );
if (index==-1) continue;
scope_bucket ¤t = oSymTable.buckets[index];
for (auto &entry : current.entries) {
for (auto &b : entry ){
if ( b.key == pcKey ) return &b;
}
}
}
return nullptr;
}
/*Lamvanei ws orisma to oSymTable, kleidh tou tou desmou pou psaxnoume kai to scope tou desmou.
H sunarthsh telika epistrefei to value tou tou desmou.Diaforetika epistrefei 0*/
binding* SymTable_get(SymTable_T &oSymTable, const string pcKey, unsigned int scope){
for ( int i=scope; i >= 0; --i )
{
const int index = indexofscope( oSymTable, i, false );
if ( index == -1 )
{
continue;
}
scope_bucket& current = oSymTable.buckets[index];
for ( auto& entry : current.entries)
{
for ( auto& b : entry )
{
if ( b.key == pcKey )
{
return &b;
}
}
}
}
return nullptr;
}
When run with the following test file, the issue occurs at the z5 = 4 / 2; expression's assign quad:
// simple arithmetic operations
z1 = 1 + 2;
z10 = 1 + 1;
z2 = 1 - 3;
z3 = 4 * 4;
z4 = 5 / 2;
What's confusing is that if I print out the sym->key after each emit() in the arithmetic-related actions, I can see the keys just fine. But once I try to access them inside the printQuads it will fail (for the div operation at least so far). This has me thinking that maybe we are shallow copying the binding* sym thus losing the key? But how come the rest of them are printed normally?
I'm thinking that the issue (which has occured again in the past at various stages) could be caused by us using a ton of copy-by-value instead of by-reference but I can't exactly confirm this because most of the time it works (I'm guessing that means that this is undefined behavior?).
I'm sure this is very difficult to help debug but maybe someone will eyeball something that I can't see after this many hours.
Debugging by eyeballing your code is probably a useful skill, but it's far from the most productive form of debugging. These days, it's much less necessary, since there are lots of good tools which you can use to detect problems. (Here, I do mean "you", specifically. I can't use any of those tools because I don't have your complete project in front of me. And nor do I particularly want it; this is not a request for you to paste hundreds of lines of code).
You're almost certainly right that your problem is related to some kind of undefined behaviour. If you're correct about the bad_alloc exception being thrown by what is effectively a copy of a std::string, then it's most likely the result of the thing being copied from not being a valid std::string. Perhaps it's an actual std::string object whose internal members have been corrupted; perhaps the pointer is not actually pointing to an active std::string (which I think is the real problem, see below). Or perhaps it's something else.
Either way, the error occurred long before the bug manifests itself, so you're only going to stumble upon where it happened by blind luck. On the other hand, there are a variety of memory error detection tools available which may be able to pinpoint the precise moment in which you violated the contract by reading or writing to memory which didn't belong to you. These include Valgrind and AddressSanitizer (also known as ASan); one or both of these is certainly available for the platform on which you are developing your project. (I say that confidently even without knowing what that platform is, but you'll have to do a little research to find the one which works best for your particular environment. Both of those names can be looked up on Wikipedia.) These tools are very easy to use, and extraordinarily useful; they can save you hours or days of debugging and a lot of frustration. As an extra added bonus, they can detect bugs you don't even know you have, saving you the embarrassment of shipping a program which will blow up in the hands of the customer or the person who is marking your assignment. So I strongly recommend learning how to use them.
I probably should leave it at that, because it's better motivation to learn to use the tools. Still, I can't resist making a guess about where the problem lies. But honestly, you will learn a lot more by ignoring what I'm about to say and trying to figure out the problem yourself.
Anyway, you don't include much in the way of information about your SymTable_T class, and the inconsistent naming convention makes me wonder if you even wrote its code; perhaps it was part of the skeleton code you were given for this assignment. From what I can see in SymTable_put and SymTable_get, the SymTable_T includes something like a hash table, but doesn't use the C++ standard library associative containers. (That's a mistake from the beginning, IMHO. This assignment is about learning how to generate code, not how to write a good hash table. The C++ standard library associative containers are certainly adequate for your purposes, whether or not they are the absolute ideal for your use case, and they have the enormous advantages of already being thoroughly documented and debugged.)
It's possible that SymTable_T was not originally written in C++ at all. The use of free-standing functions like SymTable_put and SymTable_get rather than class methods is difficult to explain unless the functions were originally written in C, which doesn't allow object methods. On the other hand, they appear to use C++ standard library collections, as evidenced by the call to push_back in SymTable_put:
current->entries[SymTable_hash(pcKey)].push_back(newnode);
That suggests that entries is a std::vector (although there are other possibilities), and if it is, it should raise a red flag when you combine it with this, from SymTable_get (whitespace-edited to save screen space here):
for ( auto& entry : current.entries) {
for ( auto& b : entry ) {
if ( b.key == pcKey )
return &b;
}
}
To be honest, I don't understand that double loop. To start with, you seem to be ignoring the fact that there is a hash table somewhere in that data structure, but beyond that, it seems to me that entry should be a binding (that's what SymTable_put pushes onto the entries container), and I don't see where a binding is an iterable object. Perhaps I'm not reading that correctly.)
Regardless, evidently SymTable_get is returning a reference to something which is stored in a container, probably a std::vector, and that container is modified from time to time by having new elements pushed onto it. And pushing a new element onto the end of a std::vector invalidates all existing references to every element of the vector. (See https://en.cppreference.com/w/cpp/container/vector/push_back)
Thus, newtemp, which returns a binding* acquired from SymTable_get, is returning a pointer which may be invalidated in the future by some call to SymTable_put (though not by every call to that function; only the ones where the stars unline unhappily). That pointer is then stored into a data object which will (much later) be given to printQuads, which will attempt to use the pointer to make a copy of a string which it will attempt to print. And, as I mentioned towards the beginning of this treatise, trying to use an object which is pointed to by a dangling pointer is Undefined Behaviour.
As a minor note, making a copy of a string in order to print it out is completely unnecessary. A reference would work just fine, and save a bunch of unnecessary memory allocations. But that won't fix the problem (if my guess turns out to be correct) because printing through a dangling pointer is just as Undefined Behaviour as making a copy through a dangling pointer, and will likely manifest in some other mysterious way.
I am looking for specific Design Pattern in C++ that solve this problem.
I want to design a Storyboard. Our version of the Storyboard
contains arbitrary many notes (imagine it like putting sticky notes on a board).
Every note has a title, a text and a set of tags. E.g.
- title: "Test Traceplayer"
- text: "Implement a unit test for the class Traceplayer of the spark core framework."
- tags: {"unit test", "traceplayer", "testing", "spark core"}
Our Storyboard should enable us to search for notes by title, text and tags.
E.g.:
searchByTitle( "Test Traceplayer" )
searchByTag({"testing", "unit test"})
searchByText("Implement a unit test for the class Traceplayer of the spark core framework.")
For the sake of simplicity we don't want to do any similiarity or prefix matching when
searching for a title, tag or text. Only an exact match should give results.
I have number of solution that solve this problem O(1) search complexity But can any one suggest any "Design Pattern" that solve this problem.
Solve that issue with three STL map and get constant time search complexity
Looking for a specific Design Pattern that solves this problem.
I have solved this problem using 3 STL Map and solution get O(1) search complexity
#include <iostream>
#include <vector>
#include <map>
#define INPUT 8
class Note {
public:
string Tital;
string Text;
vector<string> vec;
Note(){
Tital = "\0";
Text = "\0";
}
};
class storyBoard{
public:
void AddNote(string Tital,string Text,vector<string> vec );
void RemoveByTital(string &tital);
void PrintStoredData();
Note* searchByTitle(string titleSearch);
Note* searchByText(string text_);
vector<Note*> searchByTag(string titleSearch);
void printSlip(Note *tm);
storyBoard(){}
private:
std::map<string,Note *> TitalMap;
std::map<string,Note *> TextMap;
std::map<string,std::vector<Note *> > TagsMap;
};
Note* storyBoard::searchByTitle(string titleSearch){
auto it_v = TitalMap.find(titleSearch);
if (it_v != TitalMap.end()){
cout<< "Tital search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
Note* storyBoard::searchByText(string titleSearch){
auto it_v = TextMap.find(titleSearch);
if (it_v != TextMap.end()){
cout<< "Text search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
vector<Note*> storyBoard::searchByTag(string tagSearch){
auto it_v = TagsMap.find(tagSearch);
if (it_v != TagsMap.end()){
cout<< "Tag search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<tagSearch << " Not found"<<endl;
vector<Note*> del;
return del;
}
}
void storyBoard::AddNote(string Tital, string Text, vector<string> v){
Note *note = new Note;
note->Tital = Tital;
note->Text = Text;
note->vec = v;
TitalMap[note->Tital] = note;
TextMap[note->Text] = note;
for (auto it = note->vec.begin(); it != note->vec.end(); ++it){
//check that is tags already
auto it_v = TagsMap.find(*it);
if (it_v != TagsMap.end()){
it_v->second. push_back(note);
} else {
vector<Note *> &v = TagsMap[*it];
v.push_back(note);
}
}
}
void storyBoard::printSlip(Note *tm){
cout << "Tital=" << tm->Tital <<endl
<< "Text=" << tm->Text <<endl
<< "Tags = ";
for (auto it = tm->vec.begin(); it != tm->vec.end(); ++it){
cout<< *it<<"\t";
}
cout<<endl<<endl;
}
void storyBoard::PrintStoredData(){
for(auto tm : TitalMap){
printSlip(tm.second);
}
cout<<endl;
}
void feed_data_for_testing(storyBoard &Sb);
void TestCase(storyBoard &Sb);
int main() {
storyBoard Sb;
feed_data_for_testing(Sb);
Sb.PrintStoredData(); /*Print all contain data */
cout<<"************* From Here start searching ************"<<endl;
TestCase(Sb);
return 0;
}
void TestCase(storyBoard &Sb){
Note* obj = Sb.searchByTitle("Tital-3");
if(obj != NULL){
Sb.printSlip(obj);
}
obj = Sb.searchByText("Text-4");
if(obj != NULL){
Sb.printSlip(obj);
}
vector<Note *> vec = Sb.searchByTag("tag-3");
if(vec.size() !=0){
for (auto it = vec.begin(); it != vec.end(); ++it){
//cout<<(*it)->Tital << "\t";
Sb.printSlip(*it);
}
}
}
void feed_data_for_testing(storyBoard &Sb){
vector<string> tags ;
int count =INPUT;
for(int i =1;i<=count;i++){
string tital = "Tital-" + std::to_string(i);
string text = "Text-" + std::to_string(i);
tags.clear();
for(int j =1;j<=i;j++){
string tag_ = "tag-" + std::to_string(j);
tags.push_back(tag_);
}
Sb.AddNote(tital,text,tags);
}
}
I am looking for a design pattern that solves this issue.
I update your code at the following point:-
convert the class into singleton so that data maintain a single map for each type
change map to unorder_map
#define INPUT 8
using namespace std;
/*use class to store single slip data*/
class Note {
public:
string Tital;
string Text;
vector<string> vec;
Note(){ //constructor to initialize data zero
Tital = "\0";
Text = "\0";
}
};
/*create a singalton pattern class so that create only one data storage*/
class storyBoard{
public:
static storyBoard* getInstance(){
storyBoard* Instance= instance.load();
if ( !Instance ){
std::lock_guard<std::mutex> myLock(lock);
Instance= instance.load();
if( !Instance ){
Instance= new storyBoard();
instance.store(Instance);
}
}
return Instance;
}
void AddNote(string Tital,string Text,vector<string> vec );
void RemoveByTital(string &tital);
void PrintStoredData();
Note* searchByTitle(string titleSearch);
Note* searchByText(string text_);
vector<Note*> searchByTag(string titleSearch);
void printSlip(Note *tm);
private:
storyBoard()= default;
~storyBoard()= default;
storyBoard(const storyBoard&)= delete;
storyBoard& operator=(const storyBoard&)= delete;
static std::atomic<storyBoard*> instance;
static std::mutex lock;
std::unordered_map<string,Note *> TitalMap;
std::unordered_map<string,Note *> TextMap;
std::unordered_map<string,std::vector<Note *> > TagsMap;
};
std::atomic<storyBoard*> storyBoard::instance;
std::mutex storyBoard::lock;
Note* storyBoard::searchByTitle(string titleSearch){
auto it_v = TitalMap.find(titleSearch);
if (it_v != TitalMap.end()){
cout<< "Tital search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
Note* storyBoard::searchByText(string titleSearch){
auto it_v = TextMap.find(titleSearch);
if (it_v != TextMap.end()){
cout<< "Text search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
vector<Note*> storyBoard::searchByTag(string tagSearch){
auto it_v = TagsMap.find(tagSearch);
if (it_v != TagsMap.end()){
cout<< "Tag search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<tagSearch << " Not found"<<endl;
vector<Note*> del;
return del;
}
}
void storyBoard::AddNote(string Tital, string Text, vector<string> v){
Note *note = new Note;
note->Tital = Tital;
note->Text = Text;
note->vec = v;
TitalMap[note->Tital] = note;
TextMap[note->Text] = note;
for (auto it = note->vec.begin(); it != note->vec.end(); ++it){
//check that is tags already
auto it_v = TagsMap.find(*it);
if (it_v != TagsMap.end()){
it_v->second. push_back(note);
} else {
vector<Note *> &v = TagsMap[*it];
v.push_back(note);
}
}
}
void storyBoard::printSlip(Note *tm){
cout << "Tital=" << tm->Tital <<endl
<< "Text=" << tm->Text <<endl
<< "Tags = ";
for (auto it = tm->vec.begin(); it != tm->vec.end(); ++it){
cout<< *it<<"\t";
}
cout<<endl<<endl;
}
void storyBoard::PrintStoredData(){
for(auto tm : TitalMap){
printSlip(tm.second);
}
cout<<endl;
}
/**temporary function only use for testing*/
void TestCase(){
storyBoard *Sb = storyBoard::getInstance();
Note* obj = Sb->searchByTitle("Tital-3");
if(obj != NULL){
Sb->printSlip(obj);
}
obj = Sb->searchByText("Text-4");
if(obj != NULL){
Sb->printSlip(obj);
}
vector<Note *> vec = Sb->searchByTag("tag-3");
if(vec.size() !=0){
for (auto it = vec.begin(); it != vec.end(); ++it){
//cout<<(*it)->Tital << "\t";
Sb->printSlip(*it);
}
}
}
/**temporary function only use for testing*/
void feed_data_for_testing(){
storyBoard *Sb = storyBoard::getInstance();
vector<string> tags ;
int count =INPUT;
for(int i =1;i<=count;i++){
string tital = "Tital-" + std::to_string(i);
string text = "Text-" + std::to_string(i);
tags.clear();
for(int j =1;j<=i;j++){
string tag_ = "tag-" + std::to_string(j);
tags.push_back(tag_);
}
Sb->AddNote(tital,text,tags);
}
}
int main() {
storyBoard *Sb = storyBoard::getInstance();
feed_data_for_testing();
Sb->PrintStoredData(); /*Print all contain data */
cout<<"************* From Here start searching ************"<<endl;
TestCase();
return 0;
}
I think the term design pattern was mistakenly used by your interviewer in place of the term idiom.
A major issue with your code (and could be the cause of rejection) is memory handling using classical c++ idioms:
use of smart pointers to manage the lifetime of your notes
rule of 3 (or 5) to handle copy/assignment(/move) of your StoryBoard object
Note that using smart pointers is only one way of managing the memory. You could as well use other idioms:
arena of notes and referencing notes from the arena inside your hashmaps
...
Once this issue is solved you have minor issues:
returning pointers instead of references, the StoryBoard is the owner of the memory, you should not return a pointer that a caller could inadvertently free.
no const accessors (returning const references)
repetitive code that could be factored
If I am not mistaken in interpreting what the interviewer said, this question should be moved to codereview.stackexhange.com
First of all I would like to apologize for my large code. I tried to keep it structured, but I am still new to programming in C++.
I created a C++ algorithm on OSX and it worked just fine. I need to run this program on Linux however. Compiling on Linux gave no errors, however when I run the program it gives the following error:
Segmentation fault (core dumped)
I am a newbie in debugging code and have tried to debug it with gdb, but I don't know how I should continue. The information gdb gives is the following:
c2f_function(new_candidates2, old_candidates, feature_list);
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x0000000000403dc5 in c2f_function (new_candidates=std::list = {...}, old_candidates=std::list = {...},
feature_list=empty std::list) at /home/martin/emc/group4/src/c2f_function.cpp:36
36 norm = iter_old->x -iter_new->x;
I have added the code below, it consists of a main file c2f.cpp, a header file c2f.hpp and an additional file where I store functions c2f_functions.cpp.
The error seems to happen when I pass 3 lists by reference to a function called c2f_functions. this function is within the c2f_functions.cpp script.
My questions are,
how can I solve this?
why does it work well under OSX but not under Linux?
Many thnaks!
MAIN FILE c2f.cpp:
#include "c2f.hpp"
#include "c2f_function.cpp"
int main()
{
// define variables
double x, y;
// create old candidates list
list<Candidate> old_candidates;
// create new candidates list
list<Candidate> new_candidates1;
list<Candidate> new_candidates2;
list<Candidate> new_candidates3;
// create new features list
list<Candidate> feature_list;
//=============================================================================//
// LOAD FIRST DATA SET
//-----------------------------------------------------------------------------//
ifstream file1_("newcandidates_it0.txt");
if (file1_.is_open())
{
cout << "Reading file...1 " << endl;
while( file1_ >> x >> y)
{
// cout << x << "," << y << endl;
new_candidates1.push_back(Candidate(x , y));
}
file1_.close();
}
else {cout << "file is not open";}
//=============================================================================//
c2f_function(new_candidates1, old_candidates, feature_list);
//=============================================================================//
// LOAD SECOND DATA SET
//-----------------------------------------------------------------------------//
ifstream file2_("newcandidates_it1.txt");
if (file2_.is_open())
{
cout << "Reading file...2 " << endl;
while( file2_ >> x >> y)
{
// cout << x << "," << y << endl;
new_candidates2.push_back(Candidate(x , y));
}
file2_.close();
}
else {cout << "file is not open";}
//=============================================================================//
c2f_function(new_candidates2, old_candidates, feature_list);
HEADER FILE c2f.hpp
# include <iostream>
# include <stdlib.h>
# include <string>
# include <math.h>
# include <Eigen/Dense>
# include <cstdio>
# include <cstdlib>
# include <list>
# include <fstream>
# include <algorithm>
// # include <cstdarg>
using namespace std;
using namespace Eigen;
// correspondence margin: new point must lie w/i 10cm from old point
# define CORR_MARGIN 0.1
# define PERSIST_UB 3
# define PERSIST_LB -PERSIST_UB
class Candidate
{
public:
int id;
double x;
double y;
int persistency = 0;
int pflag = 0; // persistency flag
Candidate ( double xNew, double yNew ): x(xNew), y(yNew){}
void increasePersistency()
{
if (persistency < PERSIST_UB) // bound persistency from above
persistency++;
}
void decreasePersistency()
{
if (persistency > PERSIST_LB) // bound persistency from below
persistency--;
}
// bool operator< (const Candidate& right) const { return id < right.id; }
};
bool ascendingId ( Candidate a, Candidate b)
{
return a.id < b.id;
}
bool descendingId ( Candidate a, Candidate b)
{
return a.id > b.id;
}
bool ascendingPersistency ( Candidate a, Candidate b)
{
return a.persistency < b.persistency;
}
bool descendingPersistency ( Candidate a, Candidate b)
{
return a.persistency > b.persistency;
}
bool ascendingPflag ( Candidate a, Candidate b)
{
return a.pflag < b.pflag;
}
bool descendingPflag ( Candidate a, Candidate b)
{
return a.pflag > b.pflag;
}
bool sameId_Feature (Feature first, Feature second)
{ return first.id == second.id; }
bool samePflag (Candidate first, Candidate second)
{ return first.persistency == second.persistency; }
bool finder (Candidate first, Candidate second)
{return first.id == second.id;}
bool not_persistent (Candidate &a)
{ return (a.persistency==PERSIST_LB); }
Functions File c2f_function.cpp
void print_list(list<Candidate> &list2print)
{
for (auto const &iter : list2print)
{
cout << iter.x
<< "," << iter.y
<< " with id "
<< iter.id
<< " and persistency "
<< iter.persistency
<< endl;
}
}
void c2f_function(list<Candidate> &new_candidates, list<Candidate> &old_candidates, list<Candidate> &feature_list)
{
double norm;
//=============================================================================//
// CHECK FOR CORRESPONDENCE
//-----------------------------------------------------------------------------//
// Check if old candidates exist (initialization purposes)
if (old_candidates.empty() == 0) // old candidates exist
{
// Check Correspondence
for (auto iter_old = old_candidates.begin(); iter_old != old_candidates.end(); iter_old++)
{
// int persistency_upd_flag = 0;
for (auto iter_new = new_candidates.begin(); iter_new != new_candidates.end(); iter_new++)
{
// compute the norm between old_candidates and new_candidates
// norm = sqrt( pow(iter_old->x - iter_new->x, 2.0) + pow(iter_old->y - iter_new->y, 2.0));
norm = iter_old->x -iter_new->x;
if (norm <= CORR_MARGIN)
{
// Update position of old entry and increase persistency
iter_old -> x = iter_new->x;
iter_old -> y = iter_new->y;
iter_old -> increasePersistency();
// flag an update;
iter_old -> pflag = 1;
// remove list entry that has been coupled
new_candidates.erase(iter_new);
}
}
}
}
else
{
back_insert_iterator<list<Candidate>> it(old_candidates);
for (auto const &iter : new_candidates)
{
it = iter;
}
int counter=1;
for (auto iter = old_candidates.begin(); iter!= old_candidates.end(); iter++)
{
iter -> id = counter;
++counter;
}
cout << "initializing data set" << endl;
cout << endl << "====================================================" << endl;
return;
}
//=============================================================================//
//=============================================================================//
// DECREASE PERSISTENCY FOR NON-VIEWED CANDIDATES
//-----------------------------------------------------------------------------//
// remove persistency to non-associated candidates
old_candidates.sort(ascendingPflag);
for (auto iter = old_candidates.begin(); iter!= old_candidates.end(); iter++)
{
if ( iter -> pflag == 0 )
{
iter -> decreasePersistency();
find_if (feature_list.begin(), feature_list.end(),
[iter] (Candidate &item)
{
if (item.id == iter->id)
{
item.persistency = iter->persistency;
return true;
}
else return false;
}
);
}
// reset pflags
iter -> pflag = 0;
}
//=============================================================================//
//=============================================================================//
// ADD id TO REMAINING new_candidates LIST
//-----------------------------------------------------------------------------//
// get new id
old_candidates.sort(descendingId);
int new_id = old_candidates.begin() -> id + 1;
// add id to added items to old_candidates
for (auto iter = new_candidates.begin(); iter!= new_candidates.end(); iter++)
{
iter -> id = new_id;
new_id++;
}
//=============================================================================//
//=============================================================================//
// MERGE REMAINING new_candidates WITH old_candidates LIST
//-----------------------------------------------------------------------------//
old_candidates.splice(old_candidates.end(), new_candidates);
//=============================================================================//
//=============================================================================//
// ADD TO feature_list
// REMOVE FROM feature_list
// REMOVE FROM old_list
//-----------------------------------------------------------------------------//
// removing from old_candidates when persistency # lower bound
old_candidates.sort(ascendingPersistency);
for (auto const &iter_old : old_candidates)
{
if (iter_old.persistency == PERSIST_LB)
{
old_candidates.pop_front();
}
else
{break;}
}
// removing from feature_list when persistency # lower bound
feature_list.sort(ascendingPersistency);
for (auto const &iter_feat : feature_list)
{
if (iter_feat.persistency == PERSIST_LB)
{
feature_list.pop_front();
}
else
{break;}
}
// sorting
old_candidates.sort(descendingPersistency);
// adding
back_insert_iterator<list<Candidate>> it(feature_list);
// define counter
int counter;
for (auto const &iter_old : old_candidates)
{
counter =0;
if (iter_old.persistency == PERSIST_UB)
{
if (feature_list.size()>0)
{
for (auto iter_feat = feature_list.begin(); iter_feat != feature_list.end(); iter_feat++)
{
if (iter_feat->id == iter_old.id)
{
iter_feat->x = iter_old.x;
iter_feat->y = iter_old.y;
iter_feat->persistency = iter_old.persistency;
counter = 0;
break;
}
else
{
counter++;
}
}
if (counter >0)
{
it = iter_old;
}
}
else
it = iter_old;
}
else
{
break;
}
}
//=============================================================================//
//=============================================================================//
// DISPLAY FEATURE LIST
//-----------------------------------------------------------------------------//
if (feature_list.size() > 0)
{
feature_list.sort(ascendingId);
cout << "Feature members" << endl;
print_list(feature_list);
cout << endl << "====================================================" << endl;
}
else
cout << endl << "====================================================" << endl;
//=============================================================================//
}
//*****************************************************************************//
//*****************************************************************************//
SYSSEGV Segmentation Fault is caused by an attempt to access memory outside of the program's allowed area. In this case, either iter_old or iter_new is not initialized or contains a value that does not correspond to the program's memory area.
It may crash on one computer system and not on another because 1) different systems can have different values in uninitialized variables and 2) different systems define the programs available memory differently.
In short, look for bad pointer values with SEGV errors, and know that bugs can appear in different ways on different systems.
I'm not sure but I suspect the problem is that you erase an iterator and, next, you use (increment) it.
The following is the crucial part
for (auto iter_new = new_candidates.begin(); iter_new != new_candidates.end(); iter_new++)
{
norm = iter_old->x -iter_new->x;
if (norm <= CORR_MARGIN)
{
// [...]
new_candidates.erase(iter_new);
}
}
When you erase(iter_new), iter_new become an iterator pointing to an invalid object; incrementing it (iter_new++) give you (if I'm not wrong) an undefined value and the following iter_new->x can segmentation fault your program.
I suppose that a solution can be the use of the postfix increment calling erase() so that erase() call a copy of iter_new and iter_new is incremented to a valid iterator before the call to erase(); something like
auto = new_candidates.begin();
while ( iter_new != new_candidates.end() )
{
norm = iter_old->x -iter_new->x;
if (norm <= CORR_MARGIN)
{
// [...]
new_candidates.erase( iter_new++ );
}
else
++iter_new;
}
p.s.: sorry for my bad English
std::string output;
if ((checkbox1->isChecked() && checkbox2->isChecked()) &&
(!checkbox3->isChecked() || !checkbox4->isChecked() || !checkbox5->isChecked() || !checkbox6->isChecked()))
{
output = " Using Checkbox: 1, 2 ";
}
if ((checkbox1->isChecked() && checkbox2->isChecked() && checkbox3->isChecked()) &&
(!checkbox4->isChecked() || !checkbox5->isChecked() || !checkbox6->isChecked()))
{
output = " Using Checkbox: 1, 2, 3 ";
}
....
using QT creator how can I verify how many checkboxes have been checked and change the output string accordingly?
with multiple if statements it's not working due to me getting confused with all those NOT AND OR.
and it takes a long time to code all possibilities.
All your checkBoxes should be in groupBox
Try this:
QList<QCheckBox *> allButtons = ui->groupBox->findChildren<QCheckBox *>();
qDebug() <<allButtons.size();
for(int i = 0; i < allButtons.size(); ++i)
{
if(allButtons.at(i)->isChecked())
qDebug() << "Use" << allButtons.at(i)->text()<< i;//or what you need
}
Use an array of checkboxes like this
// h-file
#include <vector>
class MyForm {
...
std::vector< QCheckBox* > m_checkBoxes;
};
// cpp-file
MyForm::MyForm() {
...
m_checkBoxes.push_back( checkbox1 );
m_checkBoxes.push_back( checkbox2 );
...
m_checkBoxes.push_back( checkbox5 );
}
...
output = " Using Checkbox:";
for ( int i = 0, size = m_checkBoxes.size(); i < size; ++i ) {
if ( m_checkBoxes[ i ]->isChecked() ) {
output += std::to_string( i + 1 ) + ", ";
}
}
TLDR: Place them in a container and build your string by iterating over them.
Code:
// line taken from #Chernobyl
QList<QCheckBox *> allButtons = ui->groupBox->findChildren<QCheckBox *>();
auto index = 1;
std::ostringstream outputBuffer;
outputBuffer << "Using Checkbox: ";
for(const auto checkBox: allButtons)
{
if(checkBox->isChecked())
outputBuffer << index << ", ";
++index;
}
auto output = outputBuffer.str();
Use QString instead of std::string and then:
QCheckBox* checkboxes[6];
checkbox[0] = checkbox1;
checkbox[1] = checkbox2;
checkbox[2] = checkbox3;
checkbox[3] = checkbox4;
checkbox[4] = checkbox5;
checkbox[5] = checkbox6;
QStringList usedCheckboxes;
for (int i = 0; i < 6; i++)
{
if (checkbox[i]->isChecked())
usedCheckboxes << QString::number(i+1);
}
QString output = " Using Checkbox: " + usedCheckboxes.join(", ") + " ";
This is just an example, but there's numerous ways to implement this. You could keep your checkboxes in the QList which is a class field, so you don't have to "build" the checkboxes array every time. You could also use QString::arg() instead of + operator for string when you build the output, etc, etc.
What I've proposed is just a quick example.
I have to program something that filters email by checking if the source of the email is found in a blacklist or if any word in the email's content is found in a list of blocked words.
Here's the function that filters the email.
void filterEmail(vector<email>& amis, vector<email>& corbeille,
vector<email>& rejete, vector<email>& emails,
vector<string> b, vector<string> w,
vector<string> spam)
{
bool isListed(email, vector<string>);
bool isSpam(email, vector<string>);
for(vector<email>::size_type i = 0; i < emails.size();)
{
if(isListed(emails[i], b))
{
corbeille.push_back(emails[i]);
emails.erase(emails.begin() + i);
}
else i++;
}
for(vector<email>::size_type i = 0; i < emails.size();)
{
if(isListed(emails[i], w))
{
amis.push_back(emails[i]);
emails.erase(emails.begin() + i);
}
else i++;
}
for(vector<email>::size_type i = 0; i < emails.size();)
{
if(isSpam(emails[i], spam))
{
cout << emails[i].source << " " << emails[i].content[0];
rejete.push_back(emails[i]);
emails.erase(emails.begin() + i);
}
else i++;
}
return;
}
I have a really weird problem with the part where it checks for spam words in the content (3rd for loop)
after a few test cout's I realised after the first time isSpam returns true, the content of the email changes to the line where it first returns true, and all the next emails return true.
bool isSpam(const email e, const vector<string> motsinterdis)
{
for(vector<string>::size_type i = 0; i < e.content.size(); ++i)
{
for(vector<string>::size_type j = 0; j < motsinterdis.size(); ++j)
{
if(string::npos != e.content[i].find(motsinterdis[j]))
{
cout << e.source << endl;
cout << motsinterdis[j] << " found in " << e.content[i] << endl;
return true;
}
}
}
return false;
}
I have an Struct Email, which contains a vector content. Sorry for the french variables in there :P
Hope I've been clear enough,
Thanks a lot.