stackexchange Polymorphic (owned) reference wrapper for class hierarchies
NOTE:
1、典型的 Polymorphic-containers-with-value-semantic
#pragma once
#include <memory>
#include <cassert>
#include <functional>
#include <stdexcept>
#include <vector>
namespace stdex
{
inline namespace details
{
/// @brief Deep copy construct from (Specialized&)*src
///
/// @retval nullptr if src is nullptr
/// @retval Specialized clone of *src
///
/// @note Undefined behavior if src does not point to a Specialized*
template<typename Base, typename Specialized>
Base* polymorphic_clone(const Base *src)
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
if (src == nullptr)
return nullptr;
return new Specialized { static_cast<const Specialized&>(*src) };
}
}
/// @brief polymorphic reference interface over a base class
///
/// Respects polymorphic behavior of class ref.
/// Instances have deep copy semantics (clone) and
/// "[const] Base&" interface
///
/// @note Not regular: no trivial way to implement non-intrusive equality
///
/// @note safe to use with standard containers
template<typename Base>
class polymorphic final
{
public:
/// Functor capable to convert a Base* to it's specialized type
/// and clone it (intrusive implementation can be used)
///
/// example intrusive implementation (if supported by Base):
/// []( const Base* src ) { return src->clone(); }
typedef std::function<Base* (const Base*)> clone_functor;
/// @brief construct (takes ownership of ptr)
template<typename Specialized, typename CloneSpecialized>
polymorphic(Specialized *ptr, CloneSpecialized functor) noexcept :
instance_ { ptr }, clone_ { std::move(functor) }
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
static_assert(
std::is_constructible<clone_functor, CloneSpecialized>::value,
"CloneSpecialized is not valid for a clone functor");
}
// not implemented: UB cloning in case client provides specialized ptr
// polymorphic(Base* ptr);
// @note empty constructor for std:: containers support
polymorphic() = default;
polymorphic(polymorphic&&) = default;
polymorphic(const polymorphic &other)
// : polymorphic{std::move(other.clone())}
:
polymorphic { other.clone() } // comment by @dyp
{
}
// polymorphic& operator=(polymorphic other)
polymorphic& operator=(polymorphic other) noexcept // comment by @dyp
{
std::swap(instance_, other.instance_);
std::swap(clone_, other.clone_);
return *this;
}
~polymorphic() = default;
/// @brief Cast to contained type
/// @pre instance not moved
/// @pre *this initialized with valid instance
operator Base&() const
{
assert(instance_.get());
return *instance_.get();
}
/// @brief Cast to contained type
/// @pre instance not moved
/// @pre *this initialized with valid instance
operator const Base&() const
{
assert(instance_.get());
return *instance_.get();
}
private:
polymorphic clone() const
{
return
{ clone_(instance_.get()), clone_functor
{ clone_}};
}
std::unique_ptr<Base> instance_;
clone_functor clone_;
};
// edited after comment by @dyp
template<typename Base, typename Specialized, typename CF>
polymorphic<Base> to_polymorphic(Specialized &&temp, CF functor)
{
return
{
new Specialized
{ std::move(temp)},
typename polymorphic<Base>::clone_functor
{ std::move(functor)}
};
}
template<typename Base, typename Specialized>
polymorphic<Base> to_polymorphic(Specialized &&temp)
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
return to_polymorphic<Base, Specialized>(std::move(temp), polymorphic_clone<Base, Specialized>);
}
template<typename Base, typename Specialized, typename ...Args>
// polymorphic<Base> to_polymorphic(Args ...args)
polymorphic<Base> to_polymorphic(Args &&...args) // comment by @dyp
{
static_assert(std::is_constructible<Specialized, Args...>::value,
"Cannot instantiate Specialized from arguments");
return to_polymorphic<Base, Specialized>(std::move(Specialized { std::forward<Args...>(args...) }));
}
template<typename Base> using polymorphic_vector =
std::vector<polymorphic<Base>>;
template<typename Base, typename ...Args>
polymorphic_vector<Base> to_polymorphic_vector(Args &&...args)
{
// comment by @dyp (add std::forward)
return
{
to_polymorphic<Base>(std::forward<Args>(args))...
};
}
} // stdex
Example use (using a class hierarchy based on view
, a generic responder for HTTP requests - the implementation of view is not important here, I just had it in existing code):
stdex::polymorphic_vector<view> views = // explicit type for clarity
stdex::to_polymorphic_vector<view>(
echo_view{"/echo"}, // class echo_view : public view
directory_view{"/static_files", "~/http-server/static"}
// class directory_view : public view
);
for(auto& v: views)
if(v.matches(reuqest.url())) // bool view::matches(...);
auto response = v.handle(request); // virtual view::handle(...) = 0;