/* AccessCasted(Test) - verify helper to cast or convert as appropriate Copyright (C) Lumiera.org 2008, 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 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. * *****************************************************/ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "lib/access-casted.hpp" #include "lib/util.hpp" #include #include #include using std::move; using std::string; using std::ostream; using std::cout; using std::endl; using util::isSameObject; using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; namespace util { namespace test { namespace { // Test fixture... struct B {}; struct D : B {}; struct E : D { virtual ~E() {}; }; struct X { char x = 'x'; }; struct F : X , E { }; using lib::test::tyAbbr; ostream& operator<< (ostream& s, const B& b) { return s << "B{} adr="<<&b<<" type: "< = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << can_downcast::value < = " << has_RTTI::value < = " << has_RTTI::value < = " << has_RTTI::value < = " << std::is_convertible::value < = " << std::is_convertible::value < = " << can_use_dynamic_downcast::value < = " << can_use_conversion::value < = " << can_use_dynamic_downcast::value < = " << can_use_conversion::value < = " << can_use_dynamic_downcast::value < = " << can_use_conversion::value < = " << can_use_conversion::value < = " << can_use_dynamic_downcast::value <" << AccessCasted::access(d) <" << AccessCasted::access(rD) <::access(move(dd1)); // does not compile since it would be dangerous; we can't take a l-value ref // AccessCasted::access(rD); // from a r-value (move) reference and we can't move a l-value ref // AccessCasted::access(d); // cout << "=== build a value object ==="<" << AccessCasted::access(d) <" << AccessCasted::access(rD) <" << AccessCasted::access(move(dd1)) <" << AccessCasted::access(d) <" << AccessCasted::access(rD) <::access(move(dd1)); // should not take value moved by r-value-ref as pointer, otherwise the moved object would be lost cout << "=== dereference a pointer ==="<" << AccessCasted::access(pD) <" << AccessCasted::access(pD) <" << AccessCasted::access(move(pdd1)) <::access(pNull)); // run-time NULL check // AccessCasted::access(pD); // should not move away a value accessed through a pointer, there might be other users cout << "=== const correctness ==="<" << AccessCasted::access(d) <" << AccessCasted::access(rD) <" << AccessCasted::access(d) <" << AccessCasted::access(rD) <" << AccessCasted::access(d) <" << AccessCasted::access(rD) <" << AccessCasted::access(pD) <" << AccessCasted::access(pD) <" << AccessCasted::access(cD) <" << AccessCasted::access(rcD) <" << AccessCasted::access(cD) <" << AccessCasted::access(rcD) <" << AccessCasted::access(cD) <" << AccessCasted::access(rcD) <" << AccessCasted::access(pcD) <" << AccessCasted::access(pcD) <" << AccessCasted::access(rcD) <" << AccessCasted::access(move(cD1)) <::access(rcD); // normal ref from const ref is not const correct // AccessCasted::access(rcD); // likewise, regular pointer from const ref prohibited // AccessCasted::access(pcD); // likewise, regular ref from pointer-to-const // AccessCasted::access(pcD); // and regular pointer from pointer-to-const // AccessCasted::access(rcD); // ruled out already because moving a reference is invalid // AccessCasted::access(pcD); // ruled out already because moving a dereferenced pointer is invalid // AccessCasted::access(move(cD)); // ruled out already because taking reference from moved value is invalid // AccessCasted::access(move(cD)); // and same for taking pointer from a moved value. cout << "=== work cases: actual conversions ==="<" << AccessCasted::access(rB) <" << AccessCasted::access(rD) <" << AccessCasted::access(pB) <" << AccessCasted::access(pD) <" << AccessCasted::access(rD) <" << AccessCasted::access(pD) <" << AccessCasted::access(rpB) <" << AccessCasted::access(rpD) <" << AccessCasted::access(rD) <" << AccessCasted::access(pD) <" << AccessCasted::access(rcD) <" << AccessCasted::access(pcD) <::access(rpD); // ruled out, since it would allow to sneak-in a non-D object into the D* // AccessCasted::access(rB); // any down-casts are ruled out, // AccessCasted::access(pB); // since neither B nor D has RTTI // AccessCasted::access(pB); // // AccessCasted::access(rB); // // AccessCasted::access(rD); // we need RTTI on both ends to perform a safe dynamic downcast. // AccessCasted::access((B*)pD); // dangerous, since we have no way to know for sure it's indeed a D object // AccessCasted::access(pDE); // same here, since E has RTTI but D hasn't, we have no way to find out the real type VERIFY_ERROR (WRONG_TYPE, AccessCasted::access(rE)); // allowed by typing, but fails at runtime since it isn't an F-object cout << "Access(E(F)& as F&) --->" << AccessCasted::access(rEF) <" << AccessCasted::access(pEF) <" << AccessCasted::access(pEF) <" << AccessCasted::access(pEF) <" << AccessCasted::access(pF) <" << AccessCasted::access(pXF) <" << AccessCasted::access(pF) <::access(pEF); // cross-cast not supported (to complicated to implement) // AccessCasted::access(pXF); // downcast not possible, since X does not provide RTTI int i = 2; float fp = 3.1415; cout << "Access(int as double) --->" << AccessCasted::access(i) <" << AccessCasted::access(fp) <::access(i); // would undermine the type system, thus ruled out // AccessCasted::access(i); // allowed, but warning: returning reference to temporary (and the warning is justified) CHECK (isSameObject (d, AccessCasted::access(d))); CHECK (isSameObject (rD, AccessCasted::access(pD))); CHECK (isSameObject (d, AccessCasted::access(pD))); CHECK (!isSameObject (d, AccessCasted::access(rD))); CHECK (isSameObject (f, AccessCasted::access(rEF))); CHECK (!isSameObject (f, AccessCasted::access(pF))); // note: address adjustment due to layout of mixin object X } }; /** Register this test class... */ LAUNCHER (AccessCasted_test, "unit common"); }} // namespace lib::meta::test