问题
To make it clear, I post the code of "how to use" first:
AB.h:
#include <vector>
#include <list>
// execpt they both have children, A & B share nothing in common,
// even children use different containers
struct A {
int num;
std::vector<A> children;
};
struct B {
std::string s;
float n;
std::list<B> children;
};
main.cpp:
#include "Node.h"
// handle A & B with the common interface Node
// do not use template, since in practice, print() may be a large library
void print(Node& n) {
printf("%s\n", n.tell().c_str());
for (auto& c : n.children()) {
print(c);
}
}
////////////////////////////////////////////////////////////////////////////////
int main() {
A a { .num = 3, .children = { { .num = 7 }, { .num = 8 } } };
NodeA na(&a); // wrap A with NodeA adapter
print(na);
B b {
.s = "Will", .n = 1,
.children = { { .s = "Ryu", .n = 5 }, { .s = "Ken", .n = 8 } }
};
NodeB nb(&b); // wrap B with NodeB adapter
print(nb);
}
Compile & run:
WilldeMacBook-Pro:testNodes Will$ cc -std=c++11 -lc++ main.cpp Node.cpp
WilldeMacBook-Pro:testNodes Will$ ./a.out
3
7
8
Will1.000000
Ryu5.000000
Ken8.000000
It really worked, handle distinct A & B in a universal way, little runtime overhead, and... straightforward & simple... until you see the Node.h:
#include <memory>
#include <sstream>
#include "AB.h"
////////////////////////////////////////////////////////////////////////////////
struct Node;
struct Nodes;
struct NodeIter;
struct NodeIterPtr;
struct Node { // the only interface i care about, other three are "have-to" ones
virtual std::string tell() = 0;
virtual Nodes& children() = 0;
};
struct Nodes {
virtual NodeIterPtr begin() = 0;
virtual NodeIterPtr end() = 0;
};
struct NodeIter {
virtual ~NodeIter() {}
virtual Node& operator*() = 0;
virtual NodeIter& operator++() = 0;
virtual bool operator!=(const NodeIter& other) = 0;
};
struct NodeIterPtr {
NodeIterPtr(NodeIter* p) : ptr(p) {}
Node& operator*() { return (*ptr).operator*(); }
NodeIter& operator++() { return (*ptr).operator++(); }
bool operator!=(const NodeIterPtr& other) {
return (*ptr).operator!=(*other.ptr);
}
private:
std::shared_ptr<NodeIter> ptr;
};
////////////////////////////////////////////////////////////////////////////////
// adapter for A
struct NodeA : public Node {
NodeA(A* p) : ptr(p) {}
std::string tell() { return std::to_string(ptr->num); }
Nodes& children();
private:
A* ptr;
};
struct NodesA : public Nodes {
NodesA(std::vector<A>* ns) : nodes(ns) {}
NodeIterPtr begin();
NodeIterPtr end();
private:
std::vector<A>* nodes;
};
struct NodeIterA : public NodeIter {
NodeIterA(A* p) : ptr(p) {}
Node& operator*() { static NodeA a(nullptr); a = NodeA(ptr); return a; }
NodeIter& operator++() { ptr++; return *this; }
bool operator!=(const NodeIter& other) {
auto realOther = (const NodeIterA&)(other);
return ptr != realOther.ptr;
}
private:
A* ptr;
};
////////////////////////////////////////////////////////////////////////////////
// adapter for B
struct NodeB : public Node {
NodeB(B* p) : ptr(p) {}
std::string tell() { return ptr->s + std::to_string(ptr->n); }
Nodes& children();
private:
B* ptr;
};
struct NodesB : public Nodes {
NodesB(std::list<B>* ns) : nodes(ns) {}
NodeIterPtr begin();
NodeIterPtr end();
private:
std::list<B>* nodes;
};
struct NodeIterB : public NodeIter {
NodeIterB(std::list<B>::iterator i) : iter(i) {}
Node& operator*() { static NodeB b(nullptr); b = NodeB(&(*iter)); return b; }
NodeIter& operator++() { iter++; return *this; }
bool operator!=(const NodeIter& other) {
auto realOther = (const NodeIterB&)(other);
return iter != realOther.iter;
}
private:
std::list<B>::iterator iter;
};
And the Node.cpp:
#include "Node.h"
Nodes& NodeA::children() {
static NodesA c(nullptr);
c = NodesA(&ptr->children);
return c;
}
NodeIterPtr NodesA::begin() {
auto iter = new NodeIterA(&(*nodes->begin()));
return NodeIterPtr(iter);
}
NodeIterPtr NodesA::end() {
auto iter = new NodeIterA(&(*nodes->end()));
return NodeIterPtr(iter);
}
////////////////////////////////////////////////////////////////////////////////
Nodes& NodeB::children() {
static NodesB c(nullptr);
c = NodesB(&ptr->children);
return c;
}
NodeIterPtr NodesB::begin() {
auto iter = new NodeIterB(nodes->begin());
return NodeIterPtr(iter);
}
NodeIterPtr NodesB::end() {
auto iter = new NodeIterB(nodes->end());
return NodeIterPtr(iter);
}
I really think it's too complicated, to achieve such a simple goal. (If A & B have more than one container member, such as students/employees/tickets, That would be like hell.) Were C++ designed to do so? I even come to some bizarre ideas of returning a base class reference instead of a base class pointer. Anyway, what I want to ask is, is there a way to simplify above code? As the saying goes, "talk is cheap, show me the code", before talking any philosophy, I prefer to see code that can be directly compiled & run, same output, same simple way of usage, but with reduced complexity of Node.h/cpp. Thanks.
来源:https://stackoverflow.com/questions/61812074/how-to-simplify-the-adapter-scheme