I have a list of functions that need to be applied to a single string additively. How do I express the "Apply" function.
auto outPutString = inputString
.Apply(Transformation1)
.Apply(Transformation2)
in c++?
The string is the std::string
I have a list of functions that need to be applied to a single string additively. How do I express the "Apply" function.
auto outPutString = inputString
.Apply(Transformation1)
.Apply(Transformation2)
in c++?
The string is the std::string
From C++ 11 onwards, you may also write an Apply
function using variadic templates:
template <typename OutputT, typename InputT>
OutputT Apply(const InputT &obj)
{
return obj;
}
template <typename OutputT, typename InputT, typename Func, typename... OtherFuncs>
OutputT Apply(const InputT &obj, Func f, OtherFuncs... other)
{
return Apply<OutputT, decltype(f(obj))>(f(obj), other...);
}
Then you may use this as follows:
auto res = Apply<std::string>(
"Hello",
[](const std::string &str) { return str + " "; }, // Applicator 1
[](const std::string &str) { return str + "World"; } // Applicator 2
);
The result in this case is »Hello World«.
Because the above construction distinguishes between InputT
and OutputT
, you may "mix" types, as in:
auto res = Apply<size_t>(
"Hello",
[](const std::string &str) { return str + " World"; }, // Applicator 1
[](const std::string &str) { return str.size(); } // Applicator 2
);
This time the result is 11
.
Finally, if you really want to use chaining syntax, you could write a class that wraps the initial object and has an Apply
method.
Like this:
auto outPutString = Transformation2(Transformation1(inputString));
std::string manipulateString(std::string str) {/* do something */; return result;}
std::string manipulateStringAgain(std::string str) {/* do something else */; return result;}
std::string manipulateMe = "hello";
auto resultString = manipulateString(manipulateStringAgain(manipulateMe));
I'm going to assume when you say "a list of functions", you mean one that varies at runtime. Other answers are better if the list is static.
#include <vector>
#include <string>
#include <functional>
#include <numeric>
std::vector<std::function<std::string(std::string)>> funcs = { Transformation1, Transformation2 }; // or gotten from wherever
auto output = std::accumulate(funcs.begin(), funcs.end(), input, [](auto acc, auto fun){ return fun(acc); });
It is possible in C and C++ as well, to define pointer to a fuction and to create vector of pointers to functions. Later, you can invoke functions inside a loop with desired arguments. Please let me know if you are interested for details.
If you would like to keep the order, create some wrapping class and put your manipulation functions in there. For example:
#include <iostream>
#include <string>
using namespace std;
class StringManipulator
{
public:
StringManipulator(std::string str) : str(str) {}
operator std::string() {return str;}
StringManipulator& doSomething() {str += "1"; return *this;}
StringManipulator& doSomethingElse() {str += "2"; return *this;}
private:
std::string str;
};
int main() {
std::string result = StringManipulator("0").doSomething().doSomethingElse();
std::cout << result;
return 0;
}
Output is 012
.
operator std::string
ensures implicit conversion.
#include <vector>
#include <iostream>
// three funcitons with a string as the parameter
int funca(std::string& str)
{
std::cout << "funca:" << str << std::endl;
return 1;
}
int funcb(std::string& str)
{
std::cout << "funcb:" << str << std::endl;
return 2;
}
int funcd(std::string& str)
{
std::cout << "funcd:" << str << std::endl;
return 3;
}
int main()
{
// definition of the string
std::string str = "the string";
// declare vector of pointers to function returning an int and receiving a string as a parameter:
std::vector< int(*)(std::string&)> pf;
// load func pointers to vector:
pf.push_back(&funca);
pf.push_back(&funcb);
pf.push_back(&funcd);
//declare vector iterator:
std::vector<int (*)(std::string&)>::iterator it;
// iterate vector of func pointers:
for (it = pf.begin() ; it != pf.end(); ++it)
{
// function call using pointers and passing parameter str
// you can get return value as from 'normal' function
int ret = (*it)(str);
std::cout << "function returns:" << ret << std::endl;
}
}
/*
compiled and executed on ubuntu 18.04, output:
funca:the string
function returns:1
funcb:the string
function returns:2
funcd:the string
function returns:3
*/