Skip to content
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

Untangle cast logic to not implicitly require castability #1442

Merged
merged 7 commits into from
Jul 17, 2018
Merged

Untangle cast logic to not implicitly require castability #1442

merged 7 commits into from
Jul 17, 2018

Conversation

DennisOSRM
Copy link
Contributor

The current code requires implicitly that integral types are cast-able to floating point. In case of strongly-typed integrals (e.g. as explained here) this is not always the case.

The patch at hand uses compile-time magic to make sure that cast-ability is only requested if the type supports it.

struct is_explicitly_convertible
{
enum {value = std::is_same<A, B>::value || (std::is_constructible<B, A>::value && !std::is_convertible<A, B>::value)};
};
Copy link
Member

@jagerman jagerman Jun 29, 2018

Choose a reason for hiding this comment

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

This struct can be simplified considerably:

template <class A, class B> using is_explicitly_convertible = bool_constant<
    std::is_same<A, B>::value || (std::is_constructible<B, A>::value && !std::is_convertible<A, B>::value>;

Edit: But I'm curious why the not-implicitly-convertible check is needed here. Wouldn't it be simpler to just use is_constructible, i.e.:

template <class A, class B> using is_explicitly_convertible = any_of<std::is_same<A, B>, std::is_constructible<B, A>>;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

std::bool_constant is a relatively new addition in C++17 and would require a break. Nevertheless, it is a great suggestion and really valuable feedback. I went ahead and used std::integral_constant<bool, ..> instead in 416a43f.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But I'm curious why the not-implicitly-convertible check is needed here.

It makes sure the overload doesn't become ambiguous.

template<typename U = T>
static typename std::enable_if<!std::is_floating_point<U>::value && is_explicitly_convertible<U, unsigned long long>::value, handle>::type
cast(U src, return_value_policy /* policy */, handle /* parent */) {
return PyLong_FromUnsignedLongLong((unsigned long long) src);
}
Copy link
Member

Choose a reason for hiding this comment

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

Splitting this up into an exhaustive list of types doesn't strike me as a good idea: aside from being easy to miss some in the standard (for example, you're missing signed char, char16_t, char32_t, wchar_t), it also excludes custom types that a compiler might support or a third-party library might add is_integral specializations for.

I'm also having a bit of trouble seeing what this accomplishes (a test case to demonstrate the current failure would help). The referenced type-safe-handles type doesn't seem immediately applicable to me as it doesn't seem like it will be caught in this code at all (this is already inside a std::is_arithemetic type caster specialization).

Copy link
Contributor Author

@DennisOSRM DennisOSRM Jun 29, 2018

Choose a reason for hiding this comment

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

Splitting this up into an exhaustive list of types doesn't strike me as a good idea: aside from being easy to miss some in the standard (for example, you're missing signed char, char16_t, char32_t, wchar_t), it also excludes custom types that a compiler might support or a third-party library might add is_integral specializations for.

Agree, an exhaustive list is not beautiful at all and perhaps it is not complete. Support for signed char should be there, though.

I'm also having a bit of trouble seeing what this accomplishes (a test case to demonstrate the current failure would help). The referenced type-safe-handles type doesn't seem immediately applicable to me as it doesn't seem like it will be caught in this code at all (this is already inside a std::is_arithemetic type caster specialization).

The current code in master branch requires any type to be cast-able to floating point as well as to castable to an integral type. Otherwise the code is malformed and won't compile. Arguably this patch can be improved, but it uses a SFINAE-like approach to make sure types don't have to cast-able to fp and int numbers.

@DennisOSRM
Copy link
Contributor Author

@jagerman Please have a look at the latest revision. It should simplify things a lot.

@DennisOSRM
Copy link
Contributor Author

friendly ping

@jagerman
Copy link
Member

The change looks good to me. I'll merge tomorrow if no one else has any comments.

@DennisOSRM
Copy link
Contributor Author

cool, thanks @jagerman

@DennisOSRM
Copy link
Contributor Author

friendly ping

@jagerman jagerman merged commit 221fb1e into pybind:master Jul 17, 2018
@jagerman
Copy link
Member

Oops, sorry, got tired up with other things and forgot about this. Merged; thanks for the PR!

@DennisOSRM DennisOSRM deleted the traits_cast branch July 17, 2018 15:18
EricCousineau-TRI added a commit to EricCousineau-TRI/pybind11 that referenced this pull request Sep 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants