Test helper to show demangled C++ names
Heureka! found out that the C++ standard library exposes a cross vendor C++ ABI, which amongst others allows to show object code names and type-IDs in the language-level, human readable unmangeld form. Of course, actual application code should not rely on such a internal representation, yet it is of tremendous help when writing and debugging unit tests. Signed-off-by: Ichthyostega <prg@ichthyostega.de>
This commit is contained in:
parent
639fd224db
commit
088e4422fb
6 changed files with 188 additions and 84 deletions
101
research/try.cpp
101
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 <cstddef>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
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<typename X>
|
||||
string
|
||||
showRefRRefVal()
|
||||
{
|
||||
return std::is_lvalue_reference<X>::value? " by REF"
|
||||
: std::is_rvalue_reference<X>::value? " by MOVE": " VAL";
|
||||
}
|
||||
|
||||
template<typename... XS>
|
||||
void
|
||||
diagnostics (string id, XS const&... xs)
|
||||
{
|
||||
cout << "--"<<id<<"--\n"
|
||||
<< lib::test::showVariadicTypes<XS...>(xs...)
|
||||
<< "\n"
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
invoke (Interface const& ref)
|
||||
{
|
||||
using Ty = Interface const&;
|
||||
diagnostics<Ty> ("Invoke", ref);
|
||||
cout << "instanceof Impl?" << bool(INSTANCEOF(Impl, &ref)) <<"\n";
|
||||
cout << "________________"
|
||||
<< ref.op()
|
||||
<< "____\n";
|
||||
}
|
||||
|
||||
template<class FUN, typename...ARG>
|
||||
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<ARG...> ("Indirect-1", args...);
|
||||
fun (args...);
|
||||
}
|
||||
|
||||
template<class FUN, typename...ARG>
|
||||
void
|
||||
indirect_2 (FUN fun, ARG&&... args)
|
||||
{
|
||||
diagnostics<ARG&&...> ("Indirect-2", args...);
|
||||
indirect_1 (fun, std::forward<ARG>(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<void(Interface const&)> 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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string, Launcher*> TestMap;
|
||||
typedef shared_ptr<TestMap> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
|
|
@ -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 <cxxabi.h>
|
||||
* 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<char> 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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@ out: sizeof.+lib.+test.+test.+Wrmrmpft.+Murpf.+ = 1
|
|||
END
|
||||
|
||||
|
||||
TEST "TestOption_test" TestOption_test <<END
|
||||
TEST "Helper to show demangled C++ names" TestHelperDemangling_test <<END
|
||||
out: .+lib.test.test.Space.+Outer.+Inner
|
||||
out-lit: lib::test::test::Space const* (*)(lib::test::test::Outer<lib::test::test::Space>::Inner const&&)
|
||||
END
|
||||
|
||||
|
||||
TEST "Testsuite option handling" TestOption_test <<END
|
||||
out: Testing invocation with cmdline: ...
|
||||
out: --> Testgroup=ALL
|
||||
out: --> Test-ID =--missing--
|
||||
|
|
|
|||
80
tests/library/test/test-helper-demangling-test.cpp
Normal file
80
tests/library/test/test-helper-demangling-test.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
TestHelperDemangling(Test) - ensure a helper for C++ demangling works as expected
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2014, 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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
namespace test{
|
||||
|
||||
template<class T>
|
||||
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<Space> ship;
|
||||
auto magic = &ship.phantom;
|
||||
|
||||
cout << showType(magic) << endl;
|
||||
cout << demangleCxx(showType(magic)) << endl;
|
||||
}
|
||||
};
|
||||
|
||||
LAUNCHER (TestHelperDemangling_test, "unit common");
|
||||
|
||||
|
||||
}}} // namespace lib::test::test
|
||||
Loading…
Reference in a new issue