I'm working to create a few classes that work together to simulate functions for a rental car agency. I have the classes working and I can create objects of each of the classes, but when I try to run the following code I get a segmentation fault and I'm not sure why. Why am I getting a segmentation fault when I declare the objects in this order?
I've tried switching the order in which I declare the objects and the error goes away. If I declare two cars with just the Car() constructor, then the problem also goes away. If I remove any of the functions in either the Car or Sensor class, the error goes away.
PS: I asked this question a few days ago, and I included way too much code because I couldn't identify the error (far from complete, minimal, and verifiable), but this time I've narrowed it down as far as I can. I apologize if it's a lot of code, but I've removed all the code I can and if I remove any more of it the problem goes away.
Here's my main file:
#include "Car.h"
int main() {
char make[] = "Subaru", model[] = "Outback", name[] = "Brandon",
type[] = "radar";
Sensor sensors[3];
Sensor sensor1 = Sensor(type), sensor2 = Sensor(), sensor3 =
Sensor();
sensors[0] = sensor1;
sensors[1] = sensor2;
sensors[2]= sensor3;
Car car1 = Car();
Car car2 = Car(make, 155.81, sensors);
return 0;
}
My Car class:
#ifndef CAR_H
#define CAR_H
#include <iostream>
#include "MyString.h"
#include "Sensor.h"
using namespace std;
#define MAX_STR_SIZE 256
#define MAX_NUM_SNSRS 3
class Car {
public:
Car();
Car(char* make, float baseprice, Sensor* sensors);
void setMake(char* make);
void setBaseprice(float baseprice);
void setAvailable(bool available);
void setOwner(char* owner);
void updatePrice();
private:
char m_make[MAX_STR_SIZE];
Sensor m_sensors[MAX_NUM_SNSRS];
int m_numsensors;
float m_baseprice;
float m_finalprice;
bool m_available;
char m_owner[MAX_STR_SIZE];
};
#endif
#include "Car.h"
Car::Car() {
char dflt[] = {'\0'};
setMake(dflt);
setAvailable(true);
setOwner(dflt);
m_numsensors = 0;
setBaseprice(0.0);
}
Car::Car(char* make, float baseprice, Sensor* sensors) {
char dflt[] = {'\0'};
setMake(make);
setAvailable(true);
setOwner(dflt);
for(int i = 0; i < MAX_NUM_SNSRS; i++) {
(*(m_sensors + i)) = Sensor(*(sensors + i));
if(myStringCompare((sensors + i)->getType(), "none") != 0) {
m_numsensors++;
}
}
setBaseprice(baseprice);
}
void Car::setMake(char* make) {
myStringCopy(m_make, make);
}
void Car::setBaseprice(float baseprice) {
m_baseprice = baseprice;
updatePrice();
}
void Car::setAvailable(bool available) {
m_available = available;
}
void Car::setOwner(char* owner) {
myStringCopy(m_owner, owner);
}
void Car::updatePrice() {
float totSnsrPrice = 0.0;
for(int i = 0; i < m_numsensors; i++) {
totSnsrPrice += (m_sensors + i)->getCost();
}
m_finalprice = m_baseprice + totSnsrPrice;
}
My Sensor class:
#ifndef SENSOR_H
#define SENSOR_H
#include "MyString.h"
#define MAX_STR_SIZE 256
#define NUM_TYPES 5
class Sensor {
public:
Sensor();
Sensor(char* type);
char* getType();
float getCost();
void setType(char* type);
void setCost(float extraCost);
private:
char m_type[MAX_STR_SIZE];
float m_extracost;
};
#endif
#include "Sensor.h"
const char* validSensors[] = {"gps", "camera", "lidar", "radar",
"none"};
const float prices[] = {5.0, 10.0, 15.0, 20.0, 0.0};
Sensor::Sensor() {
char dflt[] = "none";
setType(dflt);
setCost(0.0);
}
Sensor::Sensor(char* type) {
int index = -1;
char dflt[] = "none";
for(int i = 0; i < NUM_TYPES; i++) {
if(myStringCompare(type, *(validSensors + i)) == 0) {
index = i;
}
}
if(index < 0) {
setType(dflt);
setCost(0.0);
} else {
setType(type);
setCost(*(prices + index));
}
}
char* Sensor::getType() {
return m_type;
}
float Sensor::getCost() {
return m_extracost;
}
void Sensor::setType(char* type) {
myStringCopy(m_type, type);
}
void Sensor::setCost(float extracost) {
m_extracost = extracost;
}
myStringCopy and myStringCompare are just the typical std::string copy and compare functions, we're just not allowed to use them (they are include in MyString.h, I've been using them for a while, so I know they work as intended).
I expect the output to be nothing, but still successful, instead of a segmentation fault. I cannot find the error anywhere, any help would be appreciated. Thanks!
Edit: Here's my string class, as asked:
#ifndef MYSTRING_H
#define MYSTRING_H
int myStringCompare(const char* str1, const char* str2);
char* myStringCopy(char* destination, const char* source);
#endif
#include "MyString.h"
int myStringCompare(const char* str1, const char* str2) {
int index = 0;
while(*(str1 + index) != '\0' || *(str2 + index) != '\0') {
if(*(str1 + index) < *(str2 + index)) {
return -1;
}
if(*(str1 + index) > *(str2 + index)) {
return 1;
}
index++;
}
return 0;
}
char* myStringCopy(char* destination, const char* source) {
int index = 0;
while(*(source + index) != '\0') {
*(destination + index) = *(source + index);
index++;
}
*(destination + index) = '\0';
return destination;
}
We don't have the full details on your string class, so this is hard to reproduce.
However, when you call sensors[0] = sensor1; you are copying your Sensor, but haven't defined an assignment operator (or copy constructor for that matter).
You also do this in the Car constructor with
(*(m_sensors + i)) = Sensor(*(sensors + i));
Now without the full details of your string class, I can give suggestions that might help.
First, you are doing a lot of copying when you set up the senors.
You can collapse this
Sensor sensors[3];
Sensor sensor1 = Sensor(type), sensor2 = Sensor(), sensor3 =
Sensor();
sensors[0] = sensor1;
sensors[1] = sensor2;
sensors[2]= sensor3;
down to
Sensor sensors[3]={{type}, {}, {}};
This might not solve the problem, but is less to look at.
Next, remove the setters. You can use delegating constructors to tie the two you have together, and avoid these.
This avoids some copies.
Look very carefully at what gets deep or shallow copied.
If you have two char * types,
char * word = "Hello";
char * another_word = word;
the second is a "shallow" copy. You need an equivalent of strcpy to actually make a different ("deep") copy of the string.
Following code is about searching for neighbours in realtime. As soon as a new node is added to my graph, the function updateSeqNeighbours for this node is called. What I know is, that the new node is definitely neighbour to the last one added. In the next step I use this fact to look in the neighbourhood of the previously added node, find the one closest to the new and then search this neighbourhood for the closest neighbour.
I repeat this only for example 3 times, to limit the number of neighbours for one node to 4 to keep a constant time frame for calculation. It works wonderful, except for after ~30 nodes the calculation time increases very fast with every additional node resulting in a bad_alloc exception.
#ifndef GRAPH_NODE_H_
#define GRAPH_NODE_H_
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
class Node {
public:
double x;
double y;
Node* nodePrev;
vector<Node> seqNeighbours;
//Constructor
Node();
Node(double x, double y);
virtual ~Node();
//Operator functions
Node& operator=(const Node& n);
//Get&Set
int getID();
//Public member functions
void addNeighbour(Node& n);
bool isSeqNeighbour(int ID);
int updateSeqNeighbours();
double distanceTo(Node& n);
private:
static int count;
int ID;
void _setDefaults();
};
int Node::count = 0;
Node::Node() {
_setDefaults();
}
Node::Node(double x, double y) {
_setDefaults();
this->x = x;
this->y = y;
}
Node::~Node() {
// TODO Auto-generated destructor stub
}
//Operator functions
Node& Node::operator=(const Node& n) {
if (this != &n) {
ID = n.ID;
x = n.x;
y = n.y;
seqNeighbours.clear();
seqNeighbours = n.seqNeighbours;
nodePrev = n.nodePrev;
}
return *this;
}
//Get&Set
int Node::getID() {
return this->ID;
}
//Public member functions
void Node::addNeighbour(Node& n) {
seqNeighbours.push_back(n);
}
double Node::distanceTo(Node& n) {
return sqrt((n.x-x)*(n.x-x) + (n.y-y)*(n.y-y));
}
bool Node::isSeqNeighbour(int ID) {
for (int i = 0; i < seqNeighbours.size(); i++) {
if (seqNeighbours[i].getID() == ID) {
return true;
}
}
return false;
}
int Node::updateSeqNeighbours() {
if (nodePrev == NULL) {
return 1;
} else {
Node seed = *nodePrev; //previous node as seed
seqNeighbours.push_back(seed);
for (int i = 0; i < 3; i++) {
if (seed.nodePrev == NULL) break;
double minDist = 15353453;
Node closest;
for (int j = 0; j < seed.seqNeighbours.size(); j++) {
double dist = distanceTo(seed.seqNeighbours[j]);
if (dist < minDist) {
minDist = dist;
closest = seed.seqNeighbours[j];
}
}
if (minDist < 150) {
seqNeighbours.push_back(closest);
}
seed = closest;
}
cout << "neighbours = " << seqNeighbours.size() << endl;
}
return 0;
}
void Node::_setDefaults() {
x = 0;
y = 0;
ID = count;
nodePrev = NULL;
seqNeighbours.clear();
count++;
}
#endif /* GRAPH_NODE_H_ */
Graph:
#ifndef GRAPH_GRAPH_H_
#define GRAPH_GRAPH_H_
#include <vector>
#include <iostream>
#include "Node.h"
using namespace std;
class Graph {
public:
Graph();
virtual ~Graph();
vector<Node> list;
void addNode(Node& n);
void addSeqNode(Node& n);
private:
void _setDefaults();
};
Graph::Graph() {
// TODO Auto-generated constructor stub
}
Graph::~Graph() {
// TODO Auto-generated destructor stub
}
void Graph::addNode(Node& n) {
list.push_back(n);
}
void Graph::addSeqNode(Node& n) {
if (!list.empty()) {
n.nodePrev = &list.back();
}
n.updateSeqNeighbours();
list.push_back(n);
}
void Graph::_setDefaults() {
list.clear();
}
#endif /* GRAPH_GRAPH_H_ */
I suspect running out of memory causes this. However 40 nodes with each 4 neighbours doesn't sound much of a problem to me. Anyone any idea what goes wrong?
Edit:
Error in german, so I need to guess:
An exception accured in project prSimulation1.exe of class std::bad_alloc. Adress of Exception: '0x5476016'. Process was stopped.
Your seqNeighbours is vector<Node>. That means it stores the neighbours themselves, not pointers to them or their indices. The copy constructor, therefore, copies all the neighbours. Copying each neighbour, in turn, requires to copy its neighbours, which requires to copy their neighbours, and so on. Your assignment also copies all the neighbours, which requires to copy their neighbours, and so on. This means that each copy exponentially increases memory load, until the system is unable to store all the neighbours, neighbours of neigbours etc.
PS: on a side note, a vector called "list" is a bad idea. It is like a list called "vector", a set called "map", or a cat called Dog.
I have successfully installed FLTK in VS 2015 Community Edition. I am dealing with other headers, part of Stroustrup's book, to build graphs and to create custom windows where to attach them. My problem is that when I am trying to compile I receive 4 error messagges (plus 23 Warnings):
(active) IntelliSense namespace "std" has no member
"Vector" Win32Project1 c:\Users\Leonardo\Documents\Visual Studio
2015\Projects\Win32Project1\Win32Project1\Window.h 17
Error C2873 Build 'Vector': symbol cannot be used in a
using-declaration Win32Project1 c:\users\leonardo\documents\visual
studio 2015\projects\win32project1\win32project1\Window.h 17
Error C2039 Build 'Vector': is not a member of
'std' Win32Project1 c:\users\leonardo\documents\visual studio
2015\projects\win32project1\win32project1\Window.h 17
Error C2440 Build 'return': cannot convert from 'std::ifstream' to
'bool' Win32Project1 C:\Users\Leonardo\Documents\Visual Studio
2015\Projects\Win32Project1\Win32Project1\Graph.cpp 371
I have set a Win32Project (not a Console Project), following the steps of this site: http://www.c-jump.com/bcc/common/Talk2/Cxx/FltkInstallVC/FltkInstallVC.html.
If I run the test example it perfectly works so, I guess, to have correctly installed the additional library.
Furthermore, I have added every header or source file provided by the author in the project.
main.cpp:
#include "std_lib_facilities.h"
#include "Graph.h"
#include "Simple_window.h"
int main()
{
using namespace Graph_lib;
Point tl{ 100,100 };
Simple_window win{ tl,600,400,"My Window" };
win.wait_for_button();
}
All headers and source file can be reached at: http://www.stroustrup.com/Programming/Programming-code.zip
Here Windows.h
//
// This is a GUI support code to the chapters 12-16 of the book
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//
#ifndef WINDOW_GUARD
#define WINDOW_GUARD
#include <string>
#include <vector>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include "Point.h"
using std::string;
using std::vector;
namespace Graph_lib
{
class Shape; // "forward declare" Shape
class Widget;
//------------------------------------------------------------------------------
class Window : public Fl_Window {
public:
// let the system pick the location:
Window(int w, int h, const string& title);
// top left corner in xy
Window(Point xy, int w, int h, const string& title);
virtual ~Window() { }
int x_max() const { return w; }
int y_max() const { return h; }
void resize(int ww, int hh) { w=ww, h=hh; size(ww,hh); }
void set_label(const string& s) { copy_label(s.c_str()); }
void attach(Shape& s) { shapes.push_back(&s); }
void attach(Widget&);
void detach(Shape& s); // remove s from shapes
void detach(Widget& w); // remove w from window (deactivates callbacks)
void put_on_top(Shape& p); // put p on top of other shapes
protected:
void draw();
private:
vector<Shape*> shapes; // shapes attached to window
int w,h; // window size
void init();
};
and Graph.cpp
//
// This is a GUI support code to the chapters 12-16 of the book
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//
#include <FL/Fl_GIF_Image.H>
#include <FL/Fl_JPEG_Image.H>
#include "Graph.h"
//------------------------------------------------------------------------------
namespace Graph_lib {
//------------------------------------------------------------------------------
Shape::Shape() :
lcolor(fl_color()), // default color for lines and characters
ls(0), // default style
fcolor(Color::invisible) // no fill
{}
//------------------------------------------------------------------------------
void Shape::add(Point p) // protected
{
points.push_back(p);
}
//------------------------------------------------------------------------------
void Shape::set_point(int i,Point p) // not used; not necessary so far
{
points[i] = p;
}
//------------------------------------------------------------------------------
void Shape::draw_lines() const
{
if (color().visibility() && 1<points.size()) // draw sole pixel?
for (unsigned int i=1; i<points.size(); ++i)
fl_line(points[i-1].x,points[i-1].y,points[i].x,points[i].y);
}
//------------------------------------------------------------------------------
void Shape::draw() const
{
Fl_Color oldc = fl_color();
// there is no good portable way of retrieving the current style
fl_color(lcolor.as_int()); // set color
fl_line_style(ls.style(),ls.width()); // set style
draw_lines();
fl_color(oldc); // reset color (to previous)
fl_line_style(0); // reset line style to default
}
//------------------------------------------------------------------------------
void Shape::move(int dx, int dy) // move the shape +=dx and +=dy
{
for (int i = 0; i<points.size(); ++i) {
points[i].x+=dx;
points[i].y+=dy;
}
}
//------------------------------------------------------------------------------
Line::Line(Point p1, Point p2) // construct a line from two points
{
add(p1); // add p1 to this shape
add(p2); // add p2 to this shape
}
//------------------------------------------------------------------------------
void Lines::add(Point p1, Point p2)
{
Shape::add(p1);
Shape::add(p2);
}
//------------------------------------------------------------------------------
// draw lines connecting pairs of points
void Lines::draw_lines() const
{
if (color().visibility())
for (int i=1; i<number_of_points(); i+=2)
fl_line(point(i-1).x,point(i-1).y,point(i).x,point(i).y);
}
//------------------------------------------------------------------------------
// does two lines (p1,p2) and (p3,p4) intersect?
// if se return the distance of the intersect point as distances from p1
inline pair<double,double> line_intersect(Point p1, Point p2, Point p3, Point p4, bool& parallel)
{
double x1 = p1.x;
double x2 = p2.x;
double x3 = p3.x;
double x4 = p4.x;
double y1 = p1.y;
double y2 = p2.y;
double y3 = p3.y;
double y4 = p4.y;
double denom = ((y4 - y3)*(x2-x1) - (x4-x3)*(y2-y1));
if (denom == 0){
parallel= true;
return pair<double,double>(0,0);
}
parallel = false;
return pair<double,double>( ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3))/denom,
((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3))/denom);
}
//------------------------------------------------------------------------------
//intersection between two line segments
//Returns true if the two segments intersect,
//in which case intersection is set to the point of intersection
bool line_segment_intersect(Point p1, Point p2, Point p3, Point p4, Point& intersection){
bool parallel;
pair<double,double> u = line_intersect(p1,p2,p3,p4,parallel);
if (parallel || u.first < 0 || u.first > 1 || u.second < 0 || u.second > 1) return false;
intersection.x = p1.x + u.first*(p2.x - p1.x);
intersection.y = p1.y + u.first*(p2.y - p1.y);
return true;
}
//------------------------------------------------------------------------------
void Polygon::add(Point p)
{
int np = number_of_points();
if (1<np) { // check that thenew line isn't parallel to the previous one
if (p==point(np-1)) error("polygon point equal to previous point");
bool parallel;
line_intersect(point(np-1),p,point(np-2),point(np-1),parallel);
if (parallel)
error("two polygon points lie in a straight line");
}
for (int i = 1; i<np-1; ++i) { // check that new segment doesn't interset and old point
Point ignore(0,0);
if (line_segment_intersect(point(np-1),p,point(i-1),point(i),ignore))
error("intersect in polygon");
}
Closed_polyline::add(p);
}
//------------------------------------------------------------------------------
void Polygon::draw_lines() const
{
if (number_of_points() < 3) error("less than 3 points in a Polygon");
Closed_polyline::draw_lines();
}
//------------------------------------------------------------------------------
void Open_polyline::draw_lines() const
{
if (fill_color().visibility()) {
fl_color(fill_color().as_int());
fl_begin_complex_polygon();
for(int i=0; i<number_of_points(); ++i){
fl_vertex(point(i).x, point(i).y);
}
fl_end_complex_polygon();
fl_color(color().as_int()); // reset color
}
if (color().visibility())
Shape::draw_lines();
}
//------------------------------------------------------------------------------
void Closed_polyline::draw_lines() const
{
Open_polyline::draw_lines(); // first draw the "open poly line part"
// then draw closing line:
if (color().visibility())
fl_line(point(number_of_points()-1).x,
point(number_of_points()-1).y,
point(0).x,
point(0).y);
}
//------------------------------------------------------------------------------
void draw_mark(Point xy, char c)
{
static const int dx = 4;
static const int dy = 4;
string m(1,c);
fl_draw(m.c_str(),xy.x-dx,xy.y+dy);
}
//------------------------------------------------------------------------------
void Marked_polyline::draw_lines() const
{
Open_polyline::draw_lines();
for (int i=0; i<number_of_points(); ++i)
draw_mark(point(i),mark[i%mark.size()]);
}
//------------------------------------------------------------------------------
void Rectangle::draw_lines() const
{
if (fill_color().visibility()) { // fill
fl_color(fill_color().as_int());
fl_rectf(point(0).x,point(0).y,w,h);
}
if (color().visibility()) { // lines on top of fill
fl_color(color().as_int());
fl_rect(point(0).x,point(0).y,w,h);
}
}
//------------------------------------------------------------------------------
Circle::Circle(Point p, int rr) // center and radius
:r(rr)
{
add(Point(p.x-r,p.y-r)); // store top-left corner
}
//------------------------------------------------------------------------------
Point Circle::center() const
{
return Point(point(0).x+r, point(0).y+r);
}
//------------------------------------------------------------------------------
void Circle::draw_lines() const
{
if (color().visibility())
fl_arc(point(0).x,point(0).y,r+r,r+r,0,360);
}
//------------------------------------------------------------------------------
void Ellipse::draw_lines() const
{
if (color().visibility())
fl_arc(point(0).x,point(0).y,w+w,h+h,0,360);
}
//------------------------------------------------------------------------------
void Text::draw_lines() const
{
int ofnt = fl_font();
int osz = fl_size();
fl_font(fnt.as_int(),fnt_sz);
fl_draw(lab.c_str(),point(0).x,point(0).y);
fl_font(ofnt,osz);
}
//------------------------------------------------------------------------------
Axis::Axis(Orientation d, Point xy, int length, int n, string lab) :
label(Point(0,0),lab)
{
if (length<0) error("bad axis length");
switch (d){
case Axis::x:
{
Shape::add(xy); // axis line
Shape::add(Point(xy.x+length,xy.y));
if (1<n) { // add notches
int dist = length/n;
int x = xy.x+dist;
for (int i = 0; i<n; ++i) {
notches.add(Point(x,xy.y),Point(x,xy.y-5));
x += dist;
}
}
// label under the line
label.move(length/3,xy.y+20);
break;
}
case Axis::y:
{
Shape::add(xy); // a y-axis goes up
Shape::add(Point(xy.x,xy.y-length));
if (1<n) { // add notches
int dist = length/n;
int y = xy.y-dist;
for (int i = 0; i<n; ++i) {
notches.add(Point(xy.x,y),Point(xy.x+5,y));
y -= dist;
}
}
// label at top
label.move(xy.x-10,xy.y-length-10);
break;
}
case Axis::z:
error("z axis not implemented");
}
}
//------------------------------------------------------------------------------
void Axis::draw_lines() const
{
Shape::draw_lines();
notches.draw(); // the notches may have a different color from the line
label.draw(); // the label may have a different color from the line
}
//------------------------------------------------------------------------------
void Axis::set_color(Color c)
{
Shape::set_color(c);
notches.set_color(c);
label.set_color(c);
}
//------------------------------------------------------------------------------
void Axis::move(int dx, int dy)
{
Shape::move(dx,dy);
notches.move(dx,dy);
label.move(dx,dy);
}
//------------------------------------------------------------------------------
Function::Function(Fct f, double r1, double r2, Point xy,
int count, double xscale, double yscale)
// graph f(x) for x in [r1:r2) using count line segments with (0,0) displayed at xy
// x coordinates are scaled by xscale and y coordinates scaled by yscale
{
if (r2-r1<=0) error("bad graphing range");
if (count <=0) error("non-positive graphing count");
double dist = (r2-r1)/count;
double r = r1;
for (int i = 0; i<count; ++i) {
add(Point(xy.x+int(r*xscale),xy.y-int(f(r)*yscale)));
r += dist;
}
}
//------------------------------------------------------------------------------
bool can_open(const string& s)
// check if a file named s exists and can be opened for reading
{
ifstream ff(s.c_str());
return ff;
}
//------------------------------------------------------------------------------
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
Suffix::Encoding get_encoding(const string& s)
{
struct SuffixMap
{
const char* extension;
Suffix::Encoding suffix;
};
static SuffixMap smap[] = {
{".jpg", Suffix::jpg},
{".jpeg", Suffix::jpg},
{".gif", Suffix::gif},
};
for (int i = 0, n = ARRAY_SIZE(smap); i < n; i++)
{
int len = strlen(smap[i].extension);
if (s.length() >= len && s.substr(s.length()-len, len) == smap[i].extension)
return smap[i].suffix;
}
return Suffix::none;
}
//------------------------------------------------------------------------------
// somewhat over-elaborate constructor
// because errors related to image files can be such a pain to debug
Image::Image(Point xy, string s, Suffix::Encoding e)
:w(0), h(0), fn(xy,"")
{
add(xy);
if (!can_open(s)) { // can we open s?
fn.set_label("cannot open \""+s+'\"');
p = new Bad_image(30,20); // the "error image"
return;
}
if (e == Suffix::none) e = get_encoding(s);
switch(e) { // check if it is a known encoding
case Suffix::jpg:
p = new Fl_JPEG_Image(s.c_str());
break;
case Suffix::gif:
p = new Fl_GIF_Image(s.c_str());
break;
default: // Unsupported image encoding
fn.set_label("unsupported file type \""+s+'\"');
p = new Bad_image(30,20); // the "error image"
}
}
//------------------------------------------------------------------------------
void Image::draw_lines() const
{
if (fn.label()!="") fn.draw_lines();
if (w&&h)
p->draw(point(0).x,point(0).y,w,h,cx,cy);
else
p->draw(point(0).x,point(0).y);
}
//------------------------------------------------------------------------------
} // of namespace Graph_lib
I hope someone could help me.
EDIT
/*
std_lib_facilities.h
*/
/*
simple "Programming: Principles and Practice using C++ (second edition)" course header to
be used for the first few weeks.
It provides the most common standard headers (in the global namespace)
and minimal exception/error support.
Students: please don't try to understand the details of headers just yet.
All will be explained. This header is primarily used so that you don't have
to understand every concept all at once.
By Chapter 10, you don't need this file and after Chapter 21, you'll understand it
Revised April 25, 2010: simple_error() added
Revised November 25 2013: remove support for pre-C++11 compilers, use C++11: <chrono>
Revised November 28 2013: add a few container algorithms
Revised June 8 2014: added #ifndef to workaround Microsoft C++11 weakness
*/
#ifndef H112
#define H112 251113L
#include<iostream>
#include<iomanip>
#include<fstream>
#include<sstream>
#include<cmath>
#include<cstdlib>
#include<string>
#include<list>
#include <forward_list>
#include<vector>
#include<unordered_map>
#include<algorithm>
#include <array>
#include <regex>
#include<random>
#include<stdexcept>
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
typedef long Unicode;
//------------------------------------------------------------------------------
using namespace std;
template<class T> string to_string(const T& t)
{
ostringstream os;
os << t;
return os.str();
}
struct Range_error : out_of_range { // enhanced vector range error reporting
int index;
Range_error(int i) :out_of_range("Range error: "+to_string(i)), index(i) { }
};
// trivially range-checked vector (no iterator checking):
template< class T> struct Vector : public std::vector<T> {
using size_type = typename std::vector<T>::size_type;
#ifdef _MSC_VER
// microsoft doesn't yet support C++11 inheriting constructors
Vector() { }
explicit Vector(size_type n) :std::vector<T>(n) {}
Vector(size_type n, const T& v) :std::vector<T>(n,v) {}
template <class I>
Vector(I first, I last) : std::vector<T>(first, last) {}
Vector(initializer_list<T> list) : std::vector<T>(list) {}
#else
using std::vector<T>::vector; // inheriting constructor
#endif
T& operator[](unsigned int i) // rather than return at(i);
{
if (i<0||this->size()<=i) throw Range_error(i);
return std::vector<T>::operator[](i);
}
const T& operator[](unsigned int i) const
{
if (i<0||this->size()<=i) throw Range_error(i);
return std::vector<T>::operator[](i);
}
};
// disgusting macro hack to get a range checked vector:
#define vector Vector
// trivially range-checked string (no iterator checking):
struct String : std::string {
using size_type = std::string::size_type;
// using string::string;
char& operator[](unsigned int i) // rather than return at(i);
{
if (i<0||size()<=i) throw Range_error(i);
return std::string::operator[](i);
}
const char& operator[](unsigned int i) const
{
if (i<0||size()<=i) throw Range_error(i);
return std::string::operator[](i);
}
};
namespace std {
template<> struct hash<String>
{
size_t operator()(const String& s) const
{
return hash<std::string>()(s);
}
};
} // of namespace std
struct Exit : runtime_error {
Exit(): runtime_error("Exit") {}
};
// error() simply disguises throws:
inline void error(const string& s)
{
throw runtime_error(s);
}
inline void error(const string& s, const string& s2)
{
error(s+s2);
}
inline void error(const string& s, int i)
{
ostringstream os;
os << s <<": " << i;
error(os.str());
}
template<class T> char* as_bytes(T& i) // needed for binary I/O
{
void* addr = &i; // get the address of the first byte
// of memory used to store the object
return static_cast<char*>(addr); // treat that memory as bytes
}
inline void keep_window_open()
{
cin.clear();
cout << "Please enter a character to exit\n";
char ch;
cin >> ch;
return;
}
inline void keep_window_open(string s)
{
if (s=="") return;
cin.clear();
cin.ignore(120,'\n');
for (;;) {
cout << "Please enter " << s << " to exit\n";
string ss;
while (cin >> ss && ss!=s)
cout << "Please enter " << s << " to exit\n";
return;
}
}
// error function to be used (only) until error() is introduced in Chapter 5:
inline void simple_error(string s) // write ``error: s and exit program
{
cerr << "error: " << s << '\n';
keep_window_open(); // for some Windows environments
exit(1);
}
// make std::min() and std::max() accessible on systems with antisocial macros:
#undef min
#undef max
// run-time checked narrowing cast (type conversion). See ???.
template<class R, class A> R narrow_cast(const A& a)
{
R r = R(a);
if (A(r)!=a) error(string("info loss"));
return r;
}
// random number generators. See 24.7.
inline int randint(int min, int max) { static default_random_engine ran; return uniform_int_distribution<>{min, max}(ran); }
inline int randint(int max) { return randint(0, max); }
//inline double sqrt(int x) { return sqrt(double(x)); } // to match C++0x
// container algorithms. See 21.9.
template<typename C>
using Value_type = typename C::value_type;
template<typename C>
using Iterator = typename C::iterator;
template<typename C>
// requires Container<C>()
void sort(C& c)
{
std::sort(c.begin(), c.end());
}
template<typename C, typename Pred>
// requires Container<C>() && Binary_Predicate<Value_type<C>>()
void sort(C& c, Pred p)
{
std::sort(c.begin(), c.end(), p);
}
template<typename C, typename Val>
// requires Container<C>() && Equality_comparable<C,Val>()
Iterator<C> find(C& c, Val v)
{
return std::find(c.begin(), c.end(), v);
}
template<typename C, typename Pred>
// requires Container<C>() && Predicate<Pred,Value_type<C>>()
Iterator<C> find_if(C& c, Pred p)
{
return std::find_if(c.begin(), c.end(), p);
}
#endif //H112
Error C2440 Build 'return': cannot convert from 'std::ifstream' to 'bool' Win32Project1 C:\Users\Leonardo\Documents\Visual Studio 2015\Projects\Win32Project1\Win32Project1\Graph.cpp 371
I suspect you're running into a breaking change introduced by C++11. The code appears to be this:
bool can_open(const string& s)
// check if a file named s exists and can be opened for reading
{
ifstream ff(s.c_str());
return ff;
}
The code used to work by using operator void * to cast ff from an istream to a void * which could then be cast to a bool. If the ifstream was bad the cast to void * would result in a null pointer which in turn would be treated as bool false.
But C++11 introduces an explicit operator bool() for streams (and gets rid of the void * operator?). Since the bool operator is explicit the operator won't be used for an implicit cast.
To get can_open to compile with the C++11 version of streams you need to make the cast explicit:
bool can_open(const string& s)
// check if a file named s exists and can be opened for reading
{
ifstream ff(s.c_str());
return (bool)ff;
}
However that change will break the code if it is compiled with a pre-C++11 version of streams.
i am having problem getting the correct object out from the lines
Cell cF = fList[rand() % fList.size()];//choose random frontier cell
Cell cl = inList[rand() % inList.size()];//choose random in cell
whenever i debug using visual studio, i can see a Cell will have it's members added due to the method pushToPrimsFrontierList(Cell& c), however when i try to get the object from the inList or fList, it seems like im not getting that same object reference, because its neighbour list is 0 again.what is happening here?
You can see from the image that during the first iteration a startCell is added to the inList, so when im accessing it it will return me only that one object, however that is not the case, it seems like my object is not even push_backed to the inList vector.
#ifndef __CELL_H_
#define __CELL_H_
#include <vector>
using namespace std;
class Cell{
private:
int x, y, val; // cell co-ordinate
bool visited;
void setX(int);
void setY(int);
vector<Cell> neighbourList;
public:
Cell();
Cell(int,int);
int getX();
int getY();
int getVal();
vector<Cell>& getNeighbourList();
void pushToNeighbourList(Cell&);
void setVal(int);
void setVisited(bool);
bool IsVisited();
bool equals(Cell&);
};
#endif
#ifndef GENMAZE_H
#define GENMAZE_H
#include <vector>
#include <iostream>
#include "cell.h"
using namespace std;
class GenMaze{
private:
int rows;
int cols;
int gridSize;
vector<vector<Cell>> mazeGrid;
vector<Cell> inList;
vector<Cell> fList;
public:
GenMaze(int,int);
void setRows(int);
void setCols(int);
void setGridSize(int);
int getGridSize();
int getRows();
int getCols();
vector<vector<Cell>>& getMazeGrid();
void setMazeGrid(vector<vector<Cell>>);
void setValAt(int,int, Cell);
void printMazeCoords();
void printMazeValue();
bool isOddBlock(int,int);
void Prims();
void pushToPrimsFrontierList(Cell&);
void printCell(Cell&);
void makePath(Cell&, Cell&);
void removeFromfList(Cell&);
};
#endif
void GenMaze::Prims(){
Cell startCell = mazeGrid[1][1];//startCell
inList.push_back(startCell);
pushToPrimsFrontierList(startCell);
int randomFrontier= 0;
int randomIn = 0;
while (!fList.empty()){
cout<< "e";
Cell cF = fList[rand() % fList.size()];//choose random frontier cell
Cell cl = inList[rand() % inList.size()];//choose random in cell
for (vector<Cell>::size_type i = 0; i != cl.getNeighbourList().size(); i++) {
if (cl.getNeighbourList()[i].equals(cF)){
inList.push_back(cF);
pushToPrimsFrontierList(cF);
makePath(cl, cF);
removeFromfList(cF);
}
}
}
}
void GenMaze::removeFromfList(Cell& c){
for (vector<Cell>::size_type i = 0; i != fList.size(); i++) {
if (fList[i].equals(c)){
fList.erase(fList.begin() + i);
}
}
}
void GenMaze::makePath(Cell& from, Cell& to){
cout << "making path";
//on top
if ((from.getX() - 2 == to.getX()) & (from.getY() == to.getY())){
mazeGrid[from.getX() - 1][from.getY()].setVal(0);
}
//on right
if ((from.getX() == to.getX()) & (from.getY() + 2 == to.getY())){
mazeGrid[to.getX()][from.getY() - 1].setVal(0);
}
//on bottom
if ((from.getX() + 2 == to.getX()) & (from.getY() == to.getY())){
mazeGrid[from.getX() + 1][from.getY()].setVal(0);
}
//on left
if ((from.getX() == to.getX()) & (from.getY() - 2 == to.getY())){
mazeGrid[from.getX()][from.getY() - 1].setVal(0);
}
}
void GenMaze::printCell(Cell& c){
cout << "(" << c.getX() << "," << c.getY() << ")";
}
void GenMaze::pushToPrimsFrontierList(Cell& c){
//push all Cells around the given Cell c, into the frontier list.
if (!(c.getX() - 2 < 0)){
Cell topCell = mazeGrid[c.getX() - 2][c.getY()];
fList.push_back(topCell);
c.pushToNeighbourList(topCell);
}
if (!(c.getY() - 2 < 0)){
Cell leftCell = mazeGrid[c.getX()][c.getY() - 2];
fList.push_back(leftCell);
c.pushToNeighbourList(leftCell);
}
if (!(c.getY() + 2 > getCols() - 1)){
Cell rightCell = mazeGrid[c.getX()][c.getY() + 2];
fList.push_back(rightCell);
c.pushToNeighbourList(rightCell);
}
if (!(c.getX() + 2 > getRows() - 1)){
Cell bottomCell = mazeGrid[c.getX() + 2][c.getY()];
fList.push_back(bottomCell);
c.pushToNeighbourList(bottomCell);
}
}
To the std::vector as pretty much to every STL collection you cannot put the reference to the object. If you do:
Cell c;
std::vector<Cell> myvector1;
std::vector<Cell> myvector2;
myvector1.push_back(c);
myvector2.push_back(c);
When you try to modify c in myvector1 the value won't be propagated to myvector2. This is because push_back adds the element by value not by reference. If you need the real reference to some object you should create collection of pointers to elements and the code should rather look like this:
Cell *c = new Cell;
std::vector<Cell*> myvector1;
std::vector<Cell*> myvector2;
myvector1.push_back(c);
myvector2.push_back(c);
Now when you want to modify element beneith c you just do:
myvector1[indexofc]->somecellfield = othervalue
Hey so I have three 2 classes: Screen, and Sprite : that depend on each other so if i define one i can not implement the second class to be defined into it as it has not been definded yet! Is there any simple solution to this that does not require a lot of re-coding?
Heres each class definition (Map is also a class):
the Sprite Class: move() wont work without the Screen Class definition....
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Sprite Class ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Sprite
{
public:
///////////////////// Get and SET all the privates ///////
Sprite(){};
Sprite(string a_name, char a_symbol, float a_health){
_name = a_name;
_symbol = a_symbol;
_health = a_health;};
char get_symbol() {return _symbol;};
void set_symbol(char _sym) {_symbol = _sym;};
float get_health() {return _health;};
void set_health(float _numb) {_health = _numb;};
void add_health (float _numb) {_health += _numb;};
string get_name() {return _name;};
string set_name(string _aName) {_name = _aName;};
int* get_location(){return _location;};
void set_location(int X, int Y) {
_location[0] = X;
_location[1] = Y;};
//////////////////////////////// Move ////////////
// WONT WORK UNTIL UNLESS SCEEN CLASS IS DEFINED BEFORE IT
bool move(Screen screen,int X, int Y)
{
bool OK = true;
////////////////////// check whats already there /////
char newLoc = screen.get_contents(_location[1]+Y,_location[0]+X);
if (newLoc == '|' || '/' || '_' || '=' || 'X' || 'x' )
OK = false;
if (OK == true)
{
_location[0] += X;
_location[1] += Y;
return true;
}
else
return false;
};
private:
string _name;
char _symbol;
float _health;
int _location[2];
};
And the screen class: (the sprite overloaded insert function won't work without Sprite def.
///////////////////////// SCREEN CLASS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Screen
{
private:
/////////////////////////////////////////// Screen Variables ///////////////
string _name;
vector <string> _contents;
public:
Screen(string name){_name = name;
_contents.resize(24);};
~Screen(){};
//////////////////////////////////////////// Get contents ///////////////////////////
string get_contents(int Y) {return _contents[Y];};
char get_contents(int X, int Y) {return _contents[Y][X];};
//////////////////////////////////////////// Display (1 FPS) ///////////////////////////
void Display(int numbRefreshes) //
{ //
for(int t = 0; numbRefreshes > t;) //
{ //
UL_2 = GetTickCount()+overSec-UL_1; // Get Time in Milliseconds since start //
// GATE TO GETTING INTO THE DISPLAY FUNCTION (every 1 sec)
if (UL_2 >= 1000) //
{ //
overSec += 1000 - UL_2; //
// Wait another second before update (1000 milliseconds) //
UL_1+=1000; //
UL_3+= 1; //
char UL_3_string[6]; //
itoa(UL_3, UL_3_string, 10); //
// Tell the for loop 1 render is complete (/////)
t+=1; // (///)
// Update the time counter (/)
int B = sizeof(UL_3_string); // |
for(int I = 0; I < sizeof(UL_3_string); I++)
Screen::Insert(UL_3_string[I], 38+I, 22);
/////////////////// Draw each line of the Screen
for (unsigned int I = 0; I < _contents.size(); I++)
{
cout << _contents[I];
}
//////////////// draw any empty lines NOT WORKING WTF???????
for(int emptyLines = 24-_contents.size(); emptyLines > 0; emptyLines--)
{
cout << endl;
}
}
}
};
/////////////////////////////////////////// Insert ////////////////////////
/////////////////// map
bool Insert(Map& _map)
{
for (unsigned int I = 0; I<_map.getContents().size();I++)
{
_contents[I] = _map.getContents()[I];
}
return true;
};
/////////////////// string
bool Insert(string _string, int Y)
{
_contents[Y] = _string;
return true;
};
///////////////////// char
bool Insert(char _char, int X, int Y)
{
_contents[Y][X] = _char;
return true;
};
//////////////////// sprite
bool Insert(Sprite& _sprite)
{
_contents[_sprite.get_location()[0]][_sprite.get_location()[1]] = _sprite.get_symbol();
};
};
Screen theScreen("theScreen");
Thanks for any help!! =)
I would redesign - I can't see why a sprite needs to know about a screen. And what is this:
if (newLoc == '|' || '/' || '_' || '=' || 'X' || 'x' )
supposed to be doing? That is not how you test things in C++.
Other problems - don't pass strings by value, pass them as const references, and don't begin names with underscores.
First, split your classes to .h files and .cpp files, then use forward declaration (google for it, I can't post link). There are a few other errors in your code, you might like Bjarne Stroustrup's book "Language c++"