std::set of MyElement with MyElement::SomeMethod as custom comparator - c++

I have a simple MyElement class, and I would like to use a bool MyElement::SomeMethod(...) {...} as the custom comparator for a std::set of MyElement items.
I have made my research, and I am already aware of some alternative solutions, which I list below.
I also know how to change, for example, the comparator with std::greater instead of the default std::less, with code like this:
std::set<MyElement, std::greater<MyElement> > s;
My exact problem is that I want to use bool MyElement::SomeMethod(...) {...} as custom comparator.
The only solution I come up with is analogous to the last one in the list below, namely the solution for boolean function:
using Cmp = std::integral_constant<decltype(&MyElement::SomeMethod),
&MyElement::SomeMethod>;
std::set<MyElement, Cmp> my_set;
This solution only works for a static MyElement::SomeMethod, though.
I am wondering if there is an analgous, or more concise, way for a non static method.
List of alternative solutions:
method for C++20
auto cmp = [](const MyElement& lhs, const MyElement& rhs) { return ... };
std::set<MyElement, decltype(cmp)> s;
method for C++11
auto cmp = [](const MyElement& lhs, const MyElement& rhs) { return ... };
std::set<MyElement, decltype(cmp)> s(cmp);
function instead of a lambda
bool cmp(const MyElement& lhs, const MyElement& rhs) { return ...; }
and then
std::set<MyElement, decltype(cmp)*> s(cmp);
or
std::set<int, decltype(&cmp)> s(&cmp);
struct and operator()
struct cmp {
bool operator() (const MyElement& lhs, const MyElement& rhs) const {
return ...
}
};
and then
std::set<MyElement, cmp> s;
boolean function
bool cmp(const MyElement& lhs, const MyElement& rhs) {
return ...;
}
and then
#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;
std::set<MyElement, Cmp> s;

This is a bit subjective, but to me the cleanest option is struct + operator() to match the definition of std::less, the default comparator for std::set. There's nothing wrong with the other options but a comparison functor is a common pattern and easy to recognize.
You could also define MyElement::operator<, and then you wouldn't need to pass in a comparator separately.

You can use std::mem_fn to bind a member function.
#include <functional>
#include <iostream>
#include <set>
#include <utility>
struct S {
int i;
bool cmp(const S& other) const { return i < other.i; }
};
// Define make function to avoid having to write out template types.
template <typename T, typename Cmp>
std::set<T, Cmp> make_set(Cmp&& cmp) {
return std::set<T, Cmp>{std::forward<Cmp>(cmp)};
}
int main(int argc, char* argv[]) {
auto s = make_set<S>(std::mem_fn(&S::cmp));
s.emplace(S{0});
std::cout << s.begin()->i << std::endl;
return 0;
}

Related

How do I use comparator with is_transparent type?

with C++14 we are allowed to compare elements of some associative containers (like std::set) with other types than the ones stored in a container. It's supposed to work when comparator has is_transparent denoted as a type (see e.g. std::set::find).
Suppose I have a string wrapper which performs some checks on a string (if it's format is valid format and so on - not really important, but constructing it is heavy enough that I'd like to avoid it + it can throw exceptions) and it's stored in std::set to have a container of unique values. How should I write a comparator for it? Should it look like like the one below? Can I overload and use my sw::operator<() to achieve the same?
class sw
{
public:
explicit sw(const std::string& s) : s_(s) { /* dragons be here */ }
const std::string& getString() const { return s_; }
bool operator<(const sw& other) const { return s_ < other.s_; }
private:
std::string s_;
};
struct Comparator
{
using is_transparent = std::true_type;
bool operator()(const sw& lhs, const std::string& rhs) const { return lhs.getString() < rhs; }
bool operator()(const std::string& lhs, const sw& rhs) const { return lhs < rhs.getString(); }
bool operator()(const sw& lhs, const sw& rhs) const { return lhs < rhs; }
};
int main()
{
std::set<sw, Comparator> swSet{ sw{"A"}, sw{"B"}, sw{"C"} };
std::cout << std::boolalpha << (swSet.find(std::string("A")) != swSet.end()) << std::endl;
}
I believe that above code should work as expected, but when I tested it with g++4.9 and clang++3.6, both yielded errors about missing conversion from string to key_type as if string overloads of Comparator::operator() were never taken into account. Am I missing something?
Yes, that code is correct, but it would be simpler to overload operator< to allow comparing your type with std::string and then just use std::less<> (i.e. std::less<void>) which is "transparent" already.
inline bool operator<(const sw& lhs, const std::string& rhs) { return lhs.getString() < rhs; }
inline bool operator<(const std::string& lhs, const sw& rhs) { return lhs < rhs.getString(); }
std::set<sw, std::less<>> swSet{ sw{"A"}, sw{"B"}, sw{"C"} };
Also, it's possibly worth noting that it doesn't matter what you define is_transparent to, either of these would have the same effect as your definition of it:
using is_transparent = std::false_type;
using is_transparent = void;

