C++. Generic class receiving lambda and struct function

Asked

Viewed 151 times

1

Expensive,

I’m trying to do some things in C++, but I’m still starting both in language and in the functional paradigm. What I need is to insert a function lambda in a pair. The first member of pair will be an object struct, and the second, the lambda function. My lambda function receives as parameters a struct and a vector<string> and returns a string, something like:

auto lambda = [](struct &s, vector<string> &v){
    return (s.atributo + " " + v[0]);
};

In that case, the pair would be something like:

pair<struct, funcao_lambda> par;

I have encountered many type errors (when trying to insert the lambda function in a map), and in the search for a solution, I found something I can solve: a generic class that wraps the Lambda functions and has a lambda function execution method. I found an example of a generic class for this, but due to my lack of knowledge of the language, I don’t know how to change it to accept functions with parameters (no capture):

class T {
private:
    double (*expression)();
public:
    T(double (*exp)()) : expression(exp) {}
    double execute() {
        return this->expression();
    }
};

int main() {
    T t([]()->double {return 1+1;});
    double val = t.execute(); // val = 2
    return 0;
}

In addition to changing the types double for string, What else do I need to change in the class to get lambda functions with parameters (no capture, only parameters)? The idea is that T-types are created in this form:

T t([](struct &s, vector<string> &v) -> string {
     return (s.atributo + " " + v[0]);
});

And be executed in that form:

string texto = t.execute(struct, vector<string>);

Is that possible? And more importantly, it will be possible to insert this type T into the pair?

I appreciate any help.

1 answer

2


I didn’t really like this solution with the use of a class and a pointer to a function. It seems difficult to read and, besides, the use of pointers for function in C++11 (and later) is something a little old-fashioned.

If your intention is just to insert a lambda expression into a std::pair or in a std::map, the use of a class instance std::function would solve your type problems. Check out the following code:

#include <vector>
#include <string>
#include <iostream>
#include <functional>
#include <map>
#include <utility>

struct A{
    std::string atributo;

    //sobrecarga do operador<
    //sem isso um objeto da struct A não pode ser chave de um std::map
    bool operator<(const A &other) const {
        return (atributo < other.atributo);
    }
};

int main() {

    //cria a expressão lambda
    std::function<std::string(A&, std::vector<std::string>&)> lambda = 
        [](A& s, std::vector<std::string> &v)->std::string {
        return (s.atributo + " " + v[0]);
    };

    // cria uma instância de A
    A s;
    s.atributo = "Waiting for";

    //cria um std::vector 
    std::vector<std::string> v{ "Godot", "Hamlet" };

    //insere a expressão lambda e o vetor dentro de um std::pair 
    std::pair<A, std::function<std::string(A&, std::vector<std::string>&)>> myPair =
    { s, lambda };

    //testa o par 
    std::cout << myPair.second(myPair.first, v) << "\n";

    //insere o par em um std::map
    std::map<A, std::function<std::string(A&, std::vector<std::string>&)>> myMap;
    myMap.insert(myPair);

    //testa o map
    std::cout << myMap[s](s, v);

    std::cin.get();
    return 0;
}

/*saída:
Waiting for Godot
Waiting for Godot
*/

EDIT: I made the solution with the class you presented, if you want to compare both solutions follow the code:

#include <iostream>
#include <vector>
#include <utility>
#include <map>
#include <string>

struct A {
    std::string atributo;

    //sobrecarga do operador<
    //sem isso um objeto da struct A não pode ser chave de um std::map
    bool operator<(const A &other) const {
        return (atributo < other.atributo);
    }
};

//mudei o nome da classe para B, pois T confunde com template.
class B {
private:
    std::string(*expression)(A &s, std::vector<std::string>& v);
public:
    B(std::string(*exp)(A &s, std::vector<std::string>& v)) : expression(exp) {}
    std::string execute(A &s, std::vector<std::string>& v) {
        return this->expression(s, v);
    }
};

int main() {

    // cria uma instância de A
    A a;
    a.atributo = "Waiting for";

    //cria uma instância de B
    B b([](A& s, std::vector<std::string> &v)->std::string {
        return s.atributo + " " + v[0]; });

    //cria um std::vector
    std::vector<std::string> v{ "Godot", "Hamlet" };

    //criar std::pair
    std::pair<A, B> myPair = { a, b };

    //testa std::pair
    std::cout << myPair.second.execute(myPair.first, v) << '\n';    

    //cria std::map e insere myPair
    std::map<A, B> myMap;
    myMap.insert(myPair);

    //testa std::map
    std::cout << myMap.at(a).execute(a, v) << '\n';

    std::cin.get();
    return 0;
}
  • First of all, thank you so much for your help! Very precise! Of course I need to study C++ a lot... In your example, one of the attributes of the lambda function is the first member of the pair, and that’s exactly what I need. I believe this solution will solve my problem. And I will use the map again, because in it I will reference which struct pair and lambda function I should use later. But I still have some doubts, which I will put in the next comment because of space :)

    1. The map key will be a string, not the struct as in your example, because when I go to fetch the pair, I will compare it to a string. So I just change the map creation to "map<string, B> myMap;"? And what would the insertion look like in myMap? 2. After all, I need the string that the lambda function returns, because it will process attributes of the struct and others that I will pass through the vector. To get this return string from the lambda function, what would it look like? Something like "string txt1 = myMap.at("txt2"). execute(myPair.first, v);"? This final string txt1 is what I need, after all this battle!
  • 1

    Answering your questions: 1) Yes, if you need a string key, switch to string; 2) There are different methods for inserting into a string map, I used insert(), passing a pair as parameter, but this is something you need to study; 3) the function returns a string, then, yes, you can store the return value in a variable of type string.

  • @Danielelias, mark the answer as "accept", she’s very good

  • Yes, it’s excellent! I forgot it, I’m already dialing. I’m solving some other problems here.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.