Skip to content

Commit 430b009

Browse files
committed
[libc++] Add support for blocks in std::function
rdar://14390808
1 parent b3f1682 commit 430b009

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

libcxx/include/functional

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,10 @@ POLICY: For non-variadic implementations, the number of arguments is limited
508508

509509
#include <__functional_base>
510510

511+
#if defined(_LIBCPP_HAS_EXTENSION_BLOCKS) && !defined(_LIBCPP_HAS_OBJC_ARC)
512+
#include <Block.h>
513+
#endif
514+
511515
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
512516
#pragma GCC system_header
513517
#endif
@@ -1484,6 +1488,12 @@ template <class _Fp>
14841488
_LIBCPP_INLINE_VISIBILITY
14851489
bool __not_null(function<_Fp> const& __f) { return !!__f; }
14861490

1491+
#ifdef _LIBCPP_HAS_EXTENSION_BLOCKS
1492+
template <class _Rp, class ..._Args>
1493+
_LIBCPP_INLINE_VISIBILITY
1494+
bool __not_null(_Rp (^__p)(_Args...)) { return __p; }
1495+
#endif
1496+
14871497
} // namespace __function
14881498

14891499
#ifndef _LIBCPP_CXX03_LANG
@@ -2245,6 +2255,72 @@ template <class _Rp, class... _ArgTypes> class __policy_func<_Rp(_ArgTypes...)>
22452255
#endif // _LIBCPP_NO_RTTI
22462256
};
22472257

2258+
#if defined(_LIBCPP_HAS_EXTENSION_BLOCKS) && !defined(_LIBCPP_HAS_OBJC_ARC)
2259+
2260+
template<class _Rp1, class ..._ArgTypes1, class _Alloc, class _Rp, class ..._ArgTypes>
2261+
class __func<_Rp1(^)(_ArgTypes1...), _Alloc, _Rp(_ArgTypes...)>
2262+
: public __base<_Rp(_ArgTypes...)>
2263+
{
2264+
typedef _Rp1(^__block_type)(_ArgTypes1...);
2265+
__block_type __f_;
2266+
2267+
public:
2268+
_LIBCPP_INLINE_VISIBILITY
2269+
explicit __func(__block_type const& __f)
2270+
: __f_(__f ? Block_copy(__f) : (__block_type)0)
2271+
{ }
2272+
2273+
// [TODO] add && to save on a retain
2274+
2275+
_LIBCPP_INLINE_VISIBILITY
2276+
explicit __func(__block_type __f, const _Alloc& /* unused */)
2277+
: __f_(__f ? Block_copy(__f) : (__block_type)0)
2278+
{ }
2279+
2280+
virtual __base<_Rp(_ArgTypes...)>* __clone() const {
2281+
_LIBCPP_ASSERT(false,
2282+
"Block pointers are just pointers, so they should always fit into "
2283+
"std::function's small buffer optimization. This function should "
2284+
"never be invoked.");
2285+
return nullptr;
2286+
}
2287+
2288+
virtual void __clone(__base<_Rp(_ArgTypes...)>* __p) const {
2289+
::new (__p) __func(__f_);
2290+
}
2291+
2292+
virtual void destroy() _NOEXCEPT {
2293+
if (__f_)
2294+
Block_release(__f_);
2295+
__f_ = 0;
2296+
}
2297+
2298+
virtual void destroy_deallocate() _NOEXCEPT {
2299+
_LIBCPP_ASSERT(false,
2300+
"Block pointers are just pointers, so they should always fit into "
2301+
"std::function's small buffer optimization. This function should "
2302+
"never be invoked.");
2303+
}
2304+
2305+
virtual _Rp operator()(_ArgTypes&& ... __arg) {
2306+
return __invoke(__f_, _VSTD::forward<_ArgTypes>(__arg)...);
2307+
}
2308+
2309+
#ifndef _LIBCPP_NO_RTTI
2310+
virtual const void* target(type_info const& __ti) const _NOEXCEPT {
2311+
if (__ti == typeid(__func::__block_type))
2312+
return &__f_;
2313+
return (const void*)nullptr;
2314+
}
2315+
2316+
virtual const std::type_info& target_type() const _NOEXCEPT {
2317+
return typeid(__func::__block_type);
2318+
}
2319+
#endif // _LIBCPP_NO_RTTI
2320+
};
2321+
2322+
#endif // _LIBCPP_HAS_EXTENSION_BLOCKS && !_LIBCPP_HAS_OBJC_ARC
2323+
22482324
} // __function
22492325

