Skip to content

Commit

Permalink
Add new style meta-programming primatives.
Browse files Browse the repository at this point in the history
Using class templates instead of alias templates causes a lot of
instantiations. As part of the move away from C++03, we want to
improve the efficiency of our meta-programming.

This patch lays the groundwork by introducing new _If, _EnableIf,
_And, _Or, and _IsValidExpansion (detect member). Future patches
will replace the existing implementations after verifying there
compile time differences.

llvm-svn: 364114
  • Loading branch information
EricWF committed Jun 21, 2019
1 parent 892f022 commit 51a741c
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 28 deletions.
115 changes: 87 additions & 28 deletions libcxx/include/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,93 @@ template <class _T1, class _T2> struct _LIBCPP_TEMPLATE_VIS pair;
template <class _Tp> class _LIBCPP_TEMPLATE_VIS reference_wrapper;
template <class _Tp> struct _LIBCPP_TEMPLATE_VIS hash;


template <class _Tp, _Tp __v>
struct _LIBCPP_TEMPLATE_VIS integral_constant
{
static _LIBCPP_CONSTEXPR const _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant type;
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR operator value_type() const _NOEXCEPT {return value;}
#if _LIBCPP_STD_VER > 11
_LIBCPP_INLINE_VISIBILITY
constexpr value_type operator ()() const _NOEXCEPT {return value;}
#endif
};

template <class _Tp, _Tp __v>
_LIBCPP_CONSTEXPR const _Tp integral_constant<_Tp, __v>::value;

#if _LIBCPP_STD_VER > 14
template <bool __b>
using bool_constant = integral_constant<bool, __b>;
#define _LIBCPP_BOOL_CONSTANT(__b) bool_constant<(__b)>
#else
#define _LIBCPP_BOOL_CONSTANT(__b) integral_constant<bool,(__b)>
#endif

typedef _LIBCPP_BOOL_CONSTANT(true) true_type;
typedef _LIBCPP_BOOL_CONSTANT(false) false_type;

template <bool _Val>
using _BoolConstant _LIBCPP_NODEBUG_TYPE = integral_constant<bool, _Val>;

template <bool> struct _MetaBase;
template <>
struct _MetaBase<true> {
template <class _Tp, class _Up>
using _SelectImpl _LIBCPP_NODEBUG_TYPE = _Tp;
template <template <class...> class _FirstFn, template <class...> class, class ..._Args>
using _SelectApplyImpl _LIBCPP_NODEBUG_TYPE = _FirstFn<_Args...>;
template <class _First, class...>
using _FirstImpl _LIBCPP_NODEBUG_TYPE = _First;
template <class, class _Second, class...>
using _SecondImpl _LIBCPP_NODEBUG_TYPE = _Second;
template <class _Tp = void>
using _EnableIfImpl _LIBCPP_NODEBUG_TYPE = _Tp;
template <class _Result, class _First, class ..._Rest>
using _OrImpl _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_First::type::value != true && sizeof...(_Rest) != 0>::template _OrImpl<_First, _Rest...>;
template <class _Result, class _First, class ..._Rest>
using _AndImpl _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_First::type::value == true && sizeof...(_Rest) != 0>::template _AndImpl<_First, _Rest...>;
};

