Hello, i got stuck again. When i read the text it all makes sense,
but putting it all to use is a different story.:zzz: Accelerated C++ excersise 15-4:

15-4. Add an operation to reframe a Picture, which changes the frame characters. The operation should change all of the frames in the interior picture.

Where and how should i store the characters for the frame? Ive managed to change the outer frame characters, but not the inner ones as they are made from different objects that hold different characters, or something like that.:P This code however doesnt contain the solutions i have tried out.

I would be very grateful if anyone could help me out here, i hate to leave things undone, especially if its something i couldnt understand.

#ifndef GUARD_Picture_info
#define GUARD_Picture_info

#include "stdafx.h"
#include "Ptr.hpp" // handle class
#include <string>
#include <vector>
#include <iostream>

class Picture;
class Pic_base {
	friend std::ostream& operator<<(std::ostream&, const Picture&);
	friend class Frame_Pic;
	friend class HCat_Pic;
	friend class VCat_Pic;
	friend class String_Pic;
	// no public interface
	typedef std::vector<std::string>::size_type ht_sz;
	typedef std::string::size_type wd_sz;
	// this class is an abstract base class
	virtual wd_sz width() const = 0;
	virtual ht_sz height() const = 0;
	virtual void display(std::ostream&, ht_sz, bool) const = 0;
	protected:
	static void pad(std::ostream& os, wd_sz beg, wd_sz end) {
		while (beg != end) {
		os << " ";
		++beg;
		}
	}
};
class Picture {
	friend std::ostream& operator<<(std::ostream&, const Picture&);
	friend Picture frame(const Picture&);
	friend Picture hcat(const Picture&, const Picture&);
	friend Picture vcat(const Picture&, const Picture&);
	public:
	Picture(const std::vector<std::string>& =
	std::vector<std::string>());
	private:
	Picture(Pic_base* ptr): p(ptr) { }
	Ptr<Pic_base> p;
};

class String_Pic: public Pic_base {
	friend class Picture;
	std::vector<std::string> data;
	String_Pic(const std::vector<std::string>& v): data(v) { }
	ht_sz height() const { return data.size(); }
	wd_sz width() const;
	void display(std::ostream&, ht_sz, bool) const;
};
class Frame_Pic: public Pic_base {
	friend Picture frame(const Picture&);
	Ptr<Pic_base> p;
	Frame_Pic(const Ptr<Pic_base>& pic): p(pic) { }
	wd_sz width() const { return p->width() + 4; }
	ht_sz height() const { return p->height() + 4; }
	void display(std::ostream&, ht_sz, bool) const;
};
class VCat_Pic: public Pic_base {
	friend Picture vcat(const Picture&, const Picture&);
	Ptr<Pic_base> top, bottom;
	VCat_Pic(const Ptr<Pic_base>& t, const Ptr<Pic_base>& b):
	top(t), bottom(b) { }
	wd_sz width() const { return std::max(top->width(), bottom->width()); }
	ht_sz height() const { return top->height() + bottom->height(); }
	void display(std::ostream&, ht_sz, bool) const;
};
class HCat_Pic: public Pic_base {
	friend Picture hcat(const Picture&, const Picture&);
	Ptr<Pic_base> left, right;
	HCat_Pic(const Ptr<Pic_base>& l, const Ptr<Pic_base>& r):
	left(l), right(r) { }
	wd_sz width() const { return left->width() + right->width(); }
	ht_sz height() const { return std::max(left->height(), right->height()); }
	void display(std::ostream&, ht_sz, bool) const;
};

//------------------------------ "Picture.cpp"

using std::ostream; using std::endl; using std::max; using std::string;
Picture::Picture(const std::vector<std::string>& v):p(new String_Pic(v)) { }
Picture frame(const Picture& pic) { return new Frame_Pic(pic.p); }
Picture vcat(const Picture& t, const Picture& b) { return new VCat_Pic(t.p, b.p); }
Picture hcat(const Picture& l, const Picture& r) { return new HCat_Pic(l.p, r.p); }

