diff --git a/src/lib/access-casted.hpp b/src/lib/access-casted.hpp index 296d35135..66192c49d 100644 --- a/src/lib/access-casted.hpp +++ b/src/lib/access-casted.hpp @@ -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 + 2008, 2015 Hermann Vosseler 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, we get a template static function - ** \c AcessCasted::access(SRC& elm), but the actual implementation - ** is chosen using boost::type_traits. If no sensible implementation can be - ** selected, \c EmptyVal::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::access(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 - using PlainType = typename remove_pointer< - typename remove_reference::type>::type; - - template - struct has_RTTI - { - static constexpr bool value = std::is_polymorphic>::value; - }; - - template - struct can_downcast - { - static constexpr bool value = std::is_base_of, PlainType>::value - && ( ( std::is_pointer::type>::value - && std::is_pointer::type>::value - ) - ||( !std::is_pointer::type>::value - && !std::is_pointer::type>::value - )); - }; - - template - struct can_use_dynamic_downcast - { - static constexpr bool value = !std::is_convertible::value - && can_downcast::value - && has_RTTI::value - && has_RTTI::value; - }; - - template - struct can_use_conversion - : std::is_convertible - { }; - - template - struct can_take_address - { - static constexpr bool value = !std::is_rvalue_reference::value - && !std::is_pointer::type>::value - && std::is_pointer::type>::value; - }; - - template - struct can_dereference - { - static constexpr bool value = !std::is_pointer::type>::value - && std::is_pointer::type>::value; - }; - - - template - struct if_can_use_dynamic_downcast - : std::enable_if< can_use_dynamic_downcast::value, TAR> - { }; - - template - struct if_can_use_conversion - : std::enable_if< can_use_conversion::value, TAR> - { }; - - template - struct if_can_take_address - : std::enable_if< can_take_address::value, TAR> - { }; - - template - struct if_can_dereference - : std::enable_if< can_dereference::value, TAR> - { }; + namespace { // implementation helper traits.... - + using std::remove_pointer; + using std::remove_reference; + + + template + using PlainType = typename remove_pointer< + typename remove_reference::type>::type; + + template + struct has_RTTI + { + static constexpr bool value = std::is_polymorphic>::value; + }; + + template + struct can_downcast + { + static constexpr bool value = std::is_base_of, PlainType>::value + && ( ( std::is_pointer::type>::value + && std::is_pointer::type>::value + ) + ||( !std::is_pointer::type>::value + && !std::is_pointer::type>::value + )); + }; + + template + struct can_use_dynamic_downcast + { + static constexpr bool value = !std::is_convertible::value + && can_downcast::value + && has_RTTI::value + && has_RTTI::value; + }; + + template + struct can_use_conversion + : std::is_convertible + { }; + + template + struct can_take_address + { + static constexpr bool value = !std::is_rvalue_reference::value // considered dangerous + && !std::is_pointer::type>::value + && std::is_pointer::type>::value; + }; + + template + struct can_dereference + { + static constexpr bool value = !std::is_pointer::type>::value + && std::is_pointer::type>::value; + }; + + + template + struct if_can_use_dynamic_downcast + : std::enable_if< can_use_dynamic_downcast::value, TAR> + { }; + + template + struct if_can_use_conversion + : std::enable_if< can_use_conversion::value, TAR> + { }; + + template + struct if_can_take_address + : std::enable_if< can_take_address::value, TAR> + { }; + + template + struct if_can_dereference + : std::enable_if< can_dereference::value, TAR> + { }; + }//(End)helper traits + + + + + /** + * Helper template to access a given value, + * possibly converted or casted in a safe way. + */ template struct AccessCasted { @@ -140,7 +158,6 @@ namespace util { static typename if_can_use_dynamic_downcast::type access (SRC&& elem) { - std::cerr << "downcast-"<()<<" && ->"<()< (std::forward(elem)); @@ -158,7 +175,6 @@ namespace util { static typename if_can_use_conversion::type access (SRC&& elem) { - std::cerr << "convert-"<()<<" && ->"<()< (elem); } @@ -167,7 +183,6 @@ namespace util { static typename if_can_take_address::type access (SRC&& elem) { - std::cerr << "address-"<()<<" && ->"<()<::access (&elem); } @@ -176,7 +191,6 @@ namespace util { static typename if_can_dereference::type access (SRC&& elem) { - std::cerr << "deref-"<()<<" && ->"<()< = 1 +out-lit: can_downcast = 1 +out-lit: can_downcast = 1 +out-lit: can_downcast = 0 +out-lit: can_downcast = 0 +out-lit: can_downcast = 1 +out-lit: can_downcast = 1 +out-lit: can_downcast = 1 +out-lit: can_downcast = 1 +out-lit: has_RTTI = 0 +out-lit: has_RTTI = 1 +out-lit: has_RTTI = 1 +out-lit: is_convertible = 0 +out-lit: is_convertible = 1 +out-lit: can_use_dynamic_downcast = 0 +out-lit: can_use_conversion = 0 +out-lit: can_use_dynamic_downcast = 0 +out-lit: can_use_conversion = 1 +out-lit: can_use_dynamic_downcast = 0 +out-lit: can_use_conversion = 1 +out-lit: can_use_conversion = 0 +out-lit: can_use_dynamic_downcast = 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 diff --git a/tests/library/meta/access-casted-test.cpp b/tests/library/meta/access-casted-test.cpp index a845af838..7bfaa255a 100644 --- a/tests/library/meta/access-casted-test.cpp +++ b/tests/library/meta/access-casted-test.cpp @@ -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 {