22502326
template<class _Rp, class ..._ArgTypes>
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// std::function support for the "blocks" extension
10+
11+
// UNSUPPORTED: c++98, c++03
12+
// REQUIRES: has-fblocks
13+
14+
// RUN: %{build} -fblocks
15+
// RUN: %{run}
16+
17+
#include <functional>
18+
#include <cstdlib>
19+
#include <cassert>
20+
21+
#include "test_macros.h"
22+
#include "count_new.h"
23+
24+
25+
struct A {
26+
static int count;
27+
int id_;
28+
explicit A(int id) { ++count; id_ = id; }
29+
A(const A &a) { id_ = a.id_; ++count; }
30+
~A() { id_ = -1; --count; }
31+
int operator()() const { return -1; }
32+
int operator()(int i) const { return i; }
33+
int operator()(int, int) const { return -2; }
34+
int operator()(int, int, int) const { return -3; }
35+
int id() const { return id_; }
36+
};
37+
38+
int A::count = 0;
39+
40+
int g(int) { return 0; }
41+
42+
int main(int, char**)
43+
{
44+
// swap
45+
{
46+
std::function<int(int)> f1 = g;
47+
std::function<int(int)> f2 = ^(int x) { return x + 1; };
48+
assert(globalMemCounter.checkOutstandingNewEq(0));
49+
assert(*f1.target<int(*)(int)>() == g);
50+
assert(*f2.target<int(^)(int)>() != 0);
51+
swap(f1, f2);
52+
assert(globalMemCounter.checkOutstandingNewEq(0));
53+
assert(*f1.target<int(^)(int)>() != 0);
54+
assert(*f2.target<int(*)(int)>() == g);
55+
}
56+
57+
// operator bool
58+
{
59+
std::function<int(int)> f;
60+
assert(!f);
61+
f = ^(int x) { return x+1; };
62+
assert(f);
63+
}
64+
65+
// operator()
66+
{
67+
std::function<int ()> r1(^{ return 4; });
68+
assert(r1() == 4);
69+
}
70+
{
71+
__block bool called = false;
72+
std::function<void ()> r1(^{ called = true; });
73+
r1();
74+
assert(called);
75+
}
76+
{
77+
__block int param = 0;
78+
std::function<void (int)> r1(^(int x){ param = x; });
79+
r1(4);
80+
assert(param == 4);
81+
}
82+
{
83+
std::function<int (int)> r1(^(int x){ return x + 4; });
84+
assert(r1(3) == 7);
85+
}
86+
{
87+
__block int param1 = 0;
88+
__block int param2 = 0;
89+
std::function<void (int, int)> r1(^(int x, int y){ param1 = x; param2 = y; });
90+
r1(3, 4);
91+
assert(param1 == 3);
92+
assert(param2 == 4);
93+
}
94+
{
95+
std::function<int (int, int)> r1(^(int x, int y){ return x + y; });
96+
assert(r1(3, 4) == 7);
97+
}
98+
99+
// swap
100+
{
101+
std::function<int(int)> f1 = A(999);
102+
std::function<int(int)> f2 = ^(int x) { return x + 1; };
103+
assert(A::count == 1);
104+
assert(globalMemCounter.checkOutstandingNewEq(1));
105+
assert(f1.target<A>()->id() == 999);
106+
assert((*f2.target<int(^)(int)>())(13) == 14);
107+
f1.swap(f2);
108+
assert(A::count == 1);
109+
assert(globalMemCounter.checkOutstandingNewEq(1));
110+
assert((*f1.target<int(^)(int)>())(13) == 14);
111+
assert(f2.target<A>()->id() == 999);
112+
}
113+
assert(globalMemCounter.checkOutstandingNewEq(0));
114+
assert(A::count == 0);
115+
116+
// operator== and operator!=
117+
{
118+
std::function<int(int)> f;
119+
assert(f == nullptr);
120+
assert(nullptr == f);
121+
f = ^(int x) { return x + 1; };
122+
assert(f != nullptr);
123+
assert(nullptr != f);
124+
}
125+
126+
// target
127+
{
128+
int (^block)(int) = Block_copy(^(int x) { return x + 1; });
129+
std::function<int(int)> f = block;
130+
assert(*f.target<int(^)(int)>() == block);
131+
assert(f.target<int(*)(int)>() == 0);
132+
Block_release(block);
133+
}
134+
135+
// target_type
136+
{
137+
std::function<int(int)> f = ^(int x) { return x + 1; };
138+
assert(f.target_type() == typeid(int(^)(int)));
139+
}
140+
141+
return 0;
142+
}

0 commit comments

Comments
 (0)