ostream& operator<<(ostream& os, const Picture& picture)
{
	const Pic_base::ht_sz ht = picture.p->height();
	for (Pic_base::ht_sz i = 0; i != ht; ++i) {
		picture.p->display(os, i, false);
		os << endl;
	}
return os;
};

Pic_base::wd_sz String_Pic::width() const {
	Pic_base::wd_sz n = 0;
	for (Pic_base::ht_sz i = 0; i != data.size(); ++i)
	n = max(n, data[i].size());
return n;
}

void String_Pic::display(ostream& os, ht_sz row, bool do_pad) const
{
	wd_sz start = 0;
	// write the row if we're still in range
	if (row < height()) {
	os << data[row];
	start = data[row].size();
}
	// pad the output if necessary
	if (do_pad)
	pad(os, start, width());
}

void VCat_Pic::display(ostream& os, ht_sz row, bool do_pad) const
{
	wd_sz w = 0;
	if (row < top->height()) {
		// we are in the top subpicture
		top->display(os, row, do_pad);
		w = top->width();
	} else if (row < height()) {
		// we are in the bottom subpicture
		bottom->display(os, row - top->height(), do_pad);
		w = bottom->width();
	}
	if (do_pad)
	pad(os, w, width());
}

void HCat_Pic::display(ostream& os, ht_sz row, bool do_pad) const
{
	left->display(os, row, do_pad || row < right->height());
	right->display(os, row, do_pad);
}

void Frame_Pic::display(ostream& os, ht_sz row, bool do_pad) const
{
	if (row >= height()) {
		// out of range
		if (do_pad)
		pad(os, 0, width());
	} else {
		if (row == 0 || row == height() - 1) {
			// top or bottom row
			os << string(width(), '*');
	} else if (row == 1 || row == height() - 2) {
		// second from top or bottom row
		os << "*";
		pad(os, 1, width() - 1);
		os << "*";
	} else {
		// interior row
		os << "* ";
		p->display(os, row - 2, true);
		os << " *";
	}
	}
}

#endif

Dani AI

Generated

The reason the inner frames stay the same is that each frame is a separate Frame_Pic node holding its own characters. Changing only the outer Frame_Pic (or hard-coding a new character in its display) won't touch nested Frame_Pic objects. The usual fix is to add a small, virtual "reframe" operation that rebuilds the picture tree, replacing every Frame_Pic node with a new Frame_Pic that carries the requested characters. That way nested frames are updated too. (Good call by to check the book sources; they describe this style of solution. Also, note 's point about clearer titles for future threads.)

Minimal, practical plan

  • Add storage for the frame characters in Frame_Pic (for example: corner, horizontal, vertical) and update Frame_Pic's constructor and display code to use those members instead of a hard-coded '*'.
  • Add a virtual API to Pic_base, e.g. a const method that returns a new smart-pointer to a Pic_base rebuilt with new frame chars.
  • Implement that virtual method in each derived class: String_Pic returns a copy, HCat_Pic/VCat_Pic call reframe on their children and return a new concatenation node, Frame_Pic calls reframe on its interior and constructs a new Frame_Pic with the new characters.
  • Provide a convenient free function (or Picture member) that calls the root p->reframe(...) and wraps the returned node into a Picture.

Small reference signatures (illustrative only)

virtual Ptr<Pic_base> reframe(char corner, char hor, char ver) const = 0;

Ptr<Pic_base> String_Pic::reframe(...) const;   // returns a copy
Ptr<Pic_base> HCat_Pic::reframe(...) const;     // rebuilds children
Ptr<Pic_base> Frame_Pic::reframe(...) const;    // rebuilds interior and returns new Frame_Pic

Picture reframe(const Picture& pic, char corner, char hor, char ver);

Caveats and tests

  • Keep reframe const and return new nodes so pictures remain value-like; that avoids aliasing surprises with your Ptr handle.
  • Remember to update any factory frame(...) overloads so you can still create frames with defaults.
  • Test with several nested frames (2–4 levels) and mixed hcat/vcat to confirm all frames use the new characters.

Recommended Answers

All 2 Replies

Andreas5,

Please use an informative title for your threads. This will attract the correct set of readers.

David

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.