LUMIERA.clone/src/lib/access-casted.hpp
Ichthyostega 250a5519de TICKET 141: now we've full coverage, both for Variant and AccessCasted
Note: the new Variant implementation is a re-write from scratch
and does not rely on util::AccessCasted any more. Anyway, both
are now thoroughly covered by unit test
2015-04-26 05:55:54 +02:00

217 lines
7.5 KiB
C++

/*
ACCESS-CASTED.hpp - util template to access a value using conversion or cast as appropriate
Copyright (C) Lumiera.org
2008, 2015 Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file access-casted.hpp
** Helper for accessing a value, employing either a conversion or downcast,
** depending on the relation of the source type (type of the original value)
** and the target type (type we need within the usage context).
** When instantiating AcessCasted<TAR>, we get a template static function
** \c AcessCasted<TAR>::access<SRC>(SRC&& elm), where the actual implementation
** is chosen using based on our type traits. If no sensible implementation can be
** selected, a static assertion will be triggered.
**
** The possible conversion path is limited to options considered "safe"
** - automatic type conversions, as would happen when returning a value of type SRC
** from a function with return signature TAR
** - this possibly includes an up-cast from an implementation type to a base class
** - explicit copy construction when the type TAR is a plain value
** - downcast to implementation class \em only when a safe dynamic downcast can be
** performed, based on RTTI in the actual object.
** - additionally, a pointer will be formed by taking an address
** - and a reference will be initialised by dereferencing a pointer (with NULL check).
** - if valid and permitted by the rules of the language, r-value references and const
** values, references and pointers-to-const are supported as well.
**
** @see lib::InPlaceAnyHolder usage example to access a subclass in embedded storage
**
*/
#ifndef UTIL_ACCESS_CASTED_H
#define UTIL_ACCESS_CASTED_H
#include "lib/error.hpp"
#include <type_traits>
#include <utility>
namespace util {
namespace error = lumiera::error;
namespace { // implementation helper traits....
using std::remove_pointer;
using std::remove_reference;
template <typename T>
using PlainType = typename remove_pointer<
typename remove_reference<T>::type>::type;
template <typename T>
struct has_RTTI
{
static constexpr bool value = std::is_polymorphic<PlainType<T>>::value;
};
template <typename SRC, typename TAR>
struct can_downcast
{
static constexpr bool value = std::is_base_of<PlainType<SRC>, PlainType<TAR>>::value
&& ( ( std::is_pointer<typename remove_reference<SRC>::type>::value
&& std::is_pointer<typename remove_reference<TAR>::type>::value
)
||( !std::is_pointer<typename remove_reference<SRC>::type>::value
&& !std::is_pointer<typename remove_reference<TAR>::type>::value
));
};
template <typename SRC, typename TAR>
struct can_use_dynamic_downcast
{
static constexpr bool value = !std::is_convertible<SRC,TAR>::value
&& can_downcast<SRC,TAR>::value
&& has_RTTI<SRC>::value
&& has_RTTI<TAR>::value;
};
template <typename SRC, typename TAR>
struct can_use_conversion
: std::is_convertible<SRC,TAR>
{ };
template <typename SRC, typename TAR>
struct can_take_address
{
static constexpr bool value = !std::is_rvalue_reference<SRC>::value // considered dangerous
&& !std::is_pointer<typename remove_reference<SRC>::type>::value
&& std::is_pointer<typename remove_reference<TAR>::type>::value;
};
template <typename SRC, typename TAR>
struct can_dereference
{
static constexpr bool value = !std::is_pointer<typename remove_reference<TAR>::type>::value
&& std::is_pointer<typename remove_reference<SRC>::type>::value;
};
template <typename SRC, typename TAR>
struct if_can_use_dynamic_downcast
: std::enable_if< can_use_dynamic_downcast<SRC,TAR>::value, TAR>
{ };
template <typename SRC, typename TAR>
struct if_can_use_conversion
: std::enable_if< can_use_conversion<SRC,TAR>::value, TAR>
{ };
template <typename SRC, typename TAR>
struct if_can_take_address
: std::enable_if< can_take_address<SRC,TAR>::value, TAR>
{ };
template <typename SRC, typename TAR>
struct if_can_dereference
: std::enable_if< can_dereference<SRC,TAR>::value, TAR>
{ };
}//(End)helper traits
/**
* Helper template to access a given value,
* possibly converted or casted in a safe way.
*/
template<typename TAR>
struct AccessCasted
{
typedef TAR Ret;
template<typename SRC>
static typename if_can_use_dynamic_downcast<SRC&&,TAR>::type
access (SRC&& elem)
{
try
{
return dynamic_cast<TAR> (std::forward<SRC>(elem));
}
catch (std::bad_cast& castError)
{
throw error::Invalid(castError
,"AccessCasted: not the expected runtime type; downcast failed"
,error::LUMIERA_ERROR_WRONG_TYPE);
}
}
template<typename SRC>
static typename if_can_use_conversion<SRC&&,TAR>::type
access (SRC&& elem)
{
return std::forward<SRC> (elem);
}
template<typename SRC>
static typename if_can_take_address<SRC&&,TAR>::type
access (SRC&& elem)
{
return AccessCasted<TAR>::access (&elem);
}
template<typename SRC>
static typename if_can_dereference<SRC&&,TAR>::type
access (SRC&& elem)
{
if (!elem)
throw error::Invalid("AccessCasted: attempt to build a value or reference from a NULL pointer"
,error::LUMIERA_ERROR_BOTTOM_VALUE);
return AccessCasted<TAR>::access (*elem);
}
/** catch-all to signal failure of conversion */
static TAR
access (...)
{
// NOTE: if you see this assertion failure, none of the above predicates were true.
// Chances are that you requested a conversion that is logically impossible or dangerous,
// like e.g. taking a reference from an anonymous value parameter
static_assert (!sizeof(TAR), "AccessCasted: No valid conversion or cast supported for these types.");
throw error::Invalid("impossible or unsafe type conversion requested");
}
};
} // namespace util
#endif