Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass getter-function as parameter

Tags:

c++

c++17

I have the following struct:

struct Foo  
{
public:
  int getI();
  void setI(const int& i);

  double getD();
  void setDouble(const double& d);
private:
  int m_i;    
  double m_d;
}

Now I want to call a function which sorts a list of Foo depending on one property. The sort-function looked like this, when I was using only members in my struct Foo. But now I need to use getter and setter functions and make the member private.

template <typename T>
void sort(T Foo::* mp, bool asc)
{
    std::sort(m_items.begin(), m_items.end(), [=](const Foo& s1, const Foo& s2) { return asc ? ((s1.*mp) < (s2.*mp)) : ((s1.*mp) > (s2.*mp)); });
}

I called it like that:

myList->sort(&Foo:m_i); // or
myList->sort(&Foo:m_d);

Thats not possible anymore, because the members are private and they need to be private. Is there a way of passing the getter-function which can be invoked to do the comparison inside the sort-function?

like image 303
Me3nTaL Avatar asked Dec 03 '25 21:12

Me3nTaL


2 Answers

You can use a pointer-to-member-function:

// (You also need to make your getter functions const because you use `const Foo&` parameters)
template <typename T>
void sort(T(Foo::* pmf)() const, bool asc)
{
    std::sort(m_items.begin(), m_items.end(), [=](const Foo& s1, const Foo& s2) { return asc ? ((s1.*pmf)() < (s2.*pmf)()) : ((s1.*pmf)() > (s2.*pmf)()); });
}

// Example: sort(&Foo::getD, true);

I would also recommend making it more generic:

template <typename F>
void sort(F&& projection, bool asc) {
    std::sort(m_items.beegin(), m_items.end(), [&projection, asc](const Foo& s1, const Foo& s2) {
        return std::invoke(projection, asc ? s1 : s2) < std::invoke(projection, asc ? s2 : s1);
    });
}

Which can be called with both pointer-to-member-functions and pointer-to-data-members, as well as lambda expressions if you ever need something more complicated or need to change the API again.

like image 90
Artyer Avatar answered Dec 05 '25 12:12

Artyer


If you had access to C++20 or newer, you wouldn't need a custom sorting function for this, as the standard function is flexible enough to do this as a oneliner:

std::ranges::sort(m_items, {}, &Foo::getI); // Ascending
std::ranges::sort(m_items, std::greater{}, &Foo::getI); // Descending
like image 34
HolyBlackCat Avatar answered Dec 05 '25 13:12

HolyBlackCat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!