c++c++17

Pass getter-function as parameter


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?


Solution

  • 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.