Skip to content

Commit

Permalink
Adding dedicated test_const_name. (#3578)
Browse files Browse the repository at this point in the history
* Adding dedicated test_const_name.

Also exercises pybind11::detail::_ backward compatibility.

See also: PR #3423

* Backing out tests involving int_to_str (requires C++17 or higher).

* Suppressing clang-tidy errors.

* Disabling test_const_name for MSVC 2015 due to bizarre failures.

* Stacking @pytest.mark.parametrize (thanks to @Skylion007 for pointing out).
  • Loading branch information
Ralf W. Grosse-Kunstleve authored Dec 29, 2021
1 parent 9b4f71d commit 1bbaeb3
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 4 deletions.
10 changes: 7 additions & 3 deletions include/pybind11/detail/descr.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ constexpr descr<0> const_name(char const(&)[1]) { return {}; }

template <size_t Rem, size_t... Digits> struct int_to_str : int_to_str<Rem/10, Rem%10, Digits...> { };
template <size_t...Digits> struct int_to_str<0, Digits...> {
// WARNING: This only works with C++17 or higher.
static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
};

Expand All @@ -84,9 +85,12 @@ auto constexpr const_name() -> remove_cv_t<decltype(int_to_str<Size / 10, Size %

template <typename Type> constexpr descr<1, Type> const_name() { return {'%'}; }

// The "_" might be defined as a macro - don't define it if so.
// Repeating the const_name code to avoid introducing a #define.
// If "_" is defined as a macro, py::detail::_ cannot be provided.
// It is therefore best to use py::detail::const_name universally.
// This block is for backward compatibility only.
// (The const_name code is repeated to avoid introducing a "_" #define ourselves.)
#ifndef _
#define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
template <size_t N>
constexpr descr<N-1> _(char const(&text)[N]) { return const_name<N>(text); }
template <bool B, size_t N1, size_t N2>
Expand All @@ -107,7 +111,7 @@ auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::dig
return const_name<Size>();
}
template <typename Type> constexpr descr<1, Type> _() { return const_name<Type>(); }
#endif
#endif // #ifndef _

constexpr descr<0> concat() { return {}; }

Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ set(PYBIND11_TEST_FILES
test_callbacks.cpp
test_chrono.cpp
test_class.cpp
test_const_name.cpp
test_constants_and_functions.cpp
test_copy_move.cpp
test_custom_type_casters.cpp
Expand Down
70 changes: 70 additions & 0 deletions tests/test_const_name.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2021 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "pybind11_tests.h"

#if defined(_MSC_VER) && _MSC_VER < 1910

// MSVC 2015 fails in bizarre ways.
# define PYBIND11_SKIP_TEST_CONST_NAME

#else // Only test with MSVC 2017 or newer.

// IUT = Implementation Under Test
# define CONST_NAME_TESTS(TEST_FUNC, IUT) \
std::string TEST_FUNC(int selector) { \
switch (selector) { \
case 0: \
return IUT("").text; \
case 1: \
return IUT("A").text; \
case 2: \
return IUT("Bd").text; \
case 3: \
return IUT("Cef").text; \
case 4: \
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 5: \
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 6: \
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 7: \
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 8: \
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
return IUT<true>(IUT("D1"), IUT("D2")).text; \
case 9: \
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
return IUT<false>(IUT("E1"), IUT("E2")).text; \
case 10: \
return IUT("KeepAtEnd").text; \
default: \
break; \
} \
throw std::runtime_error("Invalid selector value."); \
}

CONST_NAME_TESTS(const_name_tests, py::detail::const_name)

# ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
CONST_NAME_TESTS(underscore_tests, py::detail::_)
# endif

#endif // MSVC >= 2017

TEST_SUBMODULE(const_name, m) {
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
m.attr("const_name_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
#else
m.def("const_name_tests", const_name_tests);
#endif

#ifdef PYBIND11_SKIP_TEST_CONST_NAME
m.attr("underscore_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
#elif defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
m.def("underscore_tests", underscore_tests);
#else
m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.";
#endif
}
31 changes: 31 additions & 0 deletions tests/test_const_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
import pytest

import env
from pybind11_tests import const_name as m


@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests))
@pytest.mark.parametrize(
"selector, expected",
enumerate(
(
"",
"A",
"Bd",
"Cef",
"%",
"%",
"T1",
"U2",
"D1",
"E2",
"KeepAtEnd",
)
),
)
def test_const_name(func, selector, expected):
if isinstance(func, type(u"") if env.PY2 else str):
pytest.skip(func)
text = func(selector)
assert text == expected
2 changes: 1 addition & 1 deletion tests/test_custom_type_casters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace pybind11 { namespace detail {
template <> struct type_caster<ArgInspector1> {
public:
// Classic
#ifndef _
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
#else
PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1"));
Expand Down

0 comments on commit 1bbaeb3

Please sign in to comment.