Skip to content

Commit 363c352

Browse files
committed
Add ability to place Thrust in a custom namespace.
This provides a workaround for downstream projects that encounter a variety of issues from dynamically linking multiple libraries that use Thrust. See the new `thrust/detail/config/namespace.h` header for details. Added several tests and checks to validate that this behavior is correct, and the `__THRUST_DEFINE_HAS_MEMBER_FUNCTION` utility has been rewritten to WAR an nvcc bug when the old implementation was used with objects in an anonymous namespace. New tests: - testing/namespace_wrapped.cu - testing/cmake/check_namespace.cmake
1 parent 115cdd4 commit 363c352

File tree

623 files changed

+2397
-2639
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

623 files changed

+2397
-2639
lines changed

cmake/ThrustHeaderTesting.cmake

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ foreach(thrust_target IN LISTS THRUST_TARGETS)
118118
set(headertest_target ${config_prefix}.headers)
119119
add_library(${headertest_target} OBJECT ${headertest_srcs})
120120
target_link_libraries(${headertest_target} PUBLIC ${thrust_target})
121+
# Wrap Thrust/CUB in a custom namespace to check proper use of ns macros:
122+
target_compile_definitions(${headertest_target} PRIVATE
123+
"THRUST_WRAPPED_NAMESPACE=wrapped_thrust"
124+
"CUB_WRAPPED_NAMESPACE=wrapped_cub"
125+
)
121126
thrust_clone_target_properties(${headertest_target} ${thrust_target})
122127

123128
# Disable macro checks on TBB; the TBB atomic implementation uses `I` and

dependencies/cub

Submodule cub updated from ea01b53 to 6631c72

testing/cmake/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ if (THRUST_CPP_FOUND AND THRUST_CUDA_FOUND)
2626
${extra_cmake_flags}
2727
)
2828
endif()
29+
30+
# Check that namespace macros are used correctly:
31+
add_test(
32+
NAME thrust.test.cmake.check_namespace
33+
COMMAND
34+
"${CMAKE_COMMAND}"
35+
-D "Thrust_SOURCE_DIR=${Thrust_SOURCE_DIR}"
36+
-P "${CMAKE_CURRENT_LIST_DIR}/check_namespace.cmake"
37+
)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Check all files in thrust to make sure that they use
2+
# THRUST_NAMESPACE_BEGIN/END instead of bare `namespace thrust {}` declarations.
3+
#
4+
# This is run as a ctest test named `thrust.test.cmake.check_namespace`, or
5+
# manually with:
6+
# cmake -D "Thrust_SOURCE_DIR=<thrust project root>" -P check_namespace.cmake
7+
8+
cmake_minimum_required(VERSION 3.15)
9+
10+
set(exclusions
11+
# This defines the macros and must have bare namespace declarations:
12+
thrust/detail/config/namespace.h
13+
)
14+
15+
function(count_substrings input search_regex output_var)
16+
string(REGEX MATCHALL "${search_regex}" matches "${input}")
17+
list(LENGTH matches num_matches)
18+
set(${output_var} ${num_matches} PARENT_SCOPE)
19+
endfunction()
20+
21+
set(bare_ns_regex "namespace[ \n\r\t]+thrust[ \n\r\t]*\\{")
22+
23+
# Validation check for the above regex:
24+
count_substrings([=[
25+
namespace thrust{
26+
namespace thrust {
27+
namespace thrust {
28+
namespace thrust {
29+
namespace thrust
30+
{
31+
namespace
32+
thrust
33+
{
34+
]=]
35+
${bare_ns_regex} valid_count)
36+
if (NOT valid_count EQUAL 6)
37+
message(FATAL_ERROR "Validation of bare namespace regex failed: "
38+
"Matched ${valid_count} times, expected 6.")
39+
endif()
40+
41+
set(found_errors 0)
42+
file(GLOB_RECURSE thrust_srcs
43+
RELATIVE "${Thrust_SOURCE_DIR}"
44+
"${Thrust_SOURCE_DIR}/*.h"
45+
"${Thrust_SOURCE_DIR}/*.inl"
46+
"${Thrust_SOURCE_DIR}/*.cu"
47+
)
48+
49+
foreach(src ${thrust_srcs})
50+
if (${src} IN_LIST exclusions)
51+
continue()
52+
endif()
53+
54+
file(READ "${Thrust_SOURCE_DIR}/${src}" src_contents)
55+
56+
count_substrings("${src_contents}" "${bare_ns_regex}" bare_ns_count)
57+
count_substrings("${src_contents}" THRUST_NS_PREFIX prefix_count)
58+
count_substrings("${src_contents}" THRUST_NS_POSTFIX postfix_count)
59+
count_substrings("${src_contents}" THRUST_NAMESPACE_BEGIN begin_count)
60+
count_substrings("${src_contents}" THRUST_NAMESPACE_END end_count)
61+
count_substrings("${src_contents}" "#include <thrust/detail/config.h>" header_count)
62+
63+
if (NOT bare_ns_count EQUAL 0)
64+
message("'${src}' contains 'namespace thrust {...}'. Replace with THRUST_NAMESPACE macros.")
65+
set(found_errors 1)
66+
endif()
67+
68+
if (NOT prefix_count EQUAL 0)
69+
message("'${src}' contains 'THRUST_NS_PREFIX'. Replace with THRUST_NAMESPACE macros.")
70+
set(found_errors 1)
71+
endif()
72+
73+
if (NOT postfix_count EQUAL 0)
74+
message("'${src}' contains 'THRUST_NS_POSTFIX'. Replace with THRUST_NAMESPACE macros.")
75+
set(found_errors 1)
76+
endif()
77+
78+
if (NOT begin_count EQUAL end_count)
79+
message("'${src}' namespace macros are unbalanced:")
80+
message(" - THRUST_NAMESPACE_BEGIN occurs ${begin_count} times.")
81+
message(" - THRUST_NAMESPACE_END occurs ${end_count} times.")
82+
set(found_errors 1)
83+
endif()
84+
85+
if (begin_count GREATER 0 AND header_count EQUAL 0)
86+
message("'${src}' uses Thrust namespace macros, but does not (directly) `#include <thrust/detail/config.h>`.")
87+
set(found_errors 1)
88+
endif()
89+
endforeach()
90+
91+
if (NOT found_errors EQUAL 0)
92+
message(FATAL_ERROR "Errors detected.")
93+
endif()

testing/copy.cu

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <algorithm>
66
#include <list>
77
#include <iterator>
8+
#include <thrust/detail/config.h>
89
#include <thrust/sequence.h>
910
#include <thrust/iterator/zip_iterator.h>
1011
#include <thrust/iterator/counting_iterator.h>
@@ -733,15 +734,14 @@ struct only_set_when_expected_it
733734
}
734735
};
735736

736-
namespace thrust
737-
{
737+
THRUST_NAMESPACE_BEGIN
738738
namespace detail
739739
{
740740
// We need this type to pass as a non-const ref for unary_transform_functor
741741
// to compile:
742742
template <>
743743
struct is_non_const_reference<only_set_when_expected_it> : thrust::true_type {};
744-
}
744+
} // end namespace detail
745745

746746
template<>
747747
struct iterator_traits<only_set_when_expected_it>
@@ -750,7 +750,7 @@ struct iterator_traits<only_set_when_expected_it>
750750
typedef only_set_when_expected_it reference;
751751
typedef thrust::random_access_device_iterator_tag iterator_category;
752752
};
753-
}
753+
THRUST_NAMESPACE_END
754754

755755
void TestCopyWithBigIndexesHelper(int magnitude)
756756
{

testing/mr_disjoint_pool.cu

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ struct alloc_id
3232
}
3333
};
3434

35-
namespace thrust { namespace detail {
35+
THRUST_NAMESPACE_BEGIN
36+
namespace detail {
3637
template<>
3738
struct pointer_traits<alloc_id>
3839
{
@@ -48,7 +49,10 @@ struct pointer_traits<alloc_id>
4849
return reinterpret_cast<void *>(id.alignment);
4950
}
5051
};
51-
}}
52+
53+
} // end namespace detail
54+
55+
THRUST_NAMESPACE_END
5256