template <>
struct _MetaBase<false> {
template <class _Tp, class _Up>
using _SelectImpl _LIBCPP_NODEBUG_TYPE = _Up;
template <template <class...> class, template <class...> class _SecondFn, class ..._Args>
using _SelectApplyImpl _LIBCPP_NODEBUG_TYPE = _SecondFn<_Args...>;
template <class _Result, class ...>
using _OrImpl _LIBCPP_NODEBUG_TYPE = _Result;
template <class _Result, class ...>
using _AndImpl _LIBCPP_NODEBUG_TYPE = _Result;
};
template <bool _Cond, class _Ret = void>
using _EnableIf _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_Cond>::template _EnableIfImpl<_Ret>;
template <bool _Cond, class _IfRes, class _ElseRes>
using _If _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
template <class ..._Rest>
using _Or _LIBCPP_NODEBUG_TYPE = typename _MetaBase< sizeof...(_Rest) != 0 >::template _OrImpl<false_type, _Rest...>;
template <class ..._Rest>
using _And _LIBCPP_NODEBUG_TYPE = typename _MetaBase< sizeof...(_Rest) != 0 >::template _AndImpl<true_type, _Rest...>;
template <class _Pred>
struct _Not : _BoolConstant<!_Pred::type::value> {};
template <class ..._Args>
using _FirstType _LIBCPP_NODEBUG_TYPE = typename _MetaBase<(sizeof...(_Args) >= 1)>::template _FirstImpl<_Args...>;
template <class ..._Args>
using _SecondType _LIBCPP_NODEBUG_TYPE = typename _MetaBase<(sizeof...(_Args) >= 2)>::template _SecondImpl<_Args...>;

// Member detector base

template <template <class...> class _Templ, class ..._Args>
true_type __sfinae_test_impl(_FirstType<int, _Templ<_Args...> >);
template <template <class...> class, class ...>
false_type __sfinae_test_impl(...);

template <template <class ...> class _Templ, class ..._Args>
using _IsValidExpansion _LIBCPP_NODEBUG_TYPE = decltype(std::__sfinae_test_impl<_Templ, _Args...>(0));

template <class>
struct __void_t { typedef void type; };

Expand Down Expand Up @@ -528,34 +615,6 @@ struct __two {char __lx[2];};

// helper class:

template <class _Tp, _Tp __v>
struct _LIBCPP_TEMPLATE_VIS integral_constant
{
static _LIBCPP_CONSTEXPR const _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant type;
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR operator value_type() const _NOEXCEPT {return value;}
#if _LIBCPP_STD_VER > 11
_LIBCPP_INLINE_VISIBILITY
constexpr value_type operator ()() const _NOEXCEPT {return value;}
#endif
};

template <class _Tp, _Tp __v>
_LIBCPP_CONSTEXPR const _Tp integral_constant<_Tp, __v>::value;

#if _LIBCPP_STD_VER > 14
template <bool __b>
using bool_constant = integral_constant<bool, __b>;
#define _LIBCPP_BOOL_CONSTANT(__b) bool_constant<(__b)>
#else
#define _LIBCPP_BOOL_CONSTANT(__b) integral_constant<bool,(__b)>
#endif

typedef _LIBCPP_BOOL_CONSTANT(true) true_type;
typedef _LIBCPP_BOOL_CONSTANT(false) false_type;

#if !defined(_LIBCPP_CXX03_LANG)

// __lazy_and
Expand Down
91 changes: 91 additions & 0 deletions libcxx/test/libcxx/utilities/meta/meta_base.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//

#include <type_traits>
#include <cassert>

#include "test_macros.h"

struct Bomb;
template <int N, class T = Bomb >
struct BOOM {
using Explode = typename T::BOOMBOOM;
};

using True = std::true_type;
using False = std::false_type;

void test_if() {
ASSERT_SAME_TYPE(std::_If<true, int, long>, int);
ASSERT_SAME_TYPE(std::_If<false, int, long>, long);
}

void test_and() {
static_assert(std::_And<True>::value, "");
static_assert(!std::_And<False>::value, "");
static_assert(std::_And<True, True>::value, "");
static_assert(!std::_And<False, BOOM<1> >::value, "");
static_assert(!std::_And<True, True, True, False, BOOM<2> >::value, "");
}

void test_or() {
static_assert(std::_Or<True>::value, "");
static_assert(!std::_Or<False>::value, "");
static_assert(std::_Or<False, True>::value, "");
static_assert(std::_Or<True, std::_Not<BOOM<3> > >::value, "");
static_assert(!std::_Or<False, False>::value, "");
static_assert(std::_Or<True, BOOM<1> >::value, "");
static_assert(std::_Or<False, False, False, False, True, BOOM<2> >::value, "");
}

