17
\$\begingroup\$

I needed a boost.any look-a-like, that could handle a std::unique_ptr. I came up with this. Please provide some C++11 criticism.

#ifndef ANY_HPP
# define ANY_HPP

#include <cassert>

#include <stdexcept>

#include <typeinfo>

#include <type_traits> 

#include <utility>

namespace generic
{

class any
{
public:
  any() noexcept : content(nullptr) { }

  any(any const& other)
    : content(other.content ? other.content->clone() : nullptr)
  {
  }

  any(any&& other) noexcept { *this = std::move(other); }

  template<typename ValueType,
    typename = typename std::enable_if<
      !std::is_same<any, typename std::decay<ValueType>::type>::value
    >::type
  >
  any(ValueType&& value)
    : content(new holder<typename std::remove_reference<ValueType>::type>(
        std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers

  void swap(any& other) noexcept { std::swap(content, other.content); }

  any& operator=(any const& rhs) { return *this = any(rhs); }

  any& operator=(any&& rhs) noexcept
  {
    content = rhs.content;
    rhs.content = nullptr;

    return *this;
  }

  template<typename ValueType,
    typename = typename std::enable_if<
      !std::is_same<any, typename std::remove_const<
        typename std::remove_reference<ValueType>::type>::type>::value
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  std::type_info const& type() const noexcept
  {
    return content ? content->type() : typeid(void);
  }

private: // types

  struct placeholder
  {
    placeholder() = default;

    virtual ~placeholder() noexcept { }

    virtual placeholder* clone() const = 0;

    virtual std::type_info const& type() const = 0;
  };

  template<typename ValueType, typename = void>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T>
    holder(T&& value) : held(std::forward<T>(value)) { }

    holder& operator=(holder const&) = delete;

    placeholder* clone() const final { throw std::invalid_argument(""); }

  public: // queries
    std::type_info const& type() const noexcept { return typeid(ValueType); }

  public:
    ValueType held;
  };

  template<typename ValueType>
  struct holder<
    ValueType,
    typename std::enable_if<
      std::is_copy_constructible<ValueType>::value
    >::type
  > : public placeholder
  {
  public: // constructor
    template <class T>
    holder(T&& value) : held(std::forward<T>(value)) { }

    placeholder* clone() const final { return new holder<ValueType>(held); }

  public: // queries
    std::type_info const& type() const noexcept { return typeid(ValueType); }

  public:
    ValueType held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

  placeholder* content;
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand && (operand->type() == typeid(ValueType))
    ? &static_cast<any::holder<ValueType>*>(operand->content)->held
    : nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
{
  typedef typename std::remove_reference<ValueType>::type nonref;

#ifndef NDEBUG
  nonref* const result(any_cast<nonref>(&operand));

  if (!result)
  {
    throw std::bad_cast();
  }
  // else do nothing

  return *result;
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand)
{
  typedef typename std::remove_reference<ValueType>::type nonref;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

}

#endif // ANY_HPP
\$\endgroup\$
9
  • \$\begingroup\$ You mention unique_ptr. Why is content not a unique_ptr? \$\endgroup\$ Commented Sep 2, 2012 at 12:05
  • \$\begingroup\$ because unique_ptr is a class template. The different instantiations of it differ in type. But any, just like boost::any, can store any type. \$\endgroup\$ Commented Sep 2, 2012 at 22:37
  • \$\begingroup\$ @user1095108: No he means that instead of placeholder* content you can use unique_ptr<placeholder> content. It will make your life much easier. \$\endgroup\$ Commented Dec 31, 2012 at 12:05
  • \$\begingroup\$ It would make for prettier, but also heavier, code. \$\endgroup\$ Commented Jan 1, 2013 at 17:10
  • \$\begingroup\$ How concerned are you with the accuracy of your clone? It seems to be missing the empty and clear member functions, but I'm not sure how much you care (if at all). \$\endgroup\$ Commented Mar 5, 2014 at 3:56

2 Answers 2

1
\$\begingroup\$

Watch the move assignment operator:

any& operator=(any&& rhs) noexcept
{
    content = rhs.content;
    rhs.content = nullptr;

    return *this;
}

content was leaked here, and it'd be better to use unique_ptr<placeholder>, as other commenters have said.

~any() { delete content; }
virtual ~placeholder() noexcept { }

Destructors are noexcept by default. If you want to be explicit about it, be consistent in that.

I do not think you need all these applications of SFINAE. Non templated overloads are anyway preferred over templated ones.

\$\endgroup\$
0
\$\begingroup\$

Instead of

     std::type_info const& type() const noexcept
     {
        return content ? content->type() : typeid(void);
     }

I would recommend:

     std::type_info const& type() const noexcept
     {
        return content ? content->type() : typeid(nullptr);
     }

After all, the default content is a nullptr.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I disagree, after all, the principal requirement is, that any can hold an object of any type. Even a nullptr, which is of type nullptr_t. \$\endgroup\$ Commented Oct 5, 2014 at 8:53
  • \$\begingroup\$ @user1095108, I understand. Shouldn't type returned be typeid(nullptr) if the object is holding a nullptr? \$\endgroup\$
    – R Sahu
    Commented Oct 5, 2014 at 8:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.