5357
class dummy_resource final : public thrust::mr::memory_resource<alloc_id>
5458
{

testing/namespace_wrapped.cu

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Wrap thrust and cub in different enclosing namespaces
2+
// (In practice, you probably want these to be the same, in which case just
3+
// set THRUST_CUB_WRAPPED_NAMESPACE to set both).
4+
#define THRUST_WRAPPED_NAMESPACE wrap_thrust
5+
#define CUB_WRAPPED_NAMESPACE wrap_cub
6+
7+
#include <thrust/device_vector.h>
8+
#include <thrust/host_vector.h>
9+
#include <thrust/iterator/constant_iterator.h>
10+
#include <thrust/iterator/counting_iterator.h>
11+
#include <thrust/transform.h>
12+
13+
#include <unittest/unittest.h>
14+
15+
// Test that we can use a few common utilities and algorithms from a wrapped
16+
// namespace at runtime. More extensive testing is performed by the header
17+
// tests and the check_namespace.cmake test.
18+
void TestWrappedNamespace()
19+
{
20+
const std::size_t n = 2048;
21+
22+
const auto in_1_begin =
23+
::wrap_thrust::thrust::make_constant_iterator<int>(12);
24+
const auto in_2_begin =
25+
::wrap_thrust::thrust::make_counting_iterator<int>(1024);
26+
27+
// Check that the qualifier resolves properly:
28+
THRUST_NS_QUALIFIER::device_vector<int> d_out(n);
29+
30+
::wrap_thrust::thrust::transform(in_1_begin,
31+
in_1_begin + n,
32+
in_2_begin,
33+
d_out.begin(),
34+
::wrap_thrust::thrust::plus<>{});
35+
36+
::wrap_thrust::thrust::host_vector<int> h_out(d_out);
37+
38+
for (std::size_t i = 0; i < n; ++i)
39+
{
40+
ASSERT_EQUAL(h_out[i], static_cast<int>(i) + 1024 + 12);
41+
}
42+
}
43+
DECLARE_UNITTEST(TestWrappedNamespace);

testing/scan.cu

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#include <unittest/unittest.h>
2+
3+
#include <thrust/detail/config.h>
4+
25
#include <thrust/scan.h>
36
#include <thrust/functional.h>
47
#include <thrust/iterator/discard_iterator.h>
@@ -583,15 +586,14 @@ struct only_set_when_expected_it
583586
}
584587
};
585588

586-
namespace thrust
587-
{
589+
THRUST_NAMESPACE_BEGIN
588590
template<>
589591
struct iterator_traits<only_set_when_expected_it>
590592
{
591593
typedef long long value_type;
592594
typedef only_set_when_expected_it reference;
593595
};
594-
}
596+
THRUST_NAMESPACE_END
595597

596598
void TestInclusiveScanWithBigIndexesHelper(int magnitude)
597599
{

0 commit comments

Comments
 (0)