void test_combined() {
static_assert(std::_And<True, std::_Or<False, True, BOOM<4> > >::value, "");
static_assert(std::_And<True, std::_Or<False, True, BOOM<4> > >::value, "");
static_assert(std::_Not<std::_And<True, False, BOOM<5> > >::value, "");
}

struct MemberTest {
static int foo;
using type = long;

void func(int);
};
struct Empty {};
struct MemberTest2 {
using foo = int;
};
template <class T>
using HasFooData = decltype(T::foo);
template <class T>
using HasFooType = typename T::foo;

template <class T, class U>
using FuncCallable = decltype(std::declval<T>().func(std::declval<U>()));
template <class T>
using BadCheck = typename T::DOES_NOT_EXIST;

void test_is_valid_trait() {
static_assert(std::_IsValidExpansion<HasFooData, MemberTest>::value, "");
static_assert(!std::_IsValidExpansion<HasFooType, MemberTest>::value, "");
static_assert(!std::_IsValidExpansion<HasFooData, MemberTest2>::value, "");
static_assert(std::_IsValidExpansion<HasFooType, MemberTest2>::value, "");
static_assert(std::_IsValidExpansion<FuncCallable, MemberTest, int>::value, "");
static_assert(!std::_IsValidExpansion<FuncCallable, MemberTest, void*>::value, "");
}

void test_first_and_second_type() {
ASSERT_SAME_TYPE(std::_FirstType<int, long, void*>, int);
ASSERT_SAME_TYPE(std::_FirstType<char>, char);
ASSERT_SAME_TYPE(std::_SecondType<char, long>, long);
ASSERT_SAME_TYPE(std::_SecondType<long long, int, void*>, int);
}

int main(int, char**) {
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This is a dummy feature that prevents this test from running by default.
// REQUIRES: template-const-testing

// The table below compares the compile time and object size for each of the
// variants listed in the RUN script.
//
// Impl Compile Time Object Size
// -------------------------------------------
// _And: 3,498.639 ms 158 M
// __lazy_and: 10,138.982 ms 334 M
// __and_: 14,181.851 ms 648 M
//

// RUN: %cxx %flags %compile_flags -c %s -o %S/new.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17
// RUN: %cxx %flags %compile_flags -c %s -o %S/lazy.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 -DTEST_LAZY_AND
// RUN: %cxx %flags %compile_flags -c %s -o %S/std.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 -DTEST_STD_AND

#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "template_cost_testing.h"
using std::true_type;
using std::false_type;

#define FALSE_T() std::false_type,
#define TRUE_T() std::true_type,

#ifdef TEST_LAZY_AND
#define TEST_AND std::__lazy_and
#define TEST_OR std::__lazy_or
#elif defined(TEST_STD_AND)
#define TEST_AND std::__and_
#define TEST_OR std::__or_
#else
#define TEST_AND std::_And
#define TEST_OR std::_Or
#endif

void sink(...);

void Foo1(TEST_AND < REPEAT_1000(TRUE_T) true_type > t1) { sink(&t1); }
void Foo2(TEST_AND < REPEAT_1000(TRUE_T) REPEAT_1000(TRUE_T) true_type > t2) { sink(&t2); }
void Foo3(TEST_AND < REPEAT_1000(TRUE_T) true_type, false_type > t3) { sink(&t3); }
void Foo4(TEST_AND < REPEAT_1000(TRUE_T) REPEAT_1000(TRUE_T) true_type, false_type > t4) { sink(&t4); }
void Foo5(TEST_AND < false_type, REPEAT_1000(TRUE_T) true_type > t5) { sink(&t5); }
void Foo6(TEST_AND < false_type, REPEAT_1000(TRUE_T) REPEAT_1000(TRUE_T) true_type > t6) { sink(&t6); }

void escape() {

sink(&Foo1);
sink(&Foo2);
sink(&Foo3);
sink(&Foo4);
sink(&Foo5);
sink(&Foo6);
}


0 comments on commit 51a741c

Please sign in to comment.