Pass generic template class as functor parameter

I want to improve the following code snippet:
THNNetIPInfoIter last = std::unique(fist, end, HNInfoIPComparator());
where currently HNInfoIPComparator() is implemented as following:
// equal comparator
class HNInfoIPComparator
{
public:
bool operator()(const THNNetIPInfo &a, const THNNetIPInfo &b);
bool operator()(const SDK::TIPAddressDescription &a, const SDK::TIPAddressDescription &b);
bool operator()(const THNNetIPInfo &a, const SDK::TIPAddressDescription &b);
bool operator()(const SDK::TIPAddressDescription &a, const THNNetIPInfo &b);
};
The reason for this comparator definition is that it might be used with another STL algorithms, like std::set_difference and should handle case when ranges has different types.
The problem is that I have to write huge amount of very similar comparators and it is easy to be entangled with which comparator to use.
I want to write the following snippet:
template<typename SDKClass, typename IDLClass>
class equal {
public:
bool operator()(const IDLClass &a, const IDLClass &b) {
if (strcmp(a.ipaddr.in(), b.ipaddr.in())) {
return false;
}
return true;
}
bool operator()(const SDKClass &a, const SDKClass &b) {
if (strcmp(a.ip_address().c_str(), b.ip_address().c_str())) {
return false;
}
return true;
}
bool operator()(const IDLClass &a, const SDKClass &b) {
if (strcmp(a.ipaddr.in(), b.ip_address().c_str())) {
return false;
}
return true;
}
bool operator()(const SDKClass &a, const IDLClass &b) {
if (strcmp(a.ip_address().c_str(), b.ipaddr.in())) {
return false;
}
return true;
}
};
So HNInfoIPComparator() would be generated depending on types passed as its arguments inside std::unique function.
Therefore I want to pass to std::unique templated functor (class). Is it possible to do that and how?
Also I want to handle case when functor contains some internal data, which are used for comparisons
Most important code samples:
// Automatically generated structure from IDL specification
// Basically simple structure
struct THNNetIPInfo
{
typedef THNNetIPInfo_var _var_type;
typedef THNNetIPInfo_out _out_type;
static void _tao_any_destructor (void *);
::TAO::String_Manager ipaddr;
::TAO::String_Manager netmask;
};
// THNNetIPInfoIter - class external iterator
// which was written manually
typedef Util::CorbaSeqIter<THNNetIPInfoList, THNNetIPInfo> THNNetIPInfoIter;
// THNNetIPInfoList - also automatically generated class
// from IDL specification, list of THNNetIPInfo elements
THNNetIPInfoList list(...);
THNNetIPInfoIter first(&list, 0);
THNNetIPInfoIter end(&list, list.length());
Instead of writing a class template with four comparison operators, write a plain class with a templated comparison operator that adapts the inputs into the keys you want to compare:
class HNInfoIPComparator {
static const char* adapt(const THNNetIPInfo& t) {
return t.ipaddr.in();
}
static const char* adapt(const SDK::TIPAddressDescription& t) {
return t.ip_address().c_str();
}
public:
template <typename T, typename U>
bool operator()(const T& t, const U& u) const {
return !strcmp(adapt(t), adapt(u));
}
};
You can extend the comparator easily by adding overloads of adapt for additional types, e.g., std::string or const char*.

iterate over vector of custom pair using 'std::algorithm' no explicit loops

