Skip to content

convert some ::value to _v - #10765

Open
Jacobfaib wants to merge 1 commit into
NVIDIA:mainfrom
Jacobfaib:jacobf/2026-08-11/value-to-v
Open

convert some ::value to _v#10765
Jacobfaib wants to merge 1 commit into
NVIDIA:mainfrom
Jacobfaib:jacobf/2026-08-11/value-to-v

Conversation

@Jacobfaib

Copy link
Copy Markdown
Contributor

Description

Convert many instances of meow<...>::value to meow_v<...>.

Checklist

  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@Jacobfaib
Jacobfaib requested a review from a team as a code owner August 11, 2026 23:13
@Jacobfaib
Jacobfaib requested a review from davebayer August 11, 2026 23:13
@github-project-automation github-project-automation Bot moved this to Todo in CCCL Aug 11, 2026
@cccl-authenticator-app cccl-authenticator-app Bot moved this from Todo to In Review in CCCL Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Refactor

    • Modernized internal trait and concept checks to use variable-template forms consistently.
    • Updated tuple-size, callability, constructibility, assignability, and destructibility checks.
    • Simplified stable-sort configuration checks without changing sorting behavior.
  • Bug Fixes

    • Preserved existing algorithm behavior while improving compatibility and consistency across CUDA standard library utilities.

Walkthrough

Changes

The patch replaces legacy trait .value access with variable-template forms across tuple, callable, algorithm, sorting, and type-trait fallback implementations. Existing conditions, calculations, diagnostics, and behavior remain unchanged.

Trait modernization

