Boost logo

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