-
Notifications
You must be signed in to change notification settings - Fork 243
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
[Core] Adding hash for std::pair
#11312
Conversation
I thought that adding stuff to the std namespace is not allowed/bad practice? Surely there must be a better way no? @matekelemen do you have an idea? |
We add things to the std namespace in the stl_io.h file.
El dom., 25 jun. 2023 13:59, Philipp Bucher ***@***.***>
escribió:
… I thought that adding stuff to the std namespace is not allowed/bad
practice?
Surely there must be a better way no?
@matekelemen <https://github.com/matekelemen> do you have an idea?
—
Reply to this email directly, view it on GitHub
<#11312 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEYQZAFO5NTMKTRZDI3C2ILXNARYZANCNFSM6AAAAAAZROC2DY>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
As far as I know, providing specializations for From cplusplus reference on From cppreference's page on |
Sounds like sth to be refactored then :) EDIT: I couldnt find what you are referring to |
Sorry, I meant array_1d: Kratos/kratos/containers/array_1d.h Line 792 in 1866070
|
I see I encourage to do this in accordance to what the standard proposes (which from what I can see does not requires @matekelemen since you approved the other PR with the same thing, I leave this decision to you |
I specifically requested this format from @loumalouomega, so I'm completely happy with it, apart from the unconstrained template parameters. However, now's the time to find a solution that everyone supports. From my side, I'd go with specializing I don't see any issues with specializing
The reason why I'm hesitant in this case is because the template parameters are unconstrained, and this will poison @philbucher what do you think? |
I think the implementation is general enough to avoid conflicts |
Generality is exactly the problem here. This will be super confusing if a 3rd party lib defines a specialization for // some_3rd_party_header.hpp
#include <functional> // std::hash
#include <utility> // std::pair
namespace whatever {
struct S {};
} // namespace whatever
namespace std {
template <>
struct hash<pair<whatever::S,whatever::S>>
{
std::size_t operator()(const pair<whatever::S,whatever::S>&) const noexcept {return 0;}
}; // struct hash
} // namespace std // key_hash.h
/*...*/
namespace std {
template <class T1, class T2>
struct hash<pair<T1,T2>>
{
std::size_t operator()(const pair<T1,T2>& rPair) const noexcept {return 1;}
}; // struct hash
} // namespace std
/*...*/ So what will this print? #include <iostream>
#include "some_3rd_party_header.hpp"
#include "key_hash.h"
int main()
{
std::pair<whatever::S,whatever::S> key;
std::hash<std::pair<whatever::S,whatever::S>> hasher;
std::cout << hasher(key) << std::endl;
} How about this one? ( #include <iostream>
#include "key_hash.h"
#include "some_3rd_party_header.hpp"
int main()
{
std::pair<whatever::S,whatever::S> key;
std::hash<std::pair<whatever::S,whatever::S>> hasher;
std::cout << hasher(key) << std::endl;
} => Exactly, you had to look it up or run it on godbolt. |
So what do you suggest? |
I have 2 things in mind, neither of which is too appealing:
|
I'm open to other ideas tho. |
Probably better the second idea, despite being a pain in the ass. Besides, I removed the dependency of this in my current as I see is being probelmatic. |
Also, depending on how much sense it makes for your particular case, you could just define a namespace Kratos {
template <class TLocal, class TGlobal>
struct IndexPair
{
TLocal local;
TGlobal global;
}; // struct IndexPair
} // namespace Kratos
namespace std {
template <class TLocal, class TGlobal>
struct hash<Kratos::IndexPair<TLocal,TGlobal>>
{
std::size_t operator()(const Kratos::IndexPair<TLocal,TGlobal>&) const noexcept {/*...*/}
}; // struct hash
} // namespace std |
For the moment I will keep this PR for discussion and revert the changes of this branch in my branch. It looks like it will be a long discussion |
Did I miss a discussion on this? |
@RiccardoRossi probably selected the wrong option 🤣 |
sorry, i did not see the discussion. |
inserting in the std namespace is not allowed as you correctly pointed out (i did not see that ... sorry...) this should be in the Kratos namespace |
@RiccardoRossi the C++ standard library is specifically designed to allow user-defined specializations of What I am concerned about is that the template parameters here are unconstrained and can match any class, not just the ones Kratos defines. |
A priori hash combine works with any class, so I wouldle it that way. It is just the combination of two existing hashes, nothing else. |
sorry, i did some more searching https://en.cppreference.com/w/cpp/language/extending_std concretely at that link it tells that " implicitly telling that it can be specialized. in short...i am out of my depth here, but it looks like Mate is right and it is allowed to specialize std::hash this also tells that i should not be the one approving or not this, since obviously it escapes my knowledge |
I don't think I should be the one making the decision here either; maybe @roigcarlo can help us out and put it up for discussion in the @KratosMultiphysics/technical-committee ? I'll try summarizing my opinion: Extending the The problem I see is with the template declaration of the specialization: template<typename T1, typename T2>
struct hash<std::pair<T1, T2>> Since Right now, I have 3 solutions in mind but all of them are a bit ugly, so I'm open to other ideas:
template <class T1, class TValue, std::size_t ArraySize>
struct hash<pair<T1,Kratos::array_1d<TValue,ArraySize>>>
namespace Kratos {
template <class T1, class T2>
struct Pair {T1 first; T2 second;};
} // namespace Kratos
namespace std {
template <class T1, class T2>
struct hash<Kratos::Pair<T1,T2>>
...
}
|
But @matekelemen the hash for std::pair is not defined at all, not even std::pair<int, int>, and int is not a Kratos class |
My 2 cents: I tend to agree with @matekelemen arguments, except that for me the ideal solution would be 4 - Create our own hash class and use it explicitly where needed: class pair_hash .... // The same code you put but is our hash and not the std one
....
int std::map<std::pair<TA,TB>,int,pair_hash> my_map; Deriving from hash will only allow the user to remove the explicit hash_function from the type of the map/class where he wants t use it, but will make a mess with external libraries and pretty much everyone depending on a standard definition of the hash. Note that, even if we go for that solution we can just define the type of our class like we do with other Kratos types and it keeps being transparent for the user. Edit: If you like it better: class pair_hash {
public:
template<class T1, class T2>
operator() (const std::pair<T1,T2>) {...}
}
template<class TIdx1, class TIdx2, class TData>
using pair_map = std::map<std::pair<TIdx1, TIdx2>, TData, pair_hash>;
....
int Kratos::pair_map<int, string, int> my_map; |
Do I merge this? |
I still think that extending the std::pair is wrong and we should provide an external hash, but as you like. |
Okay |
📝 Description
This PR adds missing hash definitions and tests for STL pair (
std::pair
). The changes include:std namespace
. It calculates the hash value of a given pair of values by combining the hash values of the individual elements usingHasCombine
.🆕 Changelog