abstract:我希望在包含递归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&amp;)工作?
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;
}
答案 0 :(得分:2)
我遇到了类似的问题而我使用make_recursive_variant
然后我使用map<Key,Value>
作为变体而不是Node类。最后,我的节点类成为包含std::map
引用的实用程序函数。如:
typedef boost::make_recursive_variant<
std::map<std::string, boost::recursive_variant_>,
vector<int>,
vector<float>
>::type data_t;
// 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()(std::map<std::string, data_t>& tree) const
{
*os << endl;
for (auto& i : tree)
{
*os << prefix << i.first;
//you may want to change prefix and use new PrintData instead of *this
boost::apply_visitor(*this, i.second);
}
return *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;
}
};
class Node
{
public:
//implicit ctor for data_t conversion
Node(data_t& data) : data(data) { }
//...
private:
data_t& data;
};
编辑:我忘了提到你不再需要在Node类中使用apply_visitor,但我不确定initializer_list
ctor。它可能不适用于data_t
类型。
答案 1 :(得分:1)
声明函数,但不要在类中定义它。相反,等到你定义PrintData
之后再定义它,此时它将是一个完整的类型。
class PrintData;
class Node
{
public:
...
ostream& apply_visitor(const PrintData& fn) const;
...
};
class PrintData : public boost::static_visitor<ostream&>
{ ... };
inline ostream& Node::apply_visitor(const PrintData& fn) const
{
...
}