Layer / File(s) Summary
Algorithm validation updates
libcudacxx/include/cuda/std/__algorithm/*
Comparator checks in equal_range, includes, lower_bound, min_element, minmax, and minmax_element now use __is_callable_v.
Stable sort dispatch updates
libcudacxx/include/cuda/std/__algorithm/stable_sort.h
Stable-sort threshold checks now use __stable_sort_switch_v in recursive sorting and temporary-buffer allocation paths.
Tuple and callable trait updates
libcudacxx/include/cuda/__complex/traits.h, libcudacxx/include/cuda/__functional/address_stability.h, libcudacxx/include/cuda/std/__simd/utility.h, libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
Tuple-size checks use tuple_size_v. Copyable-argument detection uses proclaims_copyable_arguments_v.
Type-trait fallback updates
libcudacxx/include/cuda/std/__type_traits/*
Fallback implementations use is_void_v, is_assignable_v, is_constructible_v, is_reference_v, is_function_v, is_destructible_v, and related variable templates.

Suggested reviewers: davebayer, miscco


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: db93125b-7d19-4ac6-ba8d-7128f990bf42

📥 Commits

Reviewing files that changed from the base of the PR and between a0c21d1 and 8bd263c.

📒 Files selected for processing (17)
  • libcudacxx/include/cuda/__complex/traits.h
  • libcudacxx/include/cuda/__functional/address_stability.h
  • libcudacxx/include/cuda/std/__algorithm/equal_range.h
  • libcudacxx/include/cuda/std/__algorithm/includes.h
  • libcudacxx/include/cuda/std/__algorithm/lower_bound.h
  • libcudacxx/include/cuda/std/__algorithm/min_element.h
  • libcudacxx/include/cuda/std/__algorithm/minmax.h
  • libcudacxx/include/cuda/std/__algorithm/minmax_element.h
  • libcudacxx/include/cuda/std/__algorithm/stable_sort.h
  • libcudacxx/include/cuda/std/__simd/utility.h
  • libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
  • libcudacxx/include/cuda/std/__type_traits/add_pointer.h
  • libcudacxx/include/cuda/std/__type_traits/is_copy_assignable.h
  • libcudacxx/include/cuda/std/__type_traits/is_copy_constructible.h
  • libcudacxx/include/cuda/std/__type_traits/is_destructible.h
  • libcudacxx/include/cuda/std/__type_traits/is_move_constructible.h
  • libcudacxx/include/cuda/std/__type_traits/is_nothrow_destructible.h


template <typename _Range>
inline constexpr bool __has_tuple_size_v<_Range, void_t<decltype(tuple_size<remove_cvref_t<_Range>>::value)>> = true;
inline constexpr bool __has_tuple_size_v<_Range, void_t<decltype(tuple_size_v<remove_cvref_t<_Range>>)>> = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

for f in \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
do
  echo "===== $f ====="
  wc -l "$f"
  sed -n '1,125p' "$f"
done

echo "===== tuple_size and tuple_size_v definitions/usages ====="
rg -n --glob '*.{h,hpp,cpp,cc,cxx,cu,cuh}' \
  'tuple_size_v|tuple_size<|__has_tuple_size_v|__is_complex_compatible_tuple_like|__pair_like' \
  libcudacxx | head -250

echo "===== current diff summary ====="
git diff --stat -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== current diff for the three files ====="
git diff -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

Repository: NVIDIA/cccl

Length of output: 38810


🏁 Script executed:

#!/bin/bash
set -eu

echo "===== tuple_size implementation ====="
cat -n libcudacxx/include/cuda/std/__tuple_dir/tuple_size.h | sed -n '1,115p'

echo "===== constraint macro definitions ====="
rg -n -A18 -B8 \
  '_CCCL_REQUIRES_EXPR|define _CCCL_REQUIRES_EXPR|_CCCL_CONCEPT' \
  libcudacxx/include/cuda/std/__concepts/concept_macros.h \
  libcudacxx/include/cuda/std/__concepts 2>/dev/null | head -220

echo "===== related tuple-like and complex tests ====="
rg -n -A12 -B8 \
  '__pair_like|__is_complex_compatible_tuple_like|incomplete|tuple_size_v' \
  libcudacxx/test/libcudacxx/cuda/complex \
  libcudacxx/test/libcudacxx/std/utilities/tuple/tuple.tuple/tuple.helper \
  libcudacxx/test/libcudacxx/std/ranges 2>/dev/null | head -300

echo "===== recent history for the affected files ====="
git log --oneline -12 -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== available C++ compilers ====="
command -v g++ || true
command -v clang++ || true
command -v nvcc || true

Repository: NVIDIA/cccl

Length of output: 50367


🏁 Script executed:

#!/bin/bash
set -eu

echo "===== available C++ compilers ====="
command -v g++ || true
command -v clang++ || true
command -v nvcc || true

echo "===== focused concept macro sections ====="
sed -n '35,72p;201,225p;229,290p' libcudacxx/include/cuda/std/__concepts/concept_macros.h

echo "===== focused history ====="
git log --oneline -12 -- libcudacxx/include/cuda/std/__simd/utility.h
git log --oneline -12 -- libcudacxx/include/cuda/__complex/traits.h
git log --oneline -12 -- libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== commits that introduced the tuple-size spellings ====="
git log --all --oneline -S'tuple_size_v<remove_cvref_t<_Range>>' -- libcudacxx/include/cuda/std/__simd/utility.h || true
git log --all --oneline -S'tuple_size_v<_Tp>' -- libcudacxx/include/cuda/__complex/traits.h || true
git log --all --oneline -S'tuple_size_v<remove_cvref_t<_Tp>>' -- libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h || true

echo "===== standalone compiler probe ====="
cat >/tmp/tuple-size-probe.cpp <<'CPP'
`#include` <cstddef>
`#include` <type_traits>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T, class = void>
inline constexpr bool has_member_probe = false;

template <class T>
inline constexpr bool has_member_probe<T, std::void_t<decltype(tuple_size<T>::value)>> = true;

template <class T, class = void>
inline constexpr bool has_variable_probe = false;

template <class T>
inline constexpr bool has_variable_probe<T, std::void_t<decltype(tuple_size_v<T>)>> = true;

template <class T>
concept variable_constraint = requires { requires(tuple_size_v<T> == 2); };

template <class T>
concept member_constraint = requires { requires(tuple_size<T>::value == 2); };

struct Incomplete;

static_assert(!has_member_probe<Incomplete>);
static_assert(!has_variable_probe<Incomplete>);
static_assert(!member_constraint<Incomplete>);
static_assert(!variable_constraint<Incomplete>);

int main() {}
CPP

if command -v g++ >/dev/null 2>&1; then
  g++ -std=c++20 -fsyntax-only /tmp/tuple-size-probe.cpp
  echo "g++ probe: success"
fi
if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/tuple-size-probe.cpp
  echo "clang++ probe: success"
fi

Repository: NVIDIA/cccl

Length of output: 8332


🏁 Script executed:

#!/bin/bash
set -u

echo "===== conversion commit ====="
git show --no-ext-diff --format=fuller --stat 8bd263c5e
git show --no-ext-diff --format= --unified=18 8bd263c5e -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== standalone compiler probe with clang++ ====="
cat >/tmp/tuple-size-probe.cpp <<'CPP'
`#include` <cstddef>
`#include` <type_traits>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T, class = void>
inline constexpr bool has_member_probe = false;

template <class T>
inline constexpr bool has_member_probe<T, std::void_t<decltype(tuple_size<T>::value)>> = true;

template <class T, class = void>
inline constexpr bool has_variable_probe = false;

template <class T>
inline constexpr bool has_variable_probe<T, std::void_t<decltype(tuple_size_v<T>)>> = true;

template <class T>
concept variable_constraint = requires { requires(tuple_size_v<T> == 2); };

template <class T>
concept member_constraint = requires { requires(tuple_size<T>::value == 2); };

struct Incomplete;

static_assert(!has_member_probe<Incomplete>);
static_assert(!has_variable_probe<Incomplete>);
static_assert(!member_constraint<Incomplete>);
static_assert(!variable_constraint<Incomplete>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ --version | head -1
  clang++ -std=c++20 -fsyntax-only /tmp/tuple-size-probe.cpp
  echo "clang++ probe: success"
else
  echo "clang++ unavailable"
fi

Repository: NVIDIA/cccl

Length of output: 8714


🏁 Script executed:

#!/bin/bash
set -u

echo "===== _CCCL_REQUIRES_EXPR definition ====="
rg -n -A12 -B8 '_CCCL_REQUIRES_EXPR' libcudacxx/include/cuda/std

echo "===== exact pair-like behavior in a standalone probe ====="
cat >/tmp/pair-like-probe.cpp <<'CPP'
`#include` <cstddef>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T>
inline constexpr bool tuple_like_ext = false;

template <class T>
concept pair_like_variable = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size_v<T> == 2);
};

template <class T>
concept pair_like_member = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size<T>::value == 2);
};

struct Unsupported;

static_assert(!pair_like_variable<Unsupported>);
static_assert(!pair_like_member<Unsupported>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/pair-like-probe.cpp
  rc=$?
  echo "clang++ pair-like probe exit: $rc"
fi

echo "===== exact complex-style behavior in a standalone probe ====="
cat >/tmp/complex-like-probe.cpp <<'CPP'
`#include` <cstddef>
`#include` <type_traits>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T>
using tuple_element_t = typename T::missing;

template <class T>
concept complex_like_variable = requires {
  requires(tuple_size_v<T> == 2);
  requires(std::is_same_v<tuple_element_t<T>, tuple_element_t<T>>);
};

template <class T>
concept complex_like_member = requires {
  requires(tuple_size<T>::value == 2);
  requires(std::is_same_v<tuple_element_t<T>, tuple_element_t<T>>);
};

struct Unsupported;

static_assert(!complex_like_variable<Unsupported>);
static_assert(!complex_like_member<Unsupported>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/complex-like-probe.cpp
  rc=$?
  echo "clang++ complex-like probe exit: $rc"
fi

Repository: NVIDIA/cccl

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -u

echo "===== _CCCL_REQUIRES_EXPR implementation ====="
sed -n '296,322p' libcudacxx/include/cuda/std/__concepts/concept_macros.h

echo "===== pair-like probe with a recognized but incomplete tuple type ====="
cat >/tmp/pair-like-probe.cpp <<'CPP'
`#include` <cstddef>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

struct RecognizedTuple;
template <>
inline constexpr bool tuple_like_ext<RecognizedTuple> = true;

template <class T>
concept pair_like_variable = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size_v<T> == 2);
};

template <class T>
concept pair_like_member = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size<T>::value == 2);
};

static_assert(!pair_like_member<RecognizedTuple>);
static_assert(!pair_like_variable<RecognizedTuple>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/pair-like-probe.cpp >/tmp/pair-like-probe.out 2>&1
  rc=$?
  echo "clang++ pair-like probe exit: $rc"
  sed -n '1,25p' /tmp/pair-like-probe.out
fi

echo "===== direct current-header probes ====="
cat >/tmp/current-header-probe.cpp <<'CPP'
`#include` <cuda/std/__simd/utility.h>
`#include` <cuda/std/__tuple_dir/tuple_like.h>
`#include` <cuda/__complex/traits.h>

struct UnsupportedTuple;

static_assert(!::cuda::std::simd::__has_tuple_size_v<UnsupportedTuple>);
static_assert(!::cuda::std::__pair_like<UnsupportedTuple>);
static_assert(!::cuda::__is_complex_compatible_tuple_like<UnsupportedTuple>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only -Ilibcudacxx/include /tmp/current-header-probe.cpp \
    >/tmp/current-header-probe.out 2>&1
  rc=$?
  echo "clang++ current-header probe exit: $rc"
  sed -n '1,45p' /tmp/current-header-probe.out
fi

Repository: NVIDIA/cccl

Length of output: 7470


🏁 Script executed:

#!/bin/bash
set -u

echo "===== direct __pair_like probe with an incomplete recognized tuple ====="
cat >/tmp/pair-like-current-probe.cpp <<'CPP'
`#include` <cuda/std/__tuple_dir/tuple_like.h>

static_assert(!::cuda::std::__pair_like<::cuda::std::tuple<int, int>>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only -Ilibcudacxx/include \
    /tmp/pair-like-current-probe.cpp >/tmp/pair-like-current-probe.out 2>&1
  rc=$?
  echo "clang++ __pair_like probe exit: $rc"
  sed -n '1,40p' /tmp/pair-like-current-probe.out
fi

echo "===== parent implementation of all three probes ====="
git show 8bd263c5e^:libcudacxx/include/cuda/std/__simd/utility.h | sed -n '78,86p'
git show 8bd263c5e^:libcudacxx/include/cuda/__complex/traits.h | sed -n '37,44p'
git show 8bd263c5e^:libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h | sed -n '93,99p'

Repository: NVIDIA/cccl

Length of output: 4870


critical: Restore member-based tuple-size probes in all three constraint paths. tuple_size_v<T> can hard-error when tuple_size<T> is incomplete instead of producing a failed constraint.

  • libcudacxx/include/cuda/std/__simd/utility.h:83: use decltype(tuple_size<remove_cvref_t<_Range>>::value).
  • libcudacxx/include/cuda/__complex/traits.h:41: use ::cuda::std::tuple_size<_Tp>::value.
  • libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h:97: use tuple_size<remove_cvref_t<_Tp>>::value.
📍 Affects 3 files
  • libcudacxx/include/cuda/std/__simd/utility.h#L83-L83 (this comment)
  • libcudacxx/include/cuda/__complex/traits.h#L41-L41
  • libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h#L97-L97

Source: Path instructions

template <class _Tp>
_CCCL_CONCEPT __pair_like = _CCCL_REQUIRES_EXPR((_Tp)) //
(requires(__tuple_like<_Tp>), requires(tuple_size<remove_cvref_t<_Tp>>::value == 2));
(requires(__tuple_like<_Tp>), requires(tuple_size_v<remove_cvref_t<_Tp>> == 2));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- tuple_like.h ---'
sed -n '70,115p' libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
printf '%s\n' '--- concept macros ---'
sed -n '1,240p' libcudacxx/include/cuda/std/__concepts/concept_macros.h
printf '%s\n' '--- related tuple_size_v usage ---'
rg -n -C 3 'tuple_size_v|__pair_like|_CCCL_REQUIRES_EXPR' libcudacxx/include/cuda/std/__tuple_dir libcudacxx/include/cuda/std/__concepts

Repository: NVIDIA/cccl

Length of output: 31007


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '365,395p' libcudacxx/include/cuda/std/__concepts/concept_macros.h
sed -n '1,90p' libcudacxx/include/cuda/std/__tuple_dir/tuple_size.h
sed -n '1,78p' libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
git diff -- libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

Repository: NVIDIA/cccl

Length of output: 7520


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '210,295p' libcudacxx/include/cuda/std/__concepts/concept_macros.h
command -v g++ || true
command -v clang++ || true
python3 - <<'PY'
from pathlib import Path
p = Path("libcudacxx/include/cuda/std/__concepts/concept_macros.h")
text = p.read_text()
for name in ("_CCCL_CONCEPT_REQUIREMENT_CASE__CCCL_SWITCH_REQUIRES",
             "_CCCL_CONCEPT_REQUIREMENT_0",
             "_CCCL_CONCEPT_REQUIREMENT_1"):
    print(name, text.find(name))
PY
cat >/tmp/tuple_size_probe.cpp <<'CPP'
`#include` <type_traits>

template<class> struct tuple_size;
template<class T> inline constexpr int tuple_size_v = tuple_size<T>::value;

template<class T>
concept member_form = requires {
  requires (std::is_same_v<T, int>);
  requires (tuple_size<T>::value == 2);
};

template<class T>
concept variable_form = requires {
  requires (std::is_same_v<T, int>);
  requires (tuple_size_v<T> == 2);
};

static_assert(!member_form<int>);
static_assert(!variable_form<int>);
CPP
if command -v g++ >/dev/null 2>&1; then
  g++ -std=c++20 -fsyntax-only /tmp/tuple_size_probe.cpp
fi
if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/tuple_size_probe.cpp
fi

Repository: NVIDIA/cccl

Length of output: 5471


🏁 Script executed:

#!/bin/bash
set -u
cat >/tmp/cccl_fallback_probe.cpp <<'CPP'
template<bool>
struct select {};
template<>
struct select<true> {
  template<class T>
  using type = T;
};
template<bool B, class T = void>
using enable_if_t = typename select<B>::template type<T>;

template<class> struct tuple_size;
template<class T>
inline constexpr int tuple_size_v = tuple_size<T>::value;

template<bool B, enable_if_t<B, int> = 0>
inline constexpr int requires_value = 0;

template<class T>
inline constexpr bool tuple_like = false;

template<class T>
struct member_form {
  template<class U>
  static auto check()
    -> enable_if_t<
         (decltype(requires_value<tuple_like<U>>{}),
          decltype(requires_value<(tuple_size<U>::value == 2)>{}),
          true),
         void>;
};

template<class T>
struct variable_form {
  template<class U>
  static auto check()
    -> enable_if_t<
         (decltype(requires_value<tuple_like<U>>{}),
          decltype(requires_value<(tuple_size_v<U> == 2)>{}),
          true),
         void>;
};

template<class T, class = void>
struct has_member : false {};
template<class T>
struct has_member<T, decltype(member_form<T>::template check<T>())> : true {};

template<class T, class = void>
struct has_variable : false {};
template<class T>
struct has_variable<T, decltype(variable_form<T>::template check<T>())> : true {};

static_assert(!has_member<int>::value);
static_assert(!has_variable<int>::value);
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp
  printf '%s\n' 'clang probe: passed'
else
  printf '%s\n' 'clang++ unavailable'
fi
if command -v g++ >/dev/null 2>&1; then
  if g++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp; then
    printf '%s\n' 'g++ probe: passed'
  else
    printf '%s\n' 'g++ probe: unavailable or failed'
  fi
fi

Repository: NVIDIA/cccl

Length of output: 2675


🏁 Script executed:

#!/bin/bash
set -eu
cat >/tmp/cccl_fallback_probe.cpp <<'CPP'
template<bool>
struct select {};
template<>
struct select<true> {
  template<class T>
  using type = T;
};
template<bool B, class T = void>
using enable_if_t = typename select<B>::template type<T>;

template<class> struct tuple_size;
template<class T>
inline constexpr int tuple_size_v = tuple_size<T>::value;

template<bool B, enable_if_t<B, int> = 0>
inline constexpr int requires_value = 0;

template<class T>
inline constexpr bool tuple_like = false;

template<class T>
struct member_form {
  template<class U>
  static auto check()
    -> decltype(
         requires_value<tuple_like<U>>,
         requires_value<(tuple_size<U>::value == 2)>,
         void());
};

template<class T>
struct variable_form {
  template<class U>
  static auto check()
    -> decltype(
         requires_value<tuple_like<U>>,
         requires_value<(tuple_size_v<U> == 2)>,
         void());
};

template<class T, class = void>
struct has_member { static constexpr bool value = false; };
template<class T>
struct has_member<T, decltype(member_form<T>::template check<T>())> {
  static constexpr bool value = true;
};

template<class T, class = void>
struct has_variable { static constexpr bool value = false; };
template<class T>
struct has_variable<T, decltype(variable_form<T>::template check<T>())> {
  static constexpr bool value = true;
};

static_assert(!has_member<int>::value);
static_assert(!has_variable<int>::value);
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp
  printf '%s\n' 'clang probe: passed'
else
  printf '%s\n' 'clang++ unavailable'
fi
if command -v g++ >/dev/null 2>&1; then
  g++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp
  printf '%s\n' 'g++ probe: passed'
else
  printf '%s\n' 'g++ unavailable'
fi

Repository: NVIDIA/cccl

Length of output: 271


🌐 Web query:

C++ tuple_size_v requires expression hard error variable template substitution NVCC NVRTC

💡 Result:

The issue where std::tuple_size_v causes a compilation error (a hard error) instead of a SFINAE substitution failure arises because the evaluation of the variable template's initializer—std::tuple_size::value—often occurs outside the immediate context of a template substitution [1][2][3]. Under C++ template instantiation rules, SFINAE (Substitution Failure Is Not An Error) only applies to errors occurring within the immediate context of a function signature or template parameter list [2]. When you use std::tuple_size_v, the compiler must instantiate the variable template specialization to determine its value. If the type T is not tuple-like, this instantiation triggers an error in the initializer (std::tuple_size::value). Because this instantiation is considered a side effect of the template substitution rather than an error in the immediate context, the compiler treats it as a hard error [1][2]. In contrast, using std::tuple_size::value directly is often SFINAE-friendly because the failure to find a::value member in the primary template—which is typically undefined—occurs in a context where the compiler can gracefully treat it as a substitution failure [3][4]. For NVCC and NVRTC users, this behavior is a standard C++ language compliance issue rather than a specific toolchain bug [5][3]. When working with CUDA libraries (such as libcu++), encountering this error usually means: 1. The type T passed to tuple_size_v does not meet the requirements of a "tuple-like" type (it lacks an appropriate std::tuple_size specialization) [4]. 2. You are attempting to use it in a SFINAE-constrained context (like a requires clause or std::enable_if) where you expect failure to be silent, but the compiler is forcing an instantiation [2][6][3]. To resolve this, you can: - Use std::tuple_size::value instead of the variable template std::tuple_size_v [5][3]. - If you must use a variable template, wrap it in a custom trait that inherits from std::integral_constant to ensure the failure occurs in a way that respects SFINAE, or define a primary template specialization for your custom types to ensure std::tuple_size::value is always valid [6][4].

Citations:


important: Preserve the member-based check for non-concepts toolchains. _CCCL_REQUIRES_EXPR places the requirement in a substitution context, but tuple_size_v<remove_cvref_t<_Tp>> can instantiate its initializer outside the immediate context and produce a hard error for non-tuple types. Use tuple_size<remove_cvref_t<_Tp>>::value until NVCC and NVRTC compatibility is established.

Source: Path instructions

@github-actions

Copy link
Copy Markdown
Contributor

⏱️ CCCL compile-time benchmark comparison: Public headers compile-time bench

Result: 0 regression row(s), 3 improvement row(s) above threshold.

Run Value
Config public-headers-gcc13
Baseline origin/main
Preset all-dev
Targets cub.headers.base, thrust.cpp.cuda.headers.base, libcudacxx.test.public_headers
GPU / launch args rtx2080 / --cuda 13.3 --host gcc13

Artifacts: reports and traces

Direct file processing

-f file-processing exclusive --sort total

🟢 Direct file processing — Improvements
Rank Improvement impact Selected Δ Baseline Current Event Matched traces
1 0.523480 -0.523480 4.776444 4.252964 Processing Header File: libcudacxx/include/cuda/std/__cccl/prologue.h 551
2 0.401759 -0.401759 9.910397 9.508638 Processing Header File: libcudacxx/include/cuda/__device/physical_device.h 94
3 0.201424 -0.201424 1.662574 1.461150 Processing Header File: libcudacxx/include/cuda/std/__cccl/epilogue.h 551

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

1 participant