i wrote my own Pairs class:
#include <string>
#include <utility>
template<class K, class V>
class MyPair : public std::pair<K, V>
{
public:
MyPair(){};
MyPair(const K & x, const V & y) : std::pair<K, V>(x, y) {}
friend bool operator==(const MyPair& p1, const MyPair &p2)
{
return p1.first == p2.first; // requires that type K implements operator=
}
bool operator() (const MyPair& p1, const MyPair &p2)
{
return ( p1.first < p2.first ); // requires that type K implements operator<
}
};
and i declared a vector of these custom pairs, and i am trying to use a std::for_each function to implement a print() function, but for some reason i can't do that, i am getting all kind of errors, i have a feeling that this might be duo to the need for a custom iterator ?
how would i implement a solution, without explicit loops?
typedef typename std::vector<MyPair<std::string, int> > myVecType;
myVecType wordvec;
void WordVector::print() const
{
std::for_each(wordvec.begin(), wordvec.end(), [&](){});
}
The predicate for a std::for_each() needs to take a parameter because this function passes each element every time it iterates:
std::for_each(wordvec.begin(), wordvec.end(),
[&] (MyPair<std::string, int>& p) { /* ... */ });
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^

problems with c++ set container

When I try to compile the following code:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
template <class T, class S>
class Property
{
public:
pair<T,S> p;
Property(T t, S s) { p = make_pair(t,s);}
};
int main()
{
set< Property<string, string> > properties;
Property<string, string> name("name", "Andy");
properties.insert(name);
}
I get the compilation error.
However, when I replace set by vector and hence use the the push_back function instead of insert function everything works fine. Could anyone explain me what am I doing wrong?
Thanks in advice.
std::set stores its values in a sorted binary tree, so it needs to know how to compare the values it holds. By default it uses std::less as a comparison function, which for un-specialized user defined types tries to call operator<. So, the easiest way to tell the set how to compare your objects is to define an operator< for your class:
template <class T, class S>
class Property
{
public:
pair<T,S> p;
Property(T t, S s) { p = make_pair(t,s);}
bool operator<(const Property<T,S>& rhs) const
{
return p < rhs.p;
}
};
However, there are also other ways of telling std::set how to compare your type. One is to specialize the std::less template for your class:
namespace std {
template<typename T,typename S>
struct less<Property<T, S> >
{
bool operator()(const Property<T, S>& lhs, const Property<T,S>& rhs) const
{
return lhs.p < rhs.p;
}
};
}
Another is to replace the default comparison type with a function with the correct signature, or a class that has an operator() defined with the correct signature. This is where things start to get ugly.
// Comparison function
template<typename T, typename S>
bool property_less_function(const Property<T,S>& lhs, const Property<T,S>& rhs)
{
return lhs.p < rhs.p;
}
// Comparison functor
template<typename T, typename S>
struct PropertyLess
{
bool operator()(const Property<T,S>& lhs, const Property<T,S>& rhs) const
{
return lhs.p < rhs.p;
}
};
int main()
{
// Set using comparison function.
// Have to pass the function pointer in the constructor so it knows
// which function to call. The syntax could be cleaned up with some
// typedefs.
std::set<Property<std::string, std::string>,
bool(*)(const Property<std::string, std::string>&,
const Property<std::string, std::string>&)>
set1(&property_less_function<std::string, std::string>);
// Set using comparison functor. Don't have to pass a value for the functor
// because it will be default constructed.
std::set<Property<std::string, std::string>, PropertyLess<std::string, std::string> > set2;
}
Keep in mind that whatever less-than function you use, that function must define a strict weak ordering for your type.
In order to insert something into std::set, you need to have operator< defined.
For example this compiles fine on GCC 4.7.2:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
template <class T, class S>
class Property
{
public:
pair<T,S> p;
Property(T t, S s) {
p = make_pair(t,s);
}
bool operator<(const Property& p2) const {
//Something naive..
return p < p2.p;
}
};
int main()
{
set< Property<string, string> > properties;
Property<string, string> name("name", "Andy");
properties.insert(name);
}
An alternative would be to use std::unordered_set though that would require you to provide a hash for the key and defining operator==.

Overloaded operators for std::bind placeholders

boost::bind overloads several operators for its placeholders:
For convenience, the function objects produced by bind overload the logical not operator ! and the relational and logical operators ==, !=, <, <=, >, >=, &&, ||.
For example, this allows me to pass _1 == desired_value as a predicate to STL algorithms.
Unfortunately, std::bind does not seem to overload these operators :(
Why is that?
What is a good workaround to simulate _1 == desired_value with std::bind?
IIRC, Boost.Bind only overloads those operators for the placeholders because the original Boost Lambda Library, which Boost.Bind is an improvement of, did (Boost.Bind is obsolete thanks to Boost.Phoenix, btw). std::bind's placeholders are only intended for exactly that purpose, as placeholders for arguments to std::bind.
As a workaround, use polymorphic functors:
struct compare_equal{
template<class LHS, class RHS>
bool operator()(LHS&& lhs, RHS&& rhs){ // assume bool return
return std::forward<LHS>(lhs) == std::forward<RHS>(rhs);
}
};
// ...
auto bound = std::bind(compare_equal(), _1, desired_value);
Live example on Ideone.
You could always overload these operators for the placeholders yourself, for example operator<:
struct less12
{
template<typename T, typename U>
bool operator()(const T& a, const U& b) const
{
return a < b;
}
};
less12 operator<(decltype(_1), decltype(_2))
{
return less12();
}
struct less21
{
template<typename U, typename T>
bool operator()(const U& b, const T& a) const
{
return a < b;
}
};
less21 operator<(decltype(_2), decltype(_1))
{
return less21();
}
template<typename T>
struct lessa1
{
const T& a;
template<typename U>
bool operator()(const U& b) const
{
return a < b;
}
};
template<typename T>
lessa1<T> operator<(const T& a, decltype(_1))
{
lessa1<T> result = {a};
return result;
}
template<typename U>
struct less1b
{
const U& b;
template<typename T>
bool operator()(const T& a) const
{
return a < b;
}
};
template<typename U>
less1b<U> operator<(decltype(_1), const U& b)
{
less1b<U> result = {b};
return result;
}
Here is a usage example, compared with binders (custom less12 vs std::less) and lambda syntax:
template<typename Iterator>
void quicksort(Iterator begin, Iterator end)
{
// ...
auto m = std::partition(begin + 1, end, _1 < *begin);
auto m = std::partition(begin + 1, end, std::bind(less12(), _1, *begin));
auto m = std::partition(begin + 1, end, std::bind(std::less<typename std::iterator_traits<Iterator>::value_type>(), _1, *begin));
auto m = std::partition(begin + 1, end, [begin](const typename std::iterator_traits<Iterator>::value_type& x) { return x < *begin; } );
// ...
}
Really looking forward to N3421 and polymorphic lambdas here :)
std::bind does not seem to overload these operators?
Why is that?
Because C++11 added lambdas which provide the same, if not better, conveniences to produce anonymous functor objects.
What is a good workaround to simulate _1 == desired_value with std::bind?
std::bind is not used to simulate the behavior. Use C++11 lambdas to implement the answer to your question:
std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), [](int i) -> bool { return i == desired_value; });
Please note that you don't need the "-> bool" syntax if you are using a fairly recent compiler that can derive the return type.
If you don't want to or can't use C++11 lambdas, then you can create a non-anonymous functor like this:
bool IsDesiredValue (int i) {
return (i == desired_value);
}
std::vector<int>::iterator i = std::find_if (myvector.begin(), myvector.end(), IsDesiredValue);
For another example, here is a use of C++11 lambdas being used to create an anonymous functor to sort a vector by a value of a type:
std::sort(myVector.begin(), myVector.end(), [](const Foo& i, const Foo& j) -> bool { return i.myValue < j.myValue; });
An alternative, non-lambda version would be:
struct myclass {
bool operator() (const Foo& i, const Foo& j) { return (i.myValue < j.myValue); }
} myobject;
std::sort(myVector.begin(), myVector.end(), myobject);
And here's how you would do the same sort using boost's operator overload:
std::sort(myVector.begin(), myVector.end(), boost::bind(&MyClass::myValue, _1) < boost::bind(&MyClass::myValue, _2))
I think it is because C++11 has lambda, and therefore I don't see why you would need such operators instead.