|
Boost : |
From: Alexander Nasonov (alnsn_at_[hidden])
Date: 2002-04-22 07:34:15
"Peter Dimov" <pdimov_at_[hidden]> wrote in message
news:005001c1e9e4$6b6c9ce0$1d00a8c0_at_pdimov2...
> The best I can do is:
>
> template<class L, class F> void visit(boost::any * p, F f);
>
> int main()
> {
> typedef list<int, double, std::string>::type L;
> boost::any a = std::string("test.");
> visit<L>(&a, std::cout << boost::lambda::_1 << '\n');
> }
>
Nice :) Passing lambda function as visitor is really cool! Unfortunately,
(a) L is required when calling visit function and (b) if-else-if chain with
any_cast calls is not as speedy as it could be when L is lengthy typelist.
IMHO, in most cases types to be visited are known a priori even for 'any'
type. If this is the case then visitor class can be passed as 'any' template
parameter. Here is a materialization of my thoughts:
typedef TYPELIST_3(int, double, std::string) L;
typedef VisitorGenerator<L> AbstractVisitor; // see note 1
typedef any<AbstractVisitor> VisitedAny;
template<class Visitor, class Func>
void visit(any<Visitor> * pa, Func f)
{
FunctorToVisitor<Visitor, Func> v(f); // see note 2
Visitor * pv = &v;
pa->accept(pv); // see note 3
}
int main()
{
VisitedAny a = std::string("test."); // see note 4
visit(&a, std::cout << boost::lambda::_1 << '\n');
}
Notes:
1. VisitorGenerator is class that has pure virtual function 'visit' for
every type from L:
template<class L>
struct VisitorGenerator;
template<>
struct VisitorGenerator<TYPELIST_3(int, double, std::string)>
{
typedef TYPELIST_3(int, double, std::string) VisitedTypes;
virtual void visit(int) = 0;
virtual void visit(double) = 0;
virtual void visit(std::string) = 0;
};
2. FunctorToVisitor is realization of AbstractVisitor:
template<class Visitor, class Func>
FunctorToVisitor;
template<class Func>
struct FunctorToVisitor<
VisitorGenerator<TYPELIST_3(int, double, std::string)>,
Func> : public VisitorGenerator<TYPELIST_3(int, double, std::string)>
{
Func func_;
FunctorToVisitor(Func func) : func_(func) {}
void visit(int val) { func_(val); }
void visit(double val) { func_(val); }
void visit(std::string val) { func_(val); }
};
3. The any<AbstractVisitor>::accept function just delegates to holder's
accept:
template<class Visitor>
struct any
{
holder_base<Visitor> *ptr_; // real object is stored there
void accept(Visitor * pv)
{
if(ptr_) ptr_->accept(pv);
// see note 4 for zero ptr_ handling
}
// ...
};
4. Passing value of type not listed in VisitedTypes requires special
consideration. Possible solutions: (a) compile-time error, (b) runtime error
and (c) default handler. Solution (a) is not good besause 'any' class is
designed to hold _any_ type meeting ValueType concept. I personally prefer
runtime exception when there is no default handler and I would like default
handler to be pure virtual function
AbstractVisitor::visit(any<AbstractVisitor> &).
Resulting algorithm is quite simple:
if 'any' object stores object of type listed in VisitedTypes then visit for
stored type is called;
else if AbstractVisitor has member function visit(any<AbstractVisitor> &)
then call it;
else throw cannot_visit runtime error.
The same algorithm can be applied for empty 'any' object.
For example, this replacment of previous main function should throw
cannot_visit:
int main()
{
try
{
VisitedAny a = 'x'; // char not in TYPELIST_(int, double, std::string)
visit(&a, std::cout << boost::lambda::_1 << '\n'); // throw cannot_visit
} catch(const cannot_visit &) {}
}
-- Alexander Nasonov
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk