Erix Liechstenstein
Erix Liechstenstein

Reputation: 41

Passing a class member function to for_each

My question is: how do I pass a class member function into for_each

Code I am trying to get to work: (works when function is defined outside of class)

The part which fails is commented out - the one using for_each with function as the class member function

Any advice on how to get this to work?

#include <iostream>
#include <algorithm>
#include <vector>

void my_function(std::string str)
{
std::cout << "processing settings: " << str << std::endl;
}

class Settings_vector{

public:
std::vector <std::string> settings;

Settings_vector(){                          // push back vector of objects
    settings.push_back("settings 1");
    settings.push_back("settings 2");
    settings.push_back("settings 3");
    settings.push_back("settings 4");
                 }

void tester(std::string settings_string){
    std::cout << "processing settings: " << settings_string << std::endl;
                                        }
};

int main()
{
//std::vector<std::string> my_vector;

Settings_vector settings_vector;

    std:: cout << "doing things the non-class way\n" << std::endl;
for_each(settings_vector.settings.begin(), settings_vector.settings.end(),     my_function); // testing function
// WORKS
/*
  std:: cout << "doing things the modern way\n" << std::endl;
for_each(settings_vector.settings.begin(), settings_vector.settings.end(),    settings_vector.tester); // testing function
// FAILS
*/

std:: cout << "doing things the oldskool way\n" << std::endl;
for (int i = 0;i<settings_vector.settings.size();++i) {
settings_vector.tester(settings_vector.settings[i]);
}
// WORKS


return 0;
}

Upvotes: 3

Views: 1683

Answers (2)

Francesco Garbin
Francesco Garbin

Reputation: 61

For a more concise sintax, use a static class method. I've slightly edited your code for improved readability within the context of your question (aka removing distractions).

#include <iostream>
#include <vector>

class Settings {
  
  public:
    std::vector <std::string> settings;
    
    Settings(std::initializer_list<std::string> l)
    : settings(l) {
    }
  
    static void tester(std::string const& str) {
        std::cout << "processing settings: " << str << std::endl;
    }
};

int main() {
    Settings sv {"settings 1", "settings 2", "settings 3", "settings 4"};
    for_each(sv.settings.begin(), sv.settings.end(), Settings::tester);
    return 0;
}

Upvotes: 0

ovanes
ovanes

Reputation: 5673

The easiest way would be to use a lambda expression. A bit more complex approach is to use std::bind() to bind all known arguments (here the instance of the class to the member function) and leave unknown arguments with placeholders _1, _2, etc.

#include <iostream>
#include <algorithm>
#include <vector>

class Settings_vector
{
  Settings_vector()
    : settings { "settings 1"
               , "settings 2"
               , "settings 3"
               , "settings 4"
               }
  {}

  void tester(std::string settings_string)
  { std::cout << "processing settings: " << settings_string << std::endl; }

public:
  std::vector <std::string> settings;
};

int main()
{
  Settings_vector settings_vector;

  using namespace std;
  using namespace std::placeholders;  // for _1

  // Possibility Nr. 1: Use a Lambda Function
  for_each( settings_vector.settings.begin(), settings_vector.settings.end()
          , [&settings_vector](auto input){ settings_vector.tester(input); }
          )
  ;

  // Possibility Nr. 2: Partially bind existing arguments and use placeholders for others 
  for_each( settings_vector.settings.begin(), settings_vector.settings.end()
          , std::bind(&Settings_vector::tester, &settings_vector, _1);
          )
  ;

  return 0;
}

Explanations:

  • I think a lambda is straight forward. In the square brackets, you declare what goes into a closure. Here we pass settings_vector. Preceding it with & means that this instance is passed by reference. In the parenthesis, we declare the parameters to the function. I cheated a little bit, as auto in lambda expressions was introduced in C++14, but you can write it as type std::string as well.

  • std::bind() binds parameters to a function pointer and returns a callable object. If all parameters are present, the returned callable has no parameters and can be called like: callable(). Here, we want a callable to accept the result of the iteration. Thus, we use a placeholder _1, which states that this argument will be changed at call-time. Now 2 things remain:

    • Getting a pointer to a member function. This is done by using &TypeName::MemberName, in this case &Settings_vector::tester.

    • Passing a this pointer to a member function call: &settings_vector. When calling a member function, an object must be passed for which this member function is called. Because we just got a pointer to a member function without any bound object to it, that's why the second param is &settings_vector.

Upvotes: 6

Related Questions