
commit
8444e5ce55
18 changed files with 679 additions and 0 deletions
@ -0,0 +1,54 @@
|
||||
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR) |
||||
project(c++-benchmarks CXX) |
||||
|
||||
include(CMakeDetermineCXXCompiler) |
||||
|
||||
# default to Release |
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "") |
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) |
||||
endif("${CMAKE_BUILD_TYPE}" STREQUAL "") |
||||
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") |
||||
set(EXTRA_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wno-unused-parameter -pedantic" CACHE STRING "Extra flags used by the compiler during all build types.") |
||||
else("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") |
||||
set(EXTRA_CXX_FLAGS "" CACHE STRING "Extra flags used by the compiler during all build types.") |
||||
endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") |
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXX_FLAGS}") |
||||
|
||||
add_executable(benchmark-atomic-costs |
||||
benchmark-atomic-costs.cpp) |
||||
|
||||
add_executable(benchmark-atomic-costs-pthread |
||||
benchmark-atomic-costs.cpp) |
||||
set_target_properties(benchmark-atomic-costs-pthread |
||||
PROPERTIES |
||||
COMPILE_FLAGS "-pthread" |
||||
LINK_FLAGS "-pthread") |
||||
|
||||
add_executable(benchmark-shared-unique |
||||
benchmark-shared-unique.cpp |
||||
benchmark-shared-unique-impl.cpp) |
||||
|
||||
add_executable(test-NULL-type |
||||
test-NULL-type.cpp) |
||||
|
||||
add_executable(test-base-constr |
||||
test-base-constr.cpp) |
||||
|
||||
add_executable(test-const-ref-results |
||||
test-const-ref-results.cpp) |
||||
|
||||
add_executable(test-constructor-throw |
||||
test-constructor-throw.cpp) |
||||
|
||||
add_executable(test-copy-shared-ptr |
||||
test-copy-shared-ptr.cpp |
||||
test-copy-shared-ptr-impl.cpp) |
||||
|
||||
add_executable(test-rvalue-ref |
||||
test-rvalue-ref.cpp) |
||||
|
||||
add_executable(test-smart-factory |
||||
test-smart-factory.cpp) |
@ -0,0 +1,102 @@
|
||||
|
||||
#include "measure_time.h" |
||||
|
||||
#include <atomic> |
||||
#include <cstdint> |
||||
#include <thread> |
||||
#include <ext/concurrence.h> |
||||
|
||||
#define COUNT (1u << 24) |
||||
|
||||
// use "incl" for 32-bit and "incq" for 64-bit in asm below:
|
||||
// use 32-bit for now: _Atomic_word should be 32-bit too;
|
||||
// 64-bit is basically as fast as 32-bit if it fits in your cache
|
||||
typedef std::uint32_t counting; |
||||
|
||||
volatile counting __attribute__ ((aligned (64))) volatile_counter; |
||||
counting __attribute__ ((aligned (64))) simple_counter; |
||||
std::atomic<counting> __attribute__ ((aligned (64))) atomic_counter; |
||||
|
||||
_Atomic_word __attribute__ ((aligned (64))) gxx_counter; |
||||
|
||||
void benchmark_volatile() { |
||||
// should be the same as asm below
|
||||
volatile_counter = 0; |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
++volatile_counter; |
||||
} |
||||
} |
||||
|
||||
void benchmark_asm() { |
||||
simple_counter = 0; |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
asm volatile( |
||||
"incl %0;" |
||||
: "+m"(simple_counter) |
||||
); |
||||
} |
||||
} |
||||
|
||||
void benchmark_local_atomic() { |
||||
// compiler doesn't realize no one else can read this counter and
|
||||
// uses real atomic (i.e. "lock")
|
||||
std::atomic<counting> local_atomic_counter; |
||||
local_atomic_counter = 0; |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
++local_atomic_counter; |
||||
} |
||||
} |
||||
|
||||
void benchmark_atomic() { |
||||
// should be the same as "lock_asm"
|
||||
atomic_counter = 0; |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
++atomic_counter; |
||||
} |
||||
} |
||||
|
||||
void benchmark_lock_asm() { |
||||
simple_counter = 0; |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
asm volatile( |
||||
"lock incl %0;" |
||||
: "+m"(simple_counter) |
||||
); |
||||
} |
||||
} |
||||
|
||||
void benchmark_gxx_atomic() { |
||||
// if linked with pthread this will use "lock" same as atomic, otherwise
|
||||
// it uses a simple non-volatile non-atomic add instruction
|
||||
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_concurrency.html
|
||||
// claims __atomic_add_dispatch uses a volatile counter, but this isn't
|
||||
// true in gcc 4.9
|
||||
gxx_counter = 0; |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
__gnu_cxx::__atomic_add_dispatch(&gxx_counter, 1); |
||||
} |
||||
} |
||||
|
||||
|
||||
int main() { |
||||
// std::thread fake([] { return; });
|
||||
// fake.join();
|
||||
|
||||
std::cout << "volatile counter : "; |
||||
measure_time_with_dry_run(benchmark_volatile); |
||||
|
||||
std::cout << "asm counter : "; |
||||
measure_time_with_dry_run(benchmark_asm); |
||||
|
||||
std::cout << "local atomic counter: "; |
||||
measure_time_with_dry_run(benchmark_local_atomic); |
||||
|
||||
std::cout << "atomic counter : "; |
||||
measure_time_with_dry_run(benchmark_atomic); |
||||
|
||||
std::cout << "lock asm counter : "; |
||||
measure_time_with_dry_run(benchmark_lock_asm); |
||||
|
||||
std::cout << "gxx atomic counter : "; |
||||
measure_time_with_dry_run(benchmark_gxx_atomic); |
||||
} |
@ -0,0 +1,23 @@
|
||||
|
||||
#include "benchmark-shared-unique.h" |
||||
|
||||
void* forward_raw(void* p) { |
||||
return p; |
||||
} |
||||
|
||||
|
||||
std::unique_ptr<size_t> forward_unique(std::unique_ptr<size_t>&& p) { |
||||
return std::move(p); |
||||
} |
||||
|
||||
std::shared_ptr<size_t> forward_shared(std::shared_ptr<size_t>&& p) { |
||||
return std::move(p); |
||||
} |
||||
|
||||
std::unique_ptr<Large> forward_unique_large(std::unique_ptr<Large>&& p) { |
||||
return std::move(p); |
||||
} |
||||
|
||||
std::shared_ptr<Large> forward_shared_large(std::shared_ptr<Large>&& p) { |
||||
return std::move(p); |
||||
} |
@ -0,0 +1,128 @@
|
||||
|
||||
#include "benchmark-shared-unique.h" |
||||
|
||||
#include "measure_time.h" |
||||
#include "make_unique.h" |
||||
|
||||
|
||||
#define COUNT (1u << 26) |
||||
|
||||
void benchmark_forward_empty_raw() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_raw(nullptr); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_empty_unique() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_unique({}); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_empty_shared() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_shared({}); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_empty_shared_from_unique() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_shared(std::unique_ptr<size_t>{}); |
||||
} |
||||
} |
||||
|
||||
|
||||
void benchmark_forward_new_raw() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
size_t* p = new size_t{i}; |
||||
(void) forward_raw(p); |
||||
delete p; |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_new_unique() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_unique(std::make_unique<size_t>(i)); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_new_shared() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_shared(std::make_shared<size_t>(i)); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_new_shared_from_unique() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_shared(std::make_unique<size_t>(i)); |
||||
} |
||||
} |
||||
|
||||
|
||||
void benchmark_forward_new_raw_large() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
Large* p = new Large{i}; |
||||
(void) forward_raw(p); |
||||
delete p; |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_new_unique_large() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_unique_large(std::make_unique<Large>(i)); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_new_shared_large() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_shared_large(std::make_shared<Large>(i)); |
||||
} |
||||
} |
||||
|
||||
void benchmark_forward_new_shared_large_from_unique() { |
||||
for (size_t i = 0; i < COUNT; ++i) { |
||||
(void) forward_shared_large(std::make_unique<Large>(i)); |
||||
} |
||||
} |
||||
|
||||
int main() { |
||||
std::cout << "forward empty raw : "; |
||||
measure_time(benchmark_forward_empty_raw); |
||||
|
||||
std::cout << "forward empty unique : "; |
||||
measure_time(benchmark_forward_empty_unique); |
||||
|
||||
std::cout << "forward empty shared : "; |
||||
measure_time(benchmark_forward_empty_shared); |
||||
|
||||
std::cout << "forward empty shared from unique : "; |
||||
measure_time(benchmark_forward_empty_shared_from_unique); |
||||
|
||||
std::cout << "\n"; |
||||
|
||||
std::cout << "forward new raw : "; |
||||
measure_time(benchmark_forward_new_raw); |
||||
|
||||
std::cout << "forward new unique : "; |
||||
measure_time(benchmark_forward_new_unique); |
||||
|
||||
std::cout << "forward new shared : "; |
||||
measure_time(benchmark_forward_new_shared); |
||||
|
||||
std::cout << "forward new shared from unique : "; |
||||
measure_time(benchmark_forward_new_shared_from_unique); |
||||
|
||||
std::cout << "\n"; |
||||
|
||||
std::cout << "forward new raw large : "; |
||||
measure_time(benchmark_forward_new_raw_large); |
||||
|
||||
std::cout << "forward new unique large : "; |
||||
measure_time(benchmark_forward_new_unique_large); |
||||
|
||||
std::cout << "forward new shared large : "; |
||||
measure_time(benchmark_forward_new_shared_large); |
||||
|
||||
std::cout << "forward new shared large from unique: "; |
||||
measure_time(benchmark_forward_new_shared_large_from_unique); |
||||
} |
@ -0,0 +1,16 @@
|
||||
#include <memory> |
||||
|
||||
struct Large { |
||||
size_t x; |
||||
char filler[512 - sizeof(size_t)] = {}; |
||||
|
||||
Large(size_t x) : x(x) {} |
||||
}; |
||||
|
||||
void* forward_raw(void* p); |
||||
|
||||
std::unique_ptr<size_t> forward_unique(std::unique_ptr<size_t>&& p); |
||||
std::shared_ptr<size_t> forward_shared(std::shared_ptr<size_t>&& p); |
||||
|
||||
std::unique_ptr<Large> forward_unique_large(std::unique_ptr<Large>&& p); |
||||
std::shared_ptr<Large> forward_shared_large(std::shared_ptr<Large>&& p); |
@ -0,0 +1,36 @@
|
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
|
||||
#if __cplusplus <= 201103L |
||||
|
||||
namespace { |
||||
template<class T> |
||||
struct is_unbound_array : std::false_type {}; |
||||
|
||||
template<class T> |
||||
struct is_unbound_array<T[]> : std::true_type {}; |
||||
|
||||
template<class T> |
||||
struct is_bound_array : std::false_type {}; |
||||
|
||||
template<class T, std::size_t N> |
||||
struct is_bound_array<T[N]> : std::true_type {}; |
||||
} |
||||
|
||||
namespace std { |
||||
template<typename T, typename... Args> |
||||
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(Args&&... args) { |
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); |
||||
} |
||||
|
||||
template<typename T> |
||||
typename std::enable_if<is_unbound_array<T>::value, std::unique_ptr<T>>::type make_unique(size_t size) { |
||||
return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]()); |
||||
} |
||||
|
||||
template< class T, class... Args > |
||||
typename std::enable_if<is_bound_array<T>::value>::type make_unique( Args&&... args ) = delete; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,20 @@
|
||||
#pragma once |
||||
|
||||
#include <chrono> |
||||
#include <iostream> |
||||
|
||||
template<typename Callable> |
||||
void measure_time(Callable&& callable) { |
||||
using namespace std::chrono; |
||||
steady_clock::time_point t_start = steady_clock::now(); |
||||
callable(); |
||||
duration<double> time_span = duration_cast<duration<double>>(steady_clock::now() - t_start); |
||||
|
||||
std::cout << "took " << time_span.count() << " seconds\n"; |
||||
} |
||||
|
||||
template<typename Callable> |
||||
void measure_time_with_dry_run(Callable&& callable) { |
||||
callable(); |
||||
measure_time(std::forward<Callable>(callable)); |
||||
} |
@ -0,0 +1,15 @@
|
||||
|
||||
#include "type_name.h" |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
#include <iostream> |
||||
|
||||
int main() { |
||||
auto o = NULL; |
||||
int* n(reinterpret_cast<int*>(o)); |
||||
// auto n = std::make_pair(0, (intptr_t) NULL);
|
||||
std::cout << "decltype(n) is " << TYPE_NAME(n) << '\n'; |
||||
auto p = std::make_pair(std::ref(o), nullptr); |
||||
std::cout << "decltype(p) is " << TYPE_NAME(p) << '\n'; |
||||
} |
@ -0,0 +1,30 @@
|
||||
|
||||
#include <iostream> |
||||
|
||||
#define L(s) std::cerr << s << "\n" |
||||
|
||||
struct A { |
||||
A() { L("A::A()"); } |
||||
A(A&&) { L("A::A(A&&)"); } |
||||
A(const A&) { L("A::A(const A&)"); } |
||||
~A() { L("A::~A()"); } |
||||
}; |
||||
|
||||
struct B: A { |
||||
B() { L("B::B()"); } |
||||
B(A&&) { L("B::B(A&&)"); } |
||||
//B(B&&) { L("B::B(B&&)"); }
|
||||
B(const A&) { L("B::B(const A&)"); } |
||||
// B(const B&) { L("B::B(const B&)"); }
|
||||
~B() { L("B::~B()"); } |
||||
}; |
||||
|
||||
int main() { |
||||
L("A a1;"); |
||||
A a1; |
||||
L("B b1(a1);"); |
||||
B b1(a1); |
||||
L("B b1(b1);"); |
||||
B b2(static_cast<A&>(b1)); |
||||
return 0; |
||||
} |
@ -0,0 +1,22 @@
|
||||
#include <algorithm> |
||||
#include <memory> |
||||
#include <iostream> |
||||
|
||||
class C { |
||||
public: |
||||
int valid; |
||||
C() : valid(1) { } |
||||
~C() { valid = 0; } |
||||
}; |
||||
|
||||
/* some "random" comparison, doesn't matter */ |
||||
bool operator<(C const& a, C const& b) { |
||||
return reinterpret_cast<uintptr_t>(&a) < reinterpret_cast<uintptr_t>(&b); |
||||
} |
||||
|
||||
int main() { |
||||
auto const& m = |
||||
// std::max(*std::make_shared<C>(), *std::make_shared<C>());
|
||||
std::max(C(), C()); |
||||
std::cout << m.valid << "\n"; |
||||
} |
@ -0,0 +1,35 @@
|
||||
|
||||
#include <exception> |
||||
#include <memory> |
||||
#include <type_traits> |
||||
|
||||
#include "make_unique.h" |
||||
|
||||
struct C { |
||||
private: |
||||
struct private_type { }; |
||||
C(private_type) { } |
||||
int *m_i = nullptr; |
||||
|
||||
public: |
||||
C() : C(private_type()) { m_i = new int; throw std::exception(); } |
||||
~C() { delete m_i; } |
||||
C(C const&) = delete; |
||||
C& operator=(C const&) = delete; |
||||
}; |
||||
|
||||
|
||||
struct D { |
||||
std::unique_ptr<int> m_i; |
||||
D() : m_i(std::make_unique<int>()) { m_i = std::make_unique<int>(); throw std::exception(); } |
||||
}; |
||||
|
||||
int main() { |
||||
try { |
||||
C c; |
||||
} catch (...) { } |
||||
|
||||
auto a = std::make_unique<int[]>(10); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,10 @@
|
||||
|
||||
#include <memory> |
||||
|
||||
void copy_ptr(std::shared_ptr<int>& a, std::shared_ptr<int> const& b) { |
||||
a = b; |
||||
} |
||||
|
||||
void move_ptr(std::shared_ptr<int>& a, std::shared_ptr<int>& b) { |
||||
a = std::move(b); |
||||
} |
@ -0,0 +1,8 @@
|
||||
|
||||
#include <memory> |
||||
|
||||
void copy_ptr(std::shared_ptr<int>& a, std::shared_ptr<int> const& b); |
||||
void move_ptr(std::shared_ptr<int>& a, std::shared_ptr<int>& b); |
||||
|
||||
int main() { |
||||
} |
@ -0,0 +1,23 @@
|
||||
|
||||
#include "type_name.h" |
||||
|
||||
#include <iostream> |
||||
|
||||
void f(const int&) { |
||||
} |
||||
|
||||
#define T(v) std::cout << #v << ": " << TYPE_NAME(v) << "\n" |
||||
|
||||
int main() { |
||||
auto&& i = 1; |
||||
auto& ri = i; |
||||
auto const& x = std::move(ri); |
||||
decltype(ri) y = ri; |
||||
f(i); |
||||
// f(std::forward(i));
|
||||
T(i); |
||||
T(ri); |
||||
T(x); |
||||
T(y); |
||||
return 0; |
||||
} |
@ -0,0 +1,118 @@
|
||||
|
||||
#include <memory> |
||||
#include <type_traits> |
||||
|
||||
class C { |
||||
protected: |
||||
struct Protected { |
||||
private: |
||||
Protected() {}; |
||||
public: |
||||
static Protected make() { return { }; } |
||||
}; |
||||
int m_x; |
||||
|
||||
private: |
||||
struct Private { |
||||
private: |
||||
Private() {}; |
||||
public: |
||||
static Private make() { return { }; } |
||||
}; |
||||
int m_y; |
||||
|
||||
public: |
||||
/* private constructor: */ |
||||
C(Private, int x, int y) : m_x(x), m_y(y) { } |
||||
/* protected constructor: */ |
||||
C(Protected, int x) : C(Private::make(), x, 2*x) { } |
||||
|
||||
void leak_private_type(Private) { } |
||||
|
||||
static std::unique_ptr<C> Create(int x) { |
||||
return std::unique_ptr<C>(new C(Private::make(), x, 2*x)); |
||||
} |
||||
}; |
||||
|
||||
class D : public C { |
||||
private: |
||||
struct Private { }; |
||||
public: |
||||
D(Private&, int x) : C(Protected::make(), x) { } |
||||
|
||||
static std::shared_ptr<D> Create(int x) { |
||||
Private p; |
||||
return std::make_shared<D>(p, 4*x); |
||||
} |
||||
}; |
||||
|
||||
|
||||
class A { |
||||
struct this_is_private { |
||||
}; |
||||
|
||||
public: |
||||
explicit A(const this_is_private &, int) {} |
||||
|
||||
static ::std::shared_ptr<A> create(int x) { |
||||
return ::std::make_shared<A>(this_is_private{}, x); |
||||
} |
||||
|
||||
protected: |
||||
A(const A &) = delete; |
||||
const A &operator =(const A &) = delete; |
||||
}; |
||||
|
||||
::std::shared_ptr<A> foo(int x) |
||||
{ |
||||
return A::create(x); |
||||
} |
||||
|
||||
|
||||
/* usage: decltype(return_args(&Class::memberfunction)) */ |
||||
template<typename Base, typename Result, typename... Args> |
||||
std::tuple<Args...> return_args(Result (Base::*func)(Args...)); |
||||
|
||||
/* usage: decltype(result_of(&Class::memberfunction)) */ |
||||
template<typename Base, typename Result, typename... Args> |
||||
Result result_of(Result (Base::*func)(Args...)); |
||||
|
||||
/* usage: decay_tuple_element<0, decltype(return_args(&Class::memberfunction))> */ |
||||
template <size_t i, typename Tuple> |
||||
using decay_tuple_element = typename std::decay<typename std::tuple_element<i, Tuple>::type>::type; |
||||
|
||||
|
||||
struct _is_static_castable_impl |
||||
{ |
||||
template<typename _From, typename _To, typename = decltype(static_cast<_To>(std::declval<_From>()))> |
||||
static std::true_type __test(int); |
||||
|
||||
template<typename, typename> |
||||
static std::false_type __test(...); |
||||
}; |
||||
|
||||
template<typename _From, typename _To> |
||||
struct is_static_castable |
||||
: public _is_static_castable_impl |
||||
{ |
||||
typedef decltype(__test<_From, _To>(0)) type; |
||||
}; |
||||
|
||||
template<typename _From, typename _To> |
||||
using is_static_castable_t = typename is_static_castable<_From, _To>::type; |
||||
|
||||
|
||||
|
||||
int main() { |
||||
auto c = C::Create(0); |
||||
auto d = D::Create(0); |
||||
|
||||
typedef decay_tuple_element<0, decltype(return_args(&C::leak_private_type))> Private; |
||||
Private::make(); |
||||
|
||||
|
||||
auto a1 = foo(0); |
||||
auto a2 = std::shared_ptr<A>(new A({}, 0)); |
||||
|
||||
// C c1({}, 0, 0);
|
||||
} |
@ -0,0 +1,38 @@
|
||||
#pragma once |
||||
|
||||
#include <type_traits> |
||||
#include <typeinfo> |
||||
#ifndef _MSC_VER |
||||
# include <cxxabi.h> |
||||
#endif |
||||
#include <memory> |
||||
#include <string> |
||||
#include <cstdlib> |
||||
|
||||
template <class T> |
||||
std::string |
||||
type_name() |
||||
{ |
||||
typedef typename std::remove_reference<T>::type TR; |
||||
std::unique_ptr<char, void(*)(void*)> own |
||||
( |
||||
#ifndef _MSC_VER |
||||
abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), |
||||
#else |
||||
nullptr, |
||||
#endif |
||||
std::free |
||||
); |
||||
std::string r = own != nullptr ? own.get() : typeid(TR).name(); |
||||
if (std::is_const<TR>::value) |
||||
r += " const"; |
||||
if (std::is_volatile<TR>::value) |
||||
r += " volatile"; |
||||
if (std::is_lvalue_reference<T>::value) |
||||
r += "&"; |
||||
else if (std::is_rvalue_reference<T>::value) |
||||
r += "&&"; |
||||
return r; |
||||
} |
||||
|
||||
#define TYPE_NAME(p) (type_name<decltype(p)>()) |
Loading…
Reference in new issue