#include <string>
#include <memory>
#include <vector>
#include <iostream>
#include <functional>
class File;
class Directory;
class Visitor {
public:
virtual ~Visitor() {}
virtual void visit(std::shared_ptr<File>) = 0;
virtual void visit(std::shared_ptr<Directory>) = 0;
virtual void leave(std::shared_ptr<Directory>) = 0;
};
class Entry {
public:
virtual ~Entry() {};
virtual std::string getName() const = 0;
virtual size_t getSize() = 0;
virtual void accept(Visitor &) = 0;
};
class File : public Entry, public std::enable_shared_from_this<File> {
struct this_is_private {};
std::string name;
size_t size;
public:
File(this_is_private, std::string name, size_t size) : name(name), size(size) {}
std::string getName() const override {return name;}
size_t getSize() override {return size;}
void accept(Visitor &v) override {v.visit(shared_from_this());}
static std::shared_ptr<File> create(std::string name, size_t size) {
return std::make_shared<File>(this_is_private(), name, size);
}
};
class Directory : public Entry, public std::enable_shared_from_this<Directory> {
struct this_is_private {};
std::string name;
std::vector<std::shared_ptr<Entry>> es;
public:
Directory(this_is_private, std::string name) : name(name) {}
std::string getName() const override {return name;}
size_t getSize() override {
size_t sum = 0;
for (auto &e : es) sum += e->getSize();
return sum;
}
void accept(Visitor &v) override {
v.visit(shared_from_this());
for (auto &e : es) e->accept(v);
v.leave(shared_from_this());
}
void add(std::shared_ptr<Entry> entry) {es.push_back(entry);}
static std::shared_ptr<Directory> create(std::string name) {
return std::make_shared<Directory>(this_is_private(), name);
}
};
// こっから↑は安定したクラス階層を想定
//////////////////////////////////////////////////////////////////
// こっから↓はVisitor派生クラスを作ることで「新しいオペレーションを簡単に追加できる」とのこと。
class SizeVisitor : public Visitor {
size_t size;
public:
SizeVisitor() : size(0) {};
void visit(std::shared_ptr<File> file) {size += file->getSize();}
void visit(std::shared_ptr<Directory>) {}
void leave(std::shared_ptr<Directory>) {}
size_t getTotalSize() {return size;}
};
class ListVisitor : public Visitor {
std::string cd;
public:
ListVisitor() : cd("") {}
void visit(std::shared_ptr<File> file) {
std::cout << cd << "/" << file->getName() << " (" << file->getSize() << ")" << std::endl;
}
void visit(std::shared_ptr<Directory> dir) {
std::cout << cd << "/" << dir->getName() << " (" << dir->getSize() << ")" << std::endl;
cd += "/" + dir->getName();
}
void leave(std::shared_ptr<Directory> dir) {
cd.resize(cd.length() - 1 - dir->getName().length());
}
};
class FunctionalVisitor : public Visitor {
std::function<void(std::shared_ptr<File>)> vf;
std::function<void(std::shared_ptr<Directory>)> vd, ld;
public:
FunctionalVisitor(
std::function<void(std::shared_ptr<File>)> vf, std::function<void(std::shared_ptr<Directory>)> vd, std::function<void(std::shared_ptr<Directory>)> ld
) : vf(vf), vd(vd), ld(ld) {}
void visit(std::shared_ptr<File> file) {vf(file);}
void visit(std::shared_ptr<Directory> dir) {vd(dir);}
void leave(std::shared_ptr<Directory> dir) {ld(dir);}
};
int main() {
auto a = Directory::create("a");
auto b = File::create("b.txt", 100);
auto c = File::create("c.txt", 200);
auto d = Directory::create("d");
auto e = File::create("e.txt", 300);
a->add(b);
a->add(c);
a->add(d);
d->add(e);
SizeVisitor sv;
a->accept(sv);
std::cout << "total size: " << sv.getTotalSize() << std::endl;
ListVisitor lv;
std::cout << "\nlist: " << std::endl;
a->accept(lv);
std::vector<std::string> dirs;
FunctionalVisitor fv(
[&dirs](std::shared_ptr<File> file) {
if (file->getName().find(".txt") != std::string::npos) {
for (auto &d : dirs) std::cout << "/" << d;
std::cout << "/" << file->getName() << std::endl;
}
}
, [&dirs](std::shared_ptr<Directory> dir) {dirs.push_back(dir->getName());}
, [&dirs](std::shared_ptr<Directory> dir) {dirs.pop_back();}
);
std::cout << "\n.txt: " << std::endl;
a->accept(fv);
return 0;
}
/*
・https://i...content-available-to-author-only...e.com/p3li2Y を元に若干の整理を行った
・make_shared<File>(this)の多重開放?を修正
std::enable_shared_from_thisを使ってJavaの参照型変数っぽい使用感を得た。
・struct this_is_private {};
これは単にコンストラクタを実質的にprivateにするためだけに使ってる
https://e...content-available-to-author-only...e.com/w/cpp/memory/enable_shared_from_this
https://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const
このへん参照されたし
*/