C++ function map with varialbles input - c++

In C++, I want to use a map of functions with different type of input or output.
Do to so, I found that using a map with any type could be a way.
But I get several problems. First, I can not use directly the functions in the map.
However, I can use a lambda function to wrap the functions then use these lambda functions in the map.
But, I get a second problem, I still need to cast with the lambda function which is not a variable. This makes a use from a string variable complicated.
Here is a MWE:
#include <any>
#include <functional>
#include <iostream>
#include <map>
#include <string>
void funct0()
{
std::cout << "funct0" << std::endl;
}
void funct1(int p)
{
std::cout << "funct1 " << p << std::endl;
};
int funct2(int p, std::string s)
{
std::cout << "funct2 " << s << std::endl;
return p+1;
};
float funct3(int a, float b)
{
std::cout << "funct3 " << std::endl;
return a +b;
}
auto funct4(int a, float b)
{
std::cout << "funct4 " << std::endl;
std::vector<float> v;
v.push_back(a);
v.push_back(b);
return v;
}
int main()
{
std::map<std::string, std::any> mapFunct;
mapFunct["F0"]= funct0;
// mapFunct["FO"](); // error: no match for call to ‘(std::map<std::__cxx11::basic_string<char>, std::any>::mapped_type {aka std::any}) ()’
mapFunct["F1"]= funct1;
// mapFunct["F1"](12); // error: no match for call to ‘(std::map<std::__cxx11::basic_string<char>, std::any>::mapped_type {aka std::any}) (int)’
// WHY THIS IS NOT WORKING ?
// From this link: https://stackoverflow.com/questions/61969316/is-it-possible-to-put-lambda-expressions-into-a-map-or-list-in-c
auto lambda0 = [](){funct0();};
auto lambda1 = [](int p) { funct1(p); return p; };
auto lambda2 = [](int p, std::string s) { return funct2(p,s); };
auto lambda3 = [](int a, float b){return funct3(a,b);};
auto lambda4 = [](int a, float b){return funct4(a,b);};
std::map<std::string, std::any> mapLambda;
mapLambda["L0"]=lambda0;
mapLambda["L1"]=lambda1;
mapLambda["L2"]=lambda2;
mapLambda["L3"]=lambda3;
mapLambda["L4"]=lambda4;
std::any_cast<decltype(lambda0)>(mapLambda["L0"])();
std::any_cast<decltype(lambda1)>(mapLambda["L1"])(2);
std::cout << std::any_cast<decltype(lambda2)>(mapLambda["L2"])(4, "HELLO") << std::endl;
std::cout << std::any_cast<decltype(lambda3)>(mapLambda["L3"])(3, 4.32) << std::endl ;
auto vec4= std::any_cast<decltype(lambda4)>(mapLambda["L4"])(6, 9.1);
std::cout << "vec4" << vec4[1] << vec4[2] << std::endl ;
std::vector<std::string> inputString;
inputString.push_back("L3(3, 4.32)");
inputString.push_back("L4(6, 9.1)");
// Using a for loop with iterator
for(auto it = std::begin(inputString); it != std::end(inputString); ++it) {
std::cout << *it << "\n";
std::string line=*it;
std::string functionInput = line.substr( 0, line.find("(") );
std::cout << functionInput << std::endl;
// argumentsInput= ;
mapLambda[functionInput](argumentsInput);
}
};
So my question are:
Why my example is working with lambda functions and not the functions ?
How can I make the last part of my example works only from the inputString variable? (ie, knowing the correct casting from the string variable)

What you probably want is something like this:
using CallWrapper = std::function<void(const std::string&)>;
std::map<std::string, CallWrapper> mapLambda;
mapLambda["L0"] = [funct0](const std::string&) { funct0(); };
mapLambda["L1"] = [funct1](const std::string& args) {
int p = ...; // parse the argument from `args`
funct1(p);
};
mapLambda["L2"] = [funct2](const std::string& args) {
// parse the arguments from `args`
int p = ...;
std::string s = ...;
funct2(p, s);
};
Now you can run the loop you envision:
for(const std::string& line : inputString) {
size_t pos = line.find('(');
std::string functionInput = line.substr( 0, pos);
std::string argumentsInput = line.substr(pos);
mapLambda[functionInput](argumentsInput);
}
The hard part, of course, is "parse the arguments from args", left as an exercise for the reader.

std::any_cast needs to cast to constructible types. A standard C++ function is neither a type nor constructible (it's just a group of statements given a name [edit: this isn't technically true, but what's going on under the hood is fairly complicated]), but std::function is. One way to get around this is to assign a standard C++ function to an std::function. Here's an example using a std::map like you were using:
#include <any>
#include <functional>
#include <iostream>
#include <map>
int my_func(int val) { return val + 1; }
std::function<int(int)> f = my_func;
int main() {
auto my_map = std::map<std::string, std::any>();
my_map["func"] = f;
std::cout << std::any_cast<std::function<int(int)>>(my_map["func"])(13) << std::endl; // prints "14"
return 0;
}
Lambdas are constructible types, which is why your code works for lambdas.
To answer your second question: I don't think it's possible. Functions with different signatures are different types, and you have to know what you're casting to. std::function<int(int, string)> and std::function<float(int, float)>, for example, are different types.
Also, the intended purpose of lambdas is to be used once then discarded. If you're going to keep lambdas around for reuse, it's better to simply just use functions.

Related

Is it possible to put lambda expressions into a map or list in C++?

#DanielLangr #luxun #cdhowie sorry for the XY problem. i am not sure i can explain well, but i try my best. the situation is almost like this: there is a base object "Worker" and some children. chef、tailor... children has the same action like walk、run、sleep...but different skill,chef can make food, tailor can Make clothes. Invoker call Worker dothings but do not exactly know their profession.so i add a interface dothings(Thing) on Worker the base object. Thing is an enum,value is MakeFood、MakeClothes...
Worker *w = new Chef();
w->dothings(MakeFood);//
w->dothings(MakeClothes);//throw exception "w do not have skill"
so i think meybe use a container in children that describe what it can do and how to do.
hope i explained clearly.and is there a better solution?
I want to put different lambda expressions into a list or Qmap, like below.
Qmap<String, lambda> map;
map.insert("first",[](int i) -> int {return i;});
map.insert("second",[](string s) -> string {return s;});
Is it possible in C++? And what is the type of lambda?
It is possible but using function wrapper.
For example,
std::map<std::string, std::function<void(std::string)>> my_map;
my_map.emplace("first", [](std::string i) { std::cout << i << std::endl; });
However, if you want to pass any type of argument to your function and return any type from your lambda/function, use boost::any. You also use std::any if you are using C++17 or above.
EDIT:
A working example:
#include <iostream>
#include <string>
#include <functional>
#include <map>
#include <boost/any.hpp>
int main()
{
auto any = [](boost::any i)
{
std::cout << "In any" << std::endl;
if (i.type() == typeid(int))
std::cout << boost::any_cast<int>(i) << std::endl;
return boost::any(1000);
};
std::map<std::string, std::function<boost::any(boost::any)>> my_map;
my_map.emplace("first", any);
my_map.emplace("second", [](boost::any i) -> boost::any { });
auto ret = my_map["first"](100);
std::cout << boost::any_cast<int>(ret) << std::endl;
return 0;
}
Outputs:
In any
100
1000
With any, the solution may look like as follows:
auto lambda1 = [](int i) { return i; };
auto lambda2 = [](std::string s) { return s; };
std::map<std::string, std::any> map;
map["first"] = lambda1;
map["second"] = lambda2;
std::cout << std::any_cast<decltype(lambda1)>(map["first"])(-1) << std::endl;
std::cout << std::any_cast<decltype(lambda2)>(map["second"])("hello") << std::endl;
I am not familiar with Qmap and String, so I used the types from the C++ Standard Library.
Live demo: https://godbolt.org/z/8XK8de
Alternatively, you can also additionally use std::function if you want to avoid those decltypes:
std::map<std::string, std::any> map;
map["first"] = std::function<int(int)>( [](int i) { return i; } );
map["second"] = std::function<std::string(std::string)>( [](std::string s) { return s; } );
std::cout << std::any_cast<std::function<int(int)>>(map["first"])(-1) << std::endl;
std::cout << std::any_cast<std::function<std::string(std::string)>>(map["second"])("hello") << std::endl
Live demo: https://godbolt.org/z/XAc3Q2
However, as other pointed out to, this really seems to be an XY problem.
It is possible as long as you are trying to insert the same lambda type ( your example has different lambda types) You have to be careful how you do it but it does work. For example
#include <iostream>
#include <map>
int main(){
auto factory = [](int i){
return [=](int j){return i+j;};
};
using L = decltype(factory(0));
std::map<int,L> map;
map.emplace(0,factory(0));
map.emplace(7,factory(7));
std::cout << map.at(0)(3) << std::endl ;
std::cout << map.at(7)(3) << std::endl ;
}
outputs
3
10
as expected and not a std::function in sight! However the following does not work
#include <iostream>
#include <map>
int main(){
auto factory = [](int i){
return [=](int j){return i+j;};
};
using L = decltype(factory(0));
std::map<int,L> map;
map[0]=factory(0);
map[7]=factory(7);
std::cout << map[0](3) << std::endl ;
std::cout << map[7](3) << std::endl ;
}
Using the indexing operator tries to use copy assignment whereas emplace doesn't.
https://godbolt.org/z/co1vno6xb

function that may work in-place

I have the following function:
shared_ptr_of_points filter(const shared_ptr_of_points input){
shared_ptr_of_points result = make_new();
for(const auto item:input->points){
if(cond(item)){
result->add_point(item);
}
}
return result;
}
A typical use is:
auto filtered_points = filter(some_noisy_points);
However, it may be used in another way which is:
some_points = filter(some_points);
The above implementation works fine in this case. However, an unnecessary copy is done.
The question: What is the classical solution for this problem?
P.S. I need both cases to work fine where in the first one, the copy should happen (I have to accept the argument as const). In the second case, no copy should happen.
While overloading is OK, making two separated functions is not an option.
You could achieve your goal nicely by using a slightly different prototype:
void filter(const shared_ptr_of_points & input, shared_ptr_of_points & output)
If you are willing to use it, then a possible implementation will look like this:
// Example program
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <algorithm>
typedef std::shared_ptr<std::vector<int>> shared_ptr_of_points;
bool cond(int x){
return x > 100;
}
void filterCopy(const shared_ptr_of_points & input, shared_ptr_of_points & output){
if (!output){
output = std::make_shared<std::vector<int>>();
}
for(const auto item:*input){
if(cond(item)){
output->push_back(item);
}
}
}
void filterInplace(shared_ptr_of_points & inout){
inout->erase(std::remove_if(inout->begin(), inout->end(), [](int x){return !cond(x);}), inout->end());
}
void filter(const shared_ptr_of_points & input, shared_ptr_of_points & output){
if (output == input)
filterInplace(output);
else
filterCopy(input, output);
}
int main()
{
shared_ptr_of_points pts = std::make_shared<std::vector<int>>();
pts->emplace_back(100);
pts->emplace_back(200);
pts->emplace_back(300);
for(const auto item:*pts){
std::cout << item << std::endl;
}
std::cout << "**********" << std::endl;
shared_ptr_of_points resCopy;
filter(pts, resCopy);
for(const auto item:*resCopy){
std::cout << item << std::endl;
}
std::cout << "**********" << std::endl;
filter(pts, pts);
for(const auto item:*pts){
std::cout << item << std::endl;
}
}

passing primitive data type to a function in c++

I want to implement a function like this
double d = string_to("1223.23",double);
int i = string_to("1223",int);
bool d = string_to("1",bool);
How can I pass the bool, int, double data type to implement this in c++?
Types line int, double and bool can only be passed as template parameters.
You can use templates like this:
#include <string>
#include <sstream>
#include <iostream>
template<typename DataType>
DataType string_to(const std::string& s)
{
DataType d;
std::istringstream(s) >> d; // convert string to DataType
return d;
}
int main()
{
double d = string_to<double>("1223.23");
int i = string_to<int>("1223");
bool b = string_to<bool>("1");
std::cout << "d: " << d << '\n';
std::cout << "i: " << i << '\n';
std::cout << "b: " << b << '\n';
}
As an alternative you can pass your numeric types by reference and rely on function overloading to select the correct function:
void string_to(const std::string& s, double& d)
{
d = std::stod(s);
}
void string_to(const std::string& s, int& i)
{
i = std::stoi(s);
}
void string_to(const std::string& s, bool& b)
{
std::istringstream(s) >> std::boolalpha >> b;
}
int main()
{
double d;
int i;
bool b;
string_to("1223.23", d);
string_to("1223", i);
string_to("true", b);
std::cout << "d: " << d << '\n';
std::cout << "i: " << i << '\n';
std::cout << "b: " << b << '\n';
}
Also you could templatize the second method (an exercise for the reader).
If you really want to do this, you can pass the type by using the typeid operator.
E.g. double d = string_to("1223.23", typeid(double));
Using the library functions atoi, stod would make more sense.
If you're aiming to write more uniform code then you could write a Converter object and use method overloading to get automatic selection by type.
class Converter
{
public:
void fromString(double& value, const char* string);
void fromString(int& value, const char* string);
void fromString(long& value, const char* string);
};
Here's another way that uses tag dispatching. You can compile and run this example.
#include <iostream>
#include <string>
#include <cmath>
namespace detail {
// declare the concept of conversion from a string to something
template<class To>
To string_to(const std::string&);
// make some models of the concept
template<>
int string_to<int>(const std::string& s) {
return atoi(s.c_str());
}
template<>
double string_to<double>(const std::string& s) {
return atof(s.c_str());
}
template<>
std::string string_to<std::string>(const std::string& s) {
return s;
}
// ... add more models here
}
// define the general case of conversion from string with a model tag
// note the unused parameter allows provision of a model that is never used
// thus the model will in all likelihood be optimised away
template<class To>
To string_to(const std::string& from, const To& /* model_tag is unused */)
{
// dispatch to correct conversion function using the To type
// as a dispatch tag type
return detail::string_to<To>(from);
}
using namespace std;
int main()
{
// examples
int a = string_to("100", a);
double b = string_to("99.9", b);
const string s = string_to("Hello", s);
cout << s << " " << a << " " << b << endl;
return 0;
}
output:
Hello 100 99.9

Convert from pointer-to-member template function to functional type objects

I have this code that works already:
// mem_fun example
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <map>
using namespace std;
struct C
{
C(int i): y_(i) {};
int y_;
string op1(int x)
{
std::ostringstream oss;
oss << "operation 1: " << x+y_;
return oss.str();
}
string op2(string x)
{
std::ostringstream oss;
oss << "operation 2: " << x << "+" << y_;
return oss.str();
}
};
struct container: map<string, C>
{
// doesn't compile
// void safeOperation(string key, ??? bound_function_and_arg_object )
template< typename argType >
void safeOperation(string key, string (C::*mf)(argType a), argType a)
{
iterator it = find(key);
if (it != end())
{
C* pC = &(it->second);
cout << (pC->*mf)(a) << "\n";
}
else
{
cout << "key: " << key << " missing\n";
}
}
};
int main () {
container objects;
objects.insert(container::value_type("a1", C(1)));
objects.insert(container::value_type("b2", C(2)));
objects.insert(container::value_type("c3", C(3)));
objects.safeOperation("a1", &C::op1, 1);
objects.safeOperation("b2", &C::op1, 2);
objects.safeOperation("d4", &C::op1, 4);
objects.safeOperation("c3", &C::op2, string("3"));
return 0;
}
I'd like to change the template function on the map to use std::mem_fun and to bind the parameters together with the operation, rather than specify them as separate parameters to safeOperation.
In other words, I'd prefer to call safeOperation similar to this:
// wrong, but hopefully communicates what I'm trying to do:
objects.safeOperation(someKey, bind(&C::op1, 4));
The sample code is here: http://cpp.sh/74pgb
I'm probably missing something simple, but appreciate the help.
When you bind a member function, the first argument has to be an instance of the class whose member function it is. So what you want to do is generalize safeOperation to take any function that can be called on a C*:
template< typename F >
void safeOperation(string key, F func) {
iterator it = find(key);
if (it != end())
{
C* pC = &(it->second);
cout << func(pC) << "\n";
}
else
{
cout << "key: " << key << " missing\n";
}
}
And then generate your funcs by binding with the argument, but also leaving a placeholder:
using namespace std:;placeholders;
objects.safeOperation("a1", std::bind(&C::op1, _1, 1));
// ^^
// placeholder for pC
boost/std::bind create an object with an implementation-specific type. The only requirement is that the object is callable using operator().
To handle any functional objects you can change your function template in the following way:
template< typename F >
void safeOperation(string key, F f)
{
// ...
cout << f(pC) << "\n";
// ...
objects.safeOperation("someKey", bind(&C::op1, placeholders::_1, 4));
That should enable almost the syntax you require.

Dynamically creating a C++ function argument list at runtime

I'm trying to generate an argument list for a function call during runtime, but I can't think of a way to accomplish this in c++.
This is for a helper library I'm writing. I'm taking input data from the client over a network and using that data to make a call to a function pointer that the user has set previously. The function takes a string(of tokens, akin to printf), and a varying amount of arguments. What I need is a way to add more arguments depending on what data has been received from the client.
I'm storing the functions in a map of function pointers
typedef void (*varying_args_fp)(string,...);
map<string,varying_args_fp> func_map;
An example usage would be
void printall(string tokens, ...)
{
va_list a_list;
va_start(a_list, tokens);
for each(auto x in tokens)
{
if (x == 'i')
{
cout << "Int: " << va_arg(a_list, int) << ' ';
}
else if(x == 'c')
{
cout << "Char: " << va_arg(a_list, char) << ' ';
}
}
va_end(a_list);
}
func_map["printall"] = printall;
func_map["printall"]("iic",5,10,'x');
// prints "Int: 5 Int: 10 Char: x"
This works nicely when hardcoding the function call and it's arguments, but if I've received the data "CreateX 10 20", the program needs to be able to make the argument call itself. eg
// func_name = "CreateX", tokens = 'ii', first_arg = 10, second_arg = 20
func_map[func_name](tokens,first_arg,second_arg);
I can't predict how users are going to lay out the functions and code this beforehand.
If anyone has suggestions on accomplishing this task another way, feel free to suggest. I need the user to be able to "bind" a function to the library, and for the library to call it later after it has received data from a networked client, a callback in essence.
Here is a C++11 solution. It does not support varargs functions like printall or printf, this is impossible with this technique and IMO impossible at all, or at the very least extremely tricky. Such function are difficult to use safely in an environment like yours anyway, since any bad request from any client could crash the server, with absolutely no recourse whatsoever. You probably should move to container-based interface for better safety and stability.
On the other hand, this method supports all (?) other functions uniformly.
#include <vector>
#include <iostream>
#include <functional>
#include <stdexcept>
#include <string>
#include <boost/any.hpp>
template <typename Ret, typename... Args>
Ret callfunc (std::function<Ret(Args...)> func, std::vector<boost::any> anyargs);
template <typename Ret>
Ret callfunc (std::function<Ret()> func, std::vector<boost::any> anyargs)
{
if (anyargs.size() > 0)
throw std::runtime_error("oops, argument list too long");
return func();
}
template <typename Ret, typename Arg0, typename... Args>
Ret callfunc (std::function<Ret(Arg0, Args...)> func, std::vector<boost::any> anyargs)
{
if (anyargs.size() == 0)
throw std::runtime_error("oops, argument list too short");
Arg0 arg0 = boost::any_cast<Arg0>(anyargs[0]);
anyargs.erase(anyargs.begin());
std::function<Ret(Args... args)> lambda =
([=](Args... args) -> Ret {
return func(arg0, args...);
});
return callfunc (lambda, anyargs);
}
template <typename Ret, typename... Args>
std::function<boost::any(std::vector<boost::any>)> adaptfunc (Ret (*func)(Args...)) {
std::function<Ret(Args...)> stdfunc = func;
std::function<boost::any(std::vector<boost::any>)> result =
([=](std::vector<boost::any> anyargs) -> boost::any {
return boost::any(callfunc(stdfunc, anyargs));
});
return result;
}
Basically you call adaptfunc(your_function), where your_function is a function of any type (except varargs). In return you get an std::function object that accepts a vector of boost::any and returns a boost::any. You put this object in your func_map, or do whatever else you want with them.
Types of the arguments and their number are checked at the time of actual call.
Functions returning void are not supported out of the box, because boost::any<void> is not supported. This can be dealt with easily by wrapping the return type in a simple template and specializing for void. I've left it out for clarity.
Here's a test driver:
int func1 (int a)
{
std::cout << "func1(" << a << ") = ";
return 33;
}
int func2 (double a, std::string b)
{
std::cout << "func2(" << a << ",\"" << b << "\") = ";
return 7;
}
int func3 (std::string a, double b)
{
std::cout << "func3(" << a << ",\"" << b << "\") = ";
return 7;
}
int func4 (int a, int b)
{
std::cout << "func4(" << a << "," << b << ") = ";
return a+b;
}
int main ()
{
std::vector<std::function<boost::any(std::vector<boost::any>)>> fcs = {
adaptfunc(func1), adaptfunc(func2), adaptfunc(func3), adaptfunc(func4) };
std::vector<std::vector<boost::any>> args =
{{777}, {66.6, std::string("yeah right")}, {std::string("whatever"), 0.123}, {3, 2}};
// correct calls will succeed
for (int i = 0; i < fcs.size(); ++i)
std::cout << boost::any_cast<int>(fcs[i](args[i])) << std::endl;
// incorrect calls will throw
for (int i = 0; i < fcs.size(); ++i)
try {
std::cout << boost::any_cast<int>(fcs[i](args[fcs.size()-1-i])) << std::endl;
} catch (std::exception& e) {
std::cout << "Could not call, got exception: " << e.what() << std::endl;
}
}
As already mentioned by #TonyTheLion, you can use boost::variant or boost::any to select between types at runtime:
typedef std::function<void(const std::string&, const std::vector<boost::variant<char, int>>&)> varying_args_fn;
std::map<std::string, varying_args_fn> func_map;
The you can e.g. use a static visitor to distinguish between the types. Here is a full example, note that the tokens parameter is actually no longer necessary since boost::variant knows at runtime what type is stored in it:
#include <map>
#include <vector>
#include <string>
#include <functional>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/any.hpp>
typedef std::function<void(const std::string&, const std::vector<boost::variant<char, int>>&)> varying_args_fn;
void printall(const std::string& tokens, const std::vector<boost::variant<char, int>>& args) {
for (const auto& x : args) {
struct : boost::static_visitor<> {
void operator()(int i) {
std::cout << "Int: " << i << ' ';
}
void operator()(char c) {
std::cout << "Char: " << c << ' ';
}
} visitor;
boost::apply_visitor(visitor, x);
}
}
int main() {
std::map<std::string, varying_args_fn> func_map;
func_map["printall"] = printall;
func_map["printall"]("iic", {5, 10, 'x'});
}