Boost logo

Boost :

From: Alexander Nasonov (alnsn_at_[hidden])
Date: 2002-08-31 07:04:18


First, I wrote in parallel post that new 'any' should be renamed to dynany
(or something like that). But in this post I still calls it 'any'. I hope
this name mess doesn't confuse you.

Alexander Nasonov wrote:
> My solution also is not perfect. It has (at least) one weakness: behaviour
> for non less comparable types. You can just forget to implement this
> operator< for some type X and then use X with for example set<any>. But
> wait, you can also missed a type while, for example, defining a visitor.
> And it's considered as your fault, not of designer of visitor library.
> It's the best that designer can give you and its behaviour is well defined
> and documented. The same, I think, can be applied to my proposal :)

Today I think that my yesterday solution is also evil! The root of all
problems is of course operator< for any. Take a look at this code:

  bool operator<(const any &, const any &);

  any a = get_some_any();
  if(a < 0)
  {
    // Hmm, what's inside 'a'?
    // May be negative int?
    // Or may be Enigma?
  }

Nor operator<, nor less specialization should be provided for an 'any'.
For operators == and != the story is different:

  template<typename T>
  bool operator==(const any & v1, const T & v2)
  {
    if(v1.type() != typeid(T))
      return false;
    return v2 == any_cast<const T &>(v1);
  }

  any a = get_some_any();
  if(a == 0)
  {
    // I know what is inside 'a'. It's int(0)
  }

Others operators can be expressed using the above operator==:

  template<typename T>
  bool operator==(const T & v1, const any & v2);

  template<typename T>
  bool operator!=(const any & v1, const T & v2);

  template<typename T>
  bool operator!=(const T & v1, const any & v2);

Operators == and != that take two anys are not presented because they
introduce a comparision problem for types that are not equal comparable.
One might think that those operators are not required because everyboby can
emulate them:

  struct X { /* ... */ };
  struct Y : X { /* ... */ };

bool foo()
{
  X x;
  any y = Y();

  // compare with x:
  if(const X * p = any_cast<const X *>(&y))
    return *p == x;
  return false;
}

This code has a problem when X doesn't provide polymorphic behavior to its
operator==. In this case only X subobject of Y is compared with x. This is
typical object slicing.
Basic requirements for type T that 'any' can store are:
1. T is value type.
2. 'any' stores value of type T as-is and never converts it implicitly into
another type.
Values are identified by its type and state. So, values of different types
are considered different (and no implicit conversion should be made to
change this situation). This is why I first check types in the body of
above operator==.
Associative containers for an 'any', I think, still desired. So, why not
define functor any_less (what a confusing name 'any', I can't stand it
any_more!:) and document its behavior. Somebody who really wants set of
anys can do that in this way:

  typedef std::set<any, any_less> my_any_set;

To avoid problems with comparing non-comparable types some support can be
provided:

  template<typename T>
  void insert(my_any_set & where, const T & what)
  {
    BOOST_STATIC_ASSERT(is_less_comparable<T>::value);
    where.insert(any(what));
  }

  void foo()
  {
    X x;
    my_any_set anys;
    insert(anys, x); // compiles ok only if x is less comparable
  }

Of course, somebody can write wrapper functions of all set members and put
those wrappers into some wrapper class:

class any_set
{
  set<any, any_less> set_;
public:
  // typedefs
  
  template<typename T>
  const_iterator find(const T & val)
  {
    BOOST_STATIC_ASSERT(is_less_comparable<T>::value);
    return set_.find(any(val));
  }

  // ...
};

--
Best regards,
Alexander Nasonov
e-mail account: alnsn
e-mail server:  mail.ru

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk