Skip to content

Test: Default Alias in Base Class #4581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/pybind11/detail/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
// which works. If not under a known-good stl, provide our own name-based hash and equality
// functions that use the type name.
#if defined(__GLIBCXX__)
#if 0
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
using type_hash = std::hash<std::type_index>;
using type_equal_to = std::equal_to<std::type_index>;
Expand Down
43 changes: 40 additions & 3 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <new>
#include <string>
Expand Down Expand Up @@ -221,10 +222,12 @@ class cpp_function : public function {

/* Dispatch code which converts function arguments and performs the actual function call */
rec->impl = [](function_call &call) -> handle {
std::cout << "in rec->impl" << std::endl;
cast_in args_converter;

/* Try to cast the function arguments into the C++ domain */
if (!args_converter.load_args(call)) {
std::cout << ".. returning PYBIND11_TRY_NEXT_OVERLOAD" << std::endl;
return PYBIND11_TRY_NEXT_OVERLOAD;
}

Expand All @@ -247,11 +250,18 @@ class cpp_function : public function {
handle result
= cast_out::cast(std::move(args_converter).template call<Return, Guard>(cap->f),
policy,
call.parent);
call.parent); // FIXME this seems to fail to detect identical
// return types with all compilers but GCC

std::cout << ".. after cast_out::cast - result=" << result.ptr() << " "
<< (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) << std::endl;

/* Invoke call policy post-call hook */
process_attributes<Extra...>::postcall(call, result);

std::cout << ".. after postcall - result=" << result.ptr() << " "
<< (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) << std::endl;

return result;
};

Expand Down Expand Up @@ -705,6 +715,7 @@ class cpp_function : public function {
}

try {
std::cout << "Starting two-pass matching..." << std::endl;
// We do this in two passes: in the first pass, we load arguments with `convert=false`;
// in the second, we allow conversion (except for arguments with an explicit
// py::arg().noconvert()). This lets us prefer calls without conversion, with
Expand Down Expand Up @@ -761,6 +772,7 @@ class cpp_function : public function {
size_t args_copied = 0;

// 0. Inject new-style `self` argument
std::cout << "0. Inject new-style `self` argument" << std::endl;
if (func.is_new_style_constructor) {
// The `value` may have been preallocated by an old-style `__init__`
// if it was a preceding candidate for overload resolution.
Expand All @@ -775,6 +787,7 @@ class cpp_function : public function {
}

// 1. Copy any position arguments given.
std::cout << "1. Copy any position arguments given." << std::endl;
bool bad_arg = false;
for (; args_copied < args_to_copy; ++args_copied) {
const argument_record *arg_rec
Expand Down Expand Up @@ -805,6 +818,8 @@ class cpp_function : public function {
dict kwargs = reinterpret_borrow<dict>(kwargs_in);

// 1.5. Fill in any missing pos_only args from defaults if they exist
std::cout << "1.5. Fill in any missing pos_only args from defaults if they exist"
<< std::endl;
if (args_copied < func.nargs_pos_only) {
for (; args_copied < func.nargs_pos_only; ++args_copied) {
const auto &arg_rec = func.args[args_copied];
Expand All @@ -827,6 +842,9 @@ class cpp_function : public function {
}

// 2. Check kwargs and, failing that, defaults that may help complete the list
std::cout << "2. Check kwargs and, failing that, defaults that may help complete "
"the list"
<< std::endl;
if (args_copied < num_args) {
bool copied_kwargs = false;

Expand Down Expand Up @@ -876,11 +894,16 @@ class cpp_function : public function {
}

// 3. Check everything was consumed (unless we have a kwargs arg)
std::cout << "3. Check everything was consumed (unless we have a kwargs arg)"
<< std::endl;
if (kwargs && !kwargs.empty() && !func.has_kwargs) {
continue; // Unconsumed kwargs, but no py::kwargs argument to accept them
}

// 4a. If we have a py::args argument, create a new tuple with leftovers
std::cout
<< "4a. If we have a py::args argument, create a new tuple with leftovers"
<< std::endl;
if (func.has_args) {
tuple extra_args;
if (args_to_copy == 0) {
Expand All @@ -906,6 +929,8 @@ class cpp_function : public function {
}

// 4b. If we have a py::kwargs, pass on any remaining kwargs
std::cout << "4b. If we have a py::kwargs, pass on any remaining kwargs"
<< std::endl;
if (func.has_kwargs) {
if (!kwargs.ptr()) {
kwargs = dict(); // If we didn't get one, send an empty one
Expand All @@ -917,7 +942,10 @@ class cpp_function : public function {

// 5. Put everything in a vector. Not technically step 5, we've been building it
// in `call.args` all along.
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
#if 1
std::cout << "5. Put everything in a vector. Not technically step 5, we've been "
"building it in `call.args` all along."
<< std::endl;
if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) {
pybind11_fail("Internal error: function call dispatcher inserted wrong number "
"of arguments!");
Expand All @@ -934,18 +962,24 @@ class cpp_function : public function {
}

// 6. Call the function.
std::cout << "6. Call the function." << std::endl;
try {
loader_life_support guard{};
result = func.impl(call);
result = func.impl(
call); // FIXME looks like this fails to match identical return types
} catch (reference_cast_error &) {
std::cout << ".. catch (reference_cast_error &)" << std::endl;
result = PYBIND11_TRY_NEXT_OVERLOAD;
}

if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) {
std::cout << ".. result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD -> break"
<< std::endl;
break;
}

if (overloaded) {
std::cout << ".. overloaded" << std::endl;
// The (overloaded) call failed; if the call has at least one argument that
// permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`)
// then add this call to the list of second pass overloads to try.
Expand All @@ -964,6 +998,9 @@ class cpp_function : public function {
if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) {
// The no-conversion pass finished without success, try again with conversion
// allowed
std::cout << "The no-conversion pass finished without success, try again with "
"conversion allowed"
<< std::endl;
for (auto &call : second_pass) {
try {
loader_life_support guard{};
Expand Down
5 changes: 3 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ set(PYBIND11_TEST_FILES
test_stl
test_stl_binders
test_tagbased_polymorphic
test_template_alias_base
test_thread
test_union
test_virtual_functions)
Expand Down Expand Up @@ -521,8 +522,8 @@ set(PYBIND11_TEST_PREFIX_COMMAND
# A single command to compile and run the tests
add_custom_target(
pytest
COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest
${PYBIND11_ABS_PYTEST_FILES}
COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest -s -vvvv
"${CMAKE_CURRENT_SOURCE_DIR}/test_template_alias_base.py"
DEPENDS ${test_targets}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
USES_TERMINAL)
Expand Down
28 changes: 28 additions & 0 deletions tests/test_template_alias_base.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "pybind11_tests.h"

#include <iostream>
#include <memory>

template <class T>
using DefaultAllocator = std::allocator<T>;

template <template <class> class Allocator = DefaultAllocator>
struct Base {};

template <template <class> class Allocator = DefaultAllocator>
struct S : public Base<Allocator> {};

// this returns S<DefaultAllocator> even though we register
// the type S<std::allocator>
// MSVC and Clang on Windows failed here to detect the registered type
S<> make_S() {
std::cout << "in make_S()" << std::endl;
return S<>{};
}

TEST_SUBMODULE(template_alias_base, m) {
py::class_<Base<std::allocator>>(m, "B_std").def(py::init());
py::class_<S<std::allocator>, Base<std::allocator>>(m, "S_std").def(py::init());

m.def("make_S", &make_S);
}
11 changes: 11 additions & 0 deletions tests/test_template_alias_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pybind11_tests import template_alias_base as m


def test_can_create_variable():
v = m.S_std()
print(v)


def test_can_return_variable():
v = m.make_S()
print(v)