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
This commit is contained in:
parent
3dfac48dea
commit
250a5519de
3 changed files with 184 additions and 89 deletions
|
|
@ -2,7 +2,7 @@
|
|||
ACCESS-CASTED.hpp - util template to access a value using conversion or cast as appropriate
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
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
|
||||
|
|
@ -22,19 +22,26 @@
|
|||
|
||||
|
||||
/** @file access-casted.hpp
|
||||
** Helper for accessing a value, employing either conversion or downcast,
|
||||
** 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), but the actual implementation
|
||||
** is chosen using boost::type_traits. If no sensible implementation can be
|
||||
** selected, \c EmptyVal<TAR>::create() is invoked instead, which by default
|
||||
** creates a NULL value or similar by using the no-argument ctor of the
|
||||
** type TAR. Alternatively, you may define an specialisation of EmptyVal,
|
||||
** e.g. throwing an exception instead of creating a NULL value.
|
||||
** \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.
|
||||
**
|
||||
** @todo WIP 4/2015 is being rewritten ///////////////////////////////////////////TICKET #738 needs slight overhaul and clean-up
|
||||
** @see lumiera::WrapperPtr usage example to access a variant record
|
||||
** @see lib::InPlaceAnyHolder usage example to access a subclass in embedded storage
|
||||
**
|
||||
*/
|
||||
|
|
@ -51,85 +58,96 @@
|
|||
|
||||
|
||||
namespace util {
|
||||
using std::remove_pointer;
|
||||
using std::remove_reference;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
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
|
||||
&& !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>
|
||||
{ };
|
||||
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
|
||||
{
|
||||
|
|
@ -140,7 +158,6 @@ namespace util {
|
|||
static typename if_can_use_dynamic_downcast<SRC&&,TAR>::type
|
||||
access (SRC&& elem)
|
||||
{
|
||||
std::cerr << "downcast-"<<lib::test::showRefKind<SRC>()<<" && ->"<<lib::test::showRefKind<SRC&&>()<<std::endl;
|
||||
try
|
||||
{
|
||||
return dynamic_cast<TAR> (std::forward<SRC>(elem));
|
||||
|
|
@ -158,7 +175,6 @@ namespace util {
|
|||
static typename if_can_use_conversion<SRC&&,TAR>::type
|
||||
access (SRC&& elem)
|
||||
{
|
||||
std::cerr << "convert-"<<lib::test::showRefKind<SRC>()<<" && ->"<<lib::test::showRefKind<SRC&&>()<<std::endl;
|
||||
return std::forward<SRC> (elem);
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +183,6 @@ namespace util {
|
|||
static typename if_can_take_address<SRC&&,TAR>::type
|
||||
access (SRC&& elem)
|
||||
{
|
||||
std::cerr << "address-"<<lib::test::showRefKind<SRC>()<<" && ->"<<lib::test::showRefKind<SRC&&>()<<std::endl;
|
||||
return AccessCasted<TAR>::access (&elem);
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +191,6 @@ namespace util {
|
|||
static typename if_can_dereference<SRC&&,TAR>::type
|
||||
access (SRC&& elem)
|
||||
{
|
||||
std::cerr << "deref-"<<lib::test::showRefKind<SRC>()<<" && ->"<<lib::test::showRefKind<SRC&&>()<<std::endl;
|
||||
if (!elem)
|
||||
throw error::Invalid("AccessCasted: attempt to build a value or reference from a NULL pointer"
|
||||
,error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,83 @@ TESTING "Library Test Suite: basic and low-level components" ./test-suite --grou
|
|||
|
||||
|
||||
TEST "casting or converting access helper" AccessCasted_test <<END
|
||||
out-lit: can_downcast<B,D> = 1
|
||||
out-lit: can_downcast<B*,D*> = 1
|
||||
out-lit: can_downcast<B&,D&> = 1
|
||||
out-lit: can_downcast<B&,D*> = 0
|
||||
out-lit: can_downcast<B*,D&> = 0
|
||||
out-lit: can_downcast<B*&,D*&> = 1
|
||||
out-lit: can_downcast<D*&,D*&> = 1
|
||||
out-lit: can_downcast<D*,E*> = 1
|
||||
out-lit: can_downcast<E*,F*> = 1
|
||||
out-lit: has_RTTI<D*> = 0
|
||||
out-lit: has_RTTI<E*> = 1
|
||||
out-lit: has_RTTI<F*> = 1
|
||||
out-lit: is_convertible<D,D&> = 0
|
||||
out-lit: is_convertible<D&,D> = 1
|
||||
out-lit: can_use_dynamic_downcast<D,D&> = 0
|
||||
out-lit: can_use_conversion<D,D&> = 0
|
||||
out-lit: can_use_dynamic_downcast<B*,D*> = 0
|
||||
out-lit: can_use_conversion<D*,B*> = 1
|
||||
out-lit: can_use_dynamic_downcast<D*&,D*&> = 0
|
||||
out-lit: can_use_conversion<D*&,D*&> = 1
|
||||
out-lit: can_use_conversion<D*,E*> = 0
|
||||
out-lit: can_use_dynamic_downcast<D*&,E*> = 0
|
||||
out-lit: === standard case: References ===
|
||||
out: Access\(D as D&\) .+D.. adr=0x.+ type: D
|
||||
out: Access\(D& as D&\) .+D.. adr=0x.+ type: D
|
||||
out-lit: === build a value object ===
|
||||
out: Access.D as D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D& as D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D&& as D. .+D.. adr=0x.+ type: D
|
||||
out-lit: === take a pointer ===
|
||||
out: Access.D as D\*. .+0x.+
|
||||
out: Access.D& as D\*. .+0x.+
|
||||
out-lit: === dereference a pointer ===
|
||||
out: Access.D\* as D&. .+D.. adr=0x.+ type: D
|
||||
out: Access.D\* as D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D\*&& as D. .+D.. adr=0x.+ type: D
|
||||
out-lit: === const correctness ===
|
||||
out: Access.D as D const&. .+D.. adr=0x.+ type: D
|
||||
out: Access.D& as D const&. .+D.. adr=0x.+ type: D
|
||||
out: Access.D as const D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D& as const D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D as const D\*. .+0x.+
|
||||
out: Access.D& as const D\*. .+0x.+
|
||||
out: Access.D\* as D const&. .+D.. adr=0x.+ type: D
|
||||
out: Access.D\* as const D. .+D.. adr=0x.+ type: D
|
||||
out: Access.const D as D const&. .+D.. adr=0x.+ type: D
|
||||
out: Access.D const& as D const&. .+D.. adr=0x.+ type: D
|
||||
out: Access.const D as const D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D const& as const D. .+D.. adr=0x.+ type: D
|
||||
out: Access.const D as const D\*. .+0x.+
|
||||
out: Access.D const& as const D\*. .+0x.+
|
||||
out: Access.const D\* as D const&. .+D.. adr=0x.+ type: D
|
||||
out: Access.const D\* as const D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D const& as D. .+D.. adr=0x.+ type: D
|
||||
out: Access.D const&& as D. .+D.. adr=0x.+ type: D
|
||||
out-lit: === work cases: actual conversions ===
|
||||
out: Access.B& as B&. .+B.. adr=0x.+ type: B
|
||||
out: Access.D& as B&. .+B.. adr=0x.+ type: B
|
||||
out: Access.B\* as B\*. .+0x.+
|
||||
out: Access.D\* as B\*. .+0x.+
|
||||
out: Access.D& as B\*. .+0x.+
|
||||
out: Access.D\* as B&. .+B.. adr=0x.+ type: B
|
||||
out: Access.B\*& as B\*&. .+0x.+
|
||||
out: Access.D\*& as D\*&. .+0x.+
|
||||
out: Access.D& as const B\*. .+0x.+
|
||||
out: Access.D\* as B const&. .+B.. adr=0x.+ type: B
|
||||
out: Access.D const& as const B\*. .+0x.+
|
||||
out: Access.const D\* as B const&. .+B.. adr=0x.+ type: B
|
||||
out: Access.E\(F\)& as F&. .+F.. adr=0x.+ type: F
|
||||
out: Access.E\(F\)\* as F\*. .+0x.+
|
||||
out: Access.E\(F\)\* as F&. .+F.. adr=0x.+ type: F
|
||||
out: Access.E\(F\)& as F\*. .+0x.+
|
||||
out: Access.F\\* as X\*. .+0x.+
|
||||
out: Access.X\(F\)\* as X\*. .+0x.+
|
||||
out: Access.F\* as B\*. .+0x.+
|
||||
out-lit: Access(int as double) --->2
|
||||
out-lit: Access(float as long) --->3
|
||||
END
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace util {
|
|||
namespace test {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -89,6 +89,10 @@ namespace test {
|
|||
* @test verify a helper template for accessing values either through conversion or (dynamic) downcast.
|
||||
* Typically, this helper is used in value holder containers or variant-like data structures,
|
||||
* where the actual type is given at instantiation time of the template and possibly erased.
|
||||
*
|
||||
* @warning we can not really cover the negative cases, which ought to be rejected by the compiler.
|
||||
* These cases have been verified one by one, and are retained commented out. You ought
|
||||
* to re-check these cases manually when using a new compiler.
|
||||
*/
|
||||
class AccessCasted_test : public Test
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue