摘要:我希望在包含递归 boost::variant 对象的类中重载 apply_visitor() 方法。
在下面包含的代码中有方法:
template <typename T>
ostream& apply_visitor(const T& fn) const
我想为不同的访问者重载这个方法。像这样的东西:
ostream& apply_visitor(const PrintData& fn) const
但问题是 PrintData 类尚未完成(请参阅下面代码中的注释)。它是在 Node 类之后定义的。所以我有两个问题(除其他外——我欢迎对这段代码进行一般性批评,该代码正在建模我想投入生产的东西)。
1)有没有办法让 apply_visitor(PrintData&) 工作?
2)我可以重新排列(递归)变体,以便所有访问者方法都在 PrintData 中,并且我不必将 apply_visitor 添加到 Node 类吗?
/**
* compiled with gcc (tested with 4.7.2 on linux)
* requires the boost development headers to be discoverable
* by the compiler.
*
* g++ -otest-recursive-visit -std=c++11 test-recursive-visit.cpp
* ./test-recursive-visit
**/
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "boost/variant.hpp"
#include "boost/variant/recursive_wrapper.hpp"
#include "boost/variant/static_visitor.hpp"
using namespace std;
/// type name demangler (as implemented in g++). For other compilers,
/// we could add more #elif statements, but for now, just return
/// the mangled name.
#ifdef __GNUG__ /// compiler is g++
#include <cxxabi.h>
string type_demangle(const string& name)
{
int status;
char* res = abi::__cxa_demangle(
name.c_str(), NULL, NULL, &status);
string demangled_name((status==0) ? res : name);
free(res);
return demangled_name;
}
#else /// compiler is not g++
string type_demangle(const string& name) { return name; }
#endif
/// forward declaration of the Node class
/// (needed for recursive variant type)
class Node;
/// the actual recursive variant type
/// (typically hidden from the users)
typedef boost::variant<
boost::recursive_wrapper<Node>,
vector<int>,
vector<float>
> data_t;
// forward declaration for PrintData. See note below concerning
// Node::apply_visitor()
class PrintData;
/// this is the object users will see
/// for prototyping, the tree object is public
class Node
{
public:
/// sub-nodes are identified by unique strings
/// which point to one of the objects that data_t
/// can hold
map<string,data_t> tree;
/// constructor for a std::map object, passed to tree
Node(const initializer_list<pair<const string,data_t>>& l)
: tree(l)
{}
//
// INTERESTING PART OF THIS EXAMPLE IS HERE
//
// I tried to replace T& with PrintData& adding
// a forward declaration at the top but I get the
// errors:
//
// line 86:
// invalid use of incomplete type ‘const class PrintData’
// line 53:
// forward declaration of ‘const class PrintData’
//
/// This is called by boost::apply_visitor(Visitor(),Node)
//ostream& apply_visitor(const PrintData& fn) const
template <typename T>
ostream& apply_visitor(const T& fn) const
{
for (auto i : tree)
{
*fn.os << fn.prefix << i.first;
i.second.apply_visitor(fn);
}
return *fn.os;
}
};
/// the printing visitor to ostream object
class PrintData : public boost::static_visitor<ostream&>
{
public:
ostream* os;
string prefix;
/// keep a pointer to the ostream and keep
/// a prefix string which will hold and "indent"
/// which is something like " " for every level
/// of recursion
PrintData(ostream& out_stream, const string& prefix_str="")
: os(&out_stream)
, prefix(prefix_str)
{}
/// recurse into the tree, adding indent characters to prefix
ostream& operator()(Node& n) const
{
*os << endl;
n.apply_visitor(PrintData(*os, prefix+" "));
*os;
}
/// actual data types that we want to print out
template <typename T>
ostream& operator()(const vector<T>& d) const
{
*os << " (vector<" << type_demangle(typeid(T).name()) << ">):";
for (T i : d)
{
*os << " " << i;
}
*os << endl;
return *os;
}
};
/// convenience operator to allow: cout << node;
ostream& operator<<(ostream& os, const Node& n)
{
return boost::apply_visitor(PrintData(os), n);
}
int main()
{
/// hooray for initialization lists!!!
Node n {
{"X", Node{
{"a", vector<int>{1,2,3}},
{"b", vector<float>{2,3,4}}
}},
{"Y", vector<int>{3,4,5}},
{"Z", Node{
{"A", Node{
{"c", vector<float>{4,5,6}}
}}
}}
};
/**
applying PrintData to n prints out the following:
X
a (vector<int>): 1 2 3
b (vector<float>): 2 3 4
Y (vector<int>): 3 4 5
Z
A
c (vector<float>): 4 5 6
**/
cout << n;
}