summaryrefslogtreecommitdiff
path: root/include/ftl/details/function.h
blob: 35c5a8b30201a934de8c99b57ba78d60dd65a6f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>

namespace android::ftl::details {

// The maximum allowed value for the template argument `N` in
// `ftl::Function<F, N>`.
constexpr size_t kFunctionMaximumN = 14;

// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member
// function type `Ret(Args...)`.

template <typename>
struct remove_member_function_pointer;

template <typename Class, typename Ret, typename... Args>
struct remove_member_function_pointer<Ret (Class::*)(Args...)> {
  using type = Ret(Args...);
};

template <typename Class, typename Ret, typename... Args>
struct remove_member_function_pointer<Ret (Class::*)(Args...) const> {
  using type = Ret(Args...);
};

template <auto MemberFunction>
using remove_member_function_pointer_t =
    typename remove_member_function_pointer<decltype(MemberFunction)>::type;

// Helper functions for binding to the supported targets.

template <typename Ret, typename... Args>
auto bind_opaque_no_op() -> Ret (*)(void*, Args...) {
  return [](void*, Args...) -> Ret {
    if constexpr (!std::is_void_v<Ret>) {
      return Ret{};
    }
  };
}

template <typename F, typename Ret, typename... Args>
auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) {
  return [](void* opaque, Args... args) -> Ret {
    return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...);
  };
}

template <auto MemberFunction, typename Class, typename Ret, typename... Args>
auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) {
  return [instance](Args... args) -> Ret {
    return std::invoke(MemberFunction, instance, std::forward<Args>(args)...);
  };
}

template <auto FreeFunction, typename Ret, typename... Args>
auto bind_free_function(Ret (*)(Args...) = nullptr) {
  return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); };
}

// Traits class for the opaque storage used by Function.

template <std::size_t N>
struct function_opaque_storage {
  // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size,
  // which allows a lambda with zero or one capture args.
  using type = std::array<std::intptr_t, N + 1>;

  template <typename S>
  static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>;

  template <typename S>
  static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>;

  template <typename S>
  static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type);

  template <typename S>
  static constexpr bool require_alignment_compatible =
      std::alignment_of_v<S> <= std::alignment_of_v<type>;

  // Copies `src` into the opaque storage, and returns that storage.
  template <typename S>
  static type opaque_copy(const S& src) {
    // TODO: Replace with C++20 concepts/constraints which can give more details.
    static_assert(require_trivially_copyable<S>,
                  "ftl::Function can only store lambdas that capture trivially copyable data.");
    static_assert(
        require_trivially_destructible<S>,
        "ftl::Function can only store lambdas that capture trivially destructible data.");
    static_assert(require_will_fit_in_opaque_storage<S>,
                  "ftl::Function has limited storage for lambda captured state. Maybe you need to "
                  "increase N?");
    static_assert(require_alignment_compatible<S>);

    type opaque;
    std::memcpy(opaque.data(), &src, sizeof(S));
    return opaque;
  }
};

// Traits class to help determine the template parameters to use for a ftl::Function, given a
// function object.

template <typename F, typename = decltype(&F::operator())>
struct function_traits {
  // The function type `F` with which to instantiate the `Function<F, N>` template.
  using type = remove_member_function_pointer_t<&F::operator()>;

  // The (minimum) size `N` with which to instantiate the `Function<F, N>` template.
  static constexpr std::size_t size =
      (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t);
};

}  // namespace android::ftl::details