diff --git a/research/try.cpp b/research/try.cpp index 400e6d74b..9f446bd07 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -26,21 +26,11 @@ // 5/14 - c++11 transition: detect empty function object // 7/14 - c++11 transition: std hash function vs. boost hash // 9/14 - variadic templates and perfect forwarding +// 11/14 - pointer to member functions and name mangling /** @file try.cpp - ** Investigation: pitfalls of "perfect forwarding". - ** Find out about the corner cases where chained argument forwarding - ** does not work as expected. The key point is to put close attention on the - ** template parameters we're passing on to the next lower layer. It is crucial - ** either to add the reference explicitly, or to have it included implicitly - ** by relying on the way how template params are matched on function calls: - ** - lvalue -> parameter becomes TY & - ** - rvalue -> parameter becomes TY - ** - ** In this final stage, the example shows what happens if we erroneously fail - ** to take the variadic arguments by '&&' on forwarding: we end-up with a - ** slicing copy to the base class. + ** Investigation: member function pointers, types and name mangling. ** */ @@ -48,11 +38,12 @@ #include "lib/util.hpp" #include -#include #include -#include #include +using lib::test::showType; +using lib::test::demangleCxx; + using std::string; using std::cout; using std::endl; @@ -60,21 +51,10 @@ using std::endl; class Interface { public: - Interface(){} - Interface(Interface const& o) { cout << "COPY.CT from "<<&o<<" !!!\n"; } - Interface(Interface const&& o) { cout << "MOVE.CT from "<<&o<<" !!!\n"; } + virtual ~Interface() { } ///< this is an interface - Interface& - operator= (Interface const& o) { cout << "COPY= from "<<&o<<" !!!\n"; return *this; } - Interface& - operator= (Interface const&& o) { cout << "MOVE= from "<<&o<<" !!!\n"; return *this; } - - - virtual ~Interface() { } - virtual string op() const - { - return "happy SLICING"; - } + virtual string moo() =0; + virtual string boo() =0; }; class Impl @@ -82,11 +62,8 @@ class Impl { string s_; - string - op() const override - { - return s_; - } + string moo() { return s_ + " Moo"; } + string boo() { return s_ + " Boo"; } public: Impl(string ss ="IMP") @@ -94,65 +71,27 @@ class Impl { } }; -template -string -showRefRRefVal() -{ - return std::is_lvalue_reference::value? " by REF" - : std::is_rvalue_reference::value? " by MOVE": " VAL"; -} -template -void -diagnostics (string id, XS const&... xs) -{ - cout << "--"<(xs...) - << "\n" - ; -} - - -void -invoke (Interface const& ref) -{ - using Ty = Interface const&; - diagnostics ("Invoke", ref); - cout << "instanceof Impl?" << bool(INSTANCEOF(Impl, &ref)) <<"\n"; - cout << "________________" - << ref.op() - << "____\n"; -} - -template -void -indirect_1 (FUN fun, ARG... args) // NOTE: erroneously taking ARG as-is, which results in taking BY VALUE when used in the forwarding chain! -{ - diagnostics ("Indirect-1", args...); - fun (args...); -} - -template -void -indirect_2 (FUN fun, ARG&&... args) -{ - diagnostics ("Indirect-2", args...); - indirect_1 (fun, std::forward(args)...); -} int main (int, char**) { Impl obj; - Interface const& ref = obj; + Interface& ref = obj; + + typedef string (Interface::*Memfun) (void); + cout << "before call. Address... "<<&ref<<"\n"; - std::function fun(invoke); + cout << ref.moo() << endl; + cout << ref.boo() << endl; - indirect_2 (fun, ref); - indirect_2 (fun, Impl("honk")); + Memfun memfun = &Interface::moo; + cout << demangleCxx (showType (memfun)) << endl; + cout << demangleCxx (showType(&Interface::moo)) << endl; + cout << (ref.*memfun) () << endl; cout << "\n.gulp.\n"; diff --git a/src/lib/test/suite.cpp b/src/lib/test/suite.cpp index 63201fbea..fd22de79a 100644 --- a/src/lib/test/suite.cpp +++ b/src/lib/test/suite.cpp @@ -50,9 +50,11 @@ namespace test { using std::shared_ptr; using boost::algorithm::trim; + using util::cStr; using util::isnil; using util::contains; using lib::test::showType; + using lib::test::demangleCxx; typedef map TestMap; typedef shared_ptr PTestMap; @@ -174,7 +176,7 @@ namespace test { { try { - INFO (test, "++------------------- invoking TEST: %s", showType(theTest).c()); + INFO (test, "++------------------- invoking TEST: %s", cStr(demangleCxx(showType(theTest)))); theTest.run (cmdline); return Suite::TEST_OK; } diff --git a/src/lib/test/test-helper.cpp b/src/lib/test/test-helper.cpp index 16c81ebf3..8c2168e7e 100644 --- a/src/lib/test/test-helper.cpp +++ b/src/lib/test/test-helper.cpp @@ -24,6 +24,11 @@ #include "lib/test/test-helper.hpp" #include "lib/test/testdummy.hpp" #include "lib/format-string.hpp" +#include "lib/unique-malloc-owner.hpp" + +#ifdef __GNUG__ +#include +#endif #include @@ -41,6 +46,64 @@ namespace test{ } +#ifdef __GNUG__ + /** + * \par Implementation notes + * GCC / G++ subscribes to a cross-vendor ABI for C++, sometimes called the IA64 ABI + * because it happens to be the native ABI for that platform. It is summarised + * \link http://www.codesourcery.com/cxx-abi/ mentor-embedded \endlink + * along with the current specification. For users of GCC greater than or equal to 3.x, + * entry points are exposed through the standard library in \c + * relies on a vendor neutral ABI for C++ compiled programs + * + * char* abi::__cxa_demangle(const char* mangled_name, + * char* output_buffer, size_t* length, + * int* status) + * + * Parameters: + * - \c mangled_name + * NUL-terminated character string containing the name to be demangled. + * - \c output_buffer + * region of memory, allocated with \c malloc, of `*length` bytes, + * into which the demangled name is stored. If \c output_buffer is not long enough, + * it is expanded using \c realloc. output_buffer may instead be NULL; in that case, + * the demangled name is placed in a region of memory allocated with \c malloc. + * - \c length + * If length is non-NULL, the length of the buffer containing the demangled name is placed in `*length`. + * - \c status + * error flag: `*status` is set to one of the following values: + * + * 0: The demangling operation succeeded. + * -1: A memory allocation failure occurred. + * -2: mangled_name is not a valid name under the C++ ABI mangling rules. + * -3: One of the arguments is invalid. + * + * The function returns a pointer to the start of the NUL-terminated demangled name, + * or NULL if the demangling fails. The caller is responsible for deallocating + * this memory using \c free. + */ + string + demangleCxx (Literal rawName) + { + int error = -4; + UniqueMallocOwner demangled (abi::__cxa_demangle (rawName, + NULL, + NULL, + &error)); + return 0==error? demangled.get() + : string(rawName); + } + +#else + string + demangleCxx (Literal rawName) + { + return string (rawName); + } +#endif + + + /** @todo probably this can be done in a more clever way. Anyone...? */ string @@ -55,7 +118,8 @@ namespace test{ garbage[--p] = alpha[rand() % MAXAL]; return garbage; } - + + /** storage for test-dummy flags */ diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp index 425fc8429..bfc338f6a 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -72,6 +72,19 @@ namespace test{ } + /** reverse the effect of C++ name mangling. + * @return string in language-level form of a C++ type or object name, + * or a string with the original input if demangling fails. + * @warning implementation relies on the cross vendor C++ ABI in use + * by GCC and compatible compilers, so portability is limited. + * The implementation is accessed through libStdC++ + * Name representation in emitted object code and type IDs is + * essentially an implementation detail and subject to change. + */ + string + demangleCxx (Literal rawName); + + /** for printing sizeof(). * prints the given size and name literally, without any further magic */ string diff --git a/tests/00support.tests b/tests/00support.tests index 8e5401ad7..61cf78d36 100644 --- a/tests/00support.tests +++ b/tests/00support.tests @@ -14,7 +14,13 @@ out: sizeof.+lib.+test.+test.+Wrmrmpft.+Murpf.+ = 1 END -TEST "TestOption_test" TestOption_test <::Inner const&&) +END + + +TEST "Testsuite option handling" TestOption_test < Testgroup=ALL out: --> Test-ID =--missing-- diff --git a/tests/library/test/test-helper-demangling-test.cpp b/tests/library/test/test-helper-demangling-test.cpp new file mode 100644 index 000000000..c158d1cee --- /dev/null +++ b/tests/library/test/test-helper-demangling-test.cpp @@ -0,0 +1,80 @@ +/* + TestHelperDemangling(Test) - ensure a helper for C++ demangling works as expected + + Copyright (C) Lumiera.org + 2014, 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 + +using std::string; +using std::cout; +using std::endl; + + +namespace lib { +namespace test{ +namespace test{ + + template + struct Outer + { + struct Inner { }; + + static const T* + phantom (Inner const&&) + { + return nullptr; + } + }; + + struct Space { }; + + + + + /**********************************************//** + * @test verify the demangling of C++ names, + * as available through the GCC platform ABI. + * The Lumiera support library exposes this + * non-portable feature through a convenience + * helper to ease the writing of unit tests. + * + * @see test-helper.hpp + */ + class TestHelperDemangling_test : public Test + { + void + run (Arg) + { + Outer ship; + auto magic = &ship.phantom; + + cout << showType(magic) << endl; + cout << demangleCxx(showType(magic)) << endl; + } + }; + + LAUNCHER (TestHelperDemangling_test, "unit common"); + + +}}} // namespace lib::test::test