Home > Software engineering >  Problem passing a method of a class as a std::function parameter
Problem passing a method of a class as a std::function parameter

Time:05-08

I am trying to write this function:

void CChristianLifeMinistryEditorDlg::PerformAutoAssignForAssignment(
    const MSAToolsLibrary::AssignmentType eAssignType, 
    const CString strStartingName, 
    std::function<void(CString)> funcSetAssignName)
{
    CString strName = L"abc";
    CChristianLifeMinistryEntry *pEntry = xxx;
    // ...
    pEntry->funcSetAssignName(strName);
}

I am having difficulty in passing a function to it:

PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_Host, L"Name 1" );
PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_CoHost, L"Name 2");
PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_ConductorCBS, L"Name 3");
PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_ReaderCBS, L"Name 4");

At the moment I am not passing a function:

void CChristianLifeMinistryEditorDlg::PerformAutoAssignForAssignment(
    const MSAToolsLibrary::AssignmentType eAssignType, 
    const CString strStartingName)
{
    CString strName = L"abc";
    CChristianLifeMinistryEntry *pEntry = xxx;
    // ...
    pEntry->funcSetAssignName(strName);

    if (eAssignType == MSAToolsLibrary::AssignmentType_ConductorCBS)
    {
        pEntry->SetCBSConductor(strNameToUse);
    }
    else if (eAssignType == MSAToolsLibrary::AssignmentType_ReaderCBS)
    {
        pEntry->SetCBSReader(strNameToUse);
    }
    else if (eAssignType == MSAToolsLibrary::AssignmentType_Host)
    {
        pEntry->SetVideoHost(strNameToUse);
    }
    else if (eAssignType == MSAToolsLibrary::AssignmentType_CoHost)
    {
        pEntry->SetVideoCohost(strNameToUse);
    }

}

Can you see what I am trying to do? I had hoped to avoid the if/else ladder because eventually I will have more functions to add to the ladder for other assignments. I had hoped I could pass them as a function.

CodePudding user response:

In order make a method of a class callable, you must supply a this object. Wrapping such a method in a std::function can be done in 2 ways. Both of them associate a specific class instance with a method, making it callable:

  1. Use std::bind - see the documentation: std::bind, and a specific stackoverflow post regarding this issue: Using generic std::function objects with member functions in one class.

  2. Use lambda functions, that keep the this context as a data member of the lambda object. See some general info here: What is a lambda expression in C 11?.

The code below demonstrates this 2 ways. I used std::string instead of MFC's CString, but the principle is the same.

#include <iostream>
#include <string>
#include <functional>

struct A
{
    void Do1(std::string const & str) { std::cout << "A::Do1" << std::endl; };
    void Do2(std::string const & str) { std::cout << "A::Do2" << std::endl; };
    void Do3(std::string const & str) { std::cout << "A::Do3" << std::endl; };
};


struct Dlg
{
    A a;

    void Perform(std::string const & str, std::function<void(std::string)> funcSetAssignName)
    {
        funcSetAssignName(str);
    }

    void m()
    {
        std::string str = "abc";

        // Pass method as std::function using std::bind:
        Perform(str, std::bind(&A::Do1, a, std::placeholders::_1));
        Perform(str, std::bind(&A::Do2, a, std::placeholders::_1));
        Perform(str, std::bind(&A::Do3, a, std::placeholders::_1));

        // Pass method as std::function using lambdas:
        Perform(str, [this](std::string const & str) { this->a.Do1(str); });
        Perform(str, [this](std::string const & str) { this->a.Do2(str); });
        Perform(str, [this](std::string const & str) { this->a.Do3(str); });
    }
};

int main()
{
    Dlg dlg;
    dlg.m();
    return 0;
}

The output:

A::Do1
A::Do2
A::Do3
A::Do1
A::Do2
A::Do3

UPDATE:
Following the OP's question in the comment below:
In order to let you determine the instance to apply the method inside Perform, you can use pointer to method. The downside is that the method you pass must be of a specified class (A in my example below). I.e. you loose the ability to pass callables from various classes (or global).

See the code example below:

#include <iostream>
#include <string>

struct A
{
    void Do1(std::string const & str) { std::cout << "A::Do1" << std::endl; };
    void Do2(std::string const & str) { std::cout << "A::Do2" << std::endl; };
    void Do3(std::string const & str) { std::cout << "A::Do3" << std::endl; };
};


struct Dlg
{
    A a;

    void Perform(std::string const & str, void (A::*funcSetAssignName)(std::string const & str))
    {
        // Here the instance for class A can be determined (I simply used `a`):
        (a.*funcSetAssignName)(str);
    }

    void m()
    {
        std::string str = "abc";

        // Pass pointer to method:
        Perform(str, &A::Do1);
        Perform(str, &A::Do2);
        Perform(str, &A::Do3);
    }
};

int main()
{
    Dlg dlg;
    dlg.m();
    return 0;
}

You can see some more examples here: Calling C member functions via a function pointer.

  • Related