Reputation: 49
I want to know, if it is possible to build a name to function table in c++, something like map<string, function handle>
. But
those functions have different signatures. I can assume they have same return type as void
.
I was thinking define something like,
struct ftable
{
std::string name;
void (void* pfun)(); // this is only for one function signature
};
But how to make it work for different types of function?
I asked a related question here. In that question, I try to store functions into some container, but I realize that I can not store function with placeholder (see following code). Is this possible with c++? Thanks!
template <typename F, typename ... Args>
std::function<void()> MapFun (F const & f, Args const & ... args)
{ return [=]{ f(args...); }; }
void userFun1 (int i1, int i2)
{ std::cout << "uf1, " << i1 << ", " << i2 << std::endl; }
int main ()
{
auto l1 = MapFun(userFun1, 1, 2);
std::unordered_map<std::string, std::function<void()>> func_map;
func_map["userFun1"] = std::bind(l1); // this is okay;
//func_map["userFun1"] = std::bind(userFun1, std::placeholders::_1, std::placeholders::_2); // this is wrong;
//auto lll1 = MapFun(userFun1, std::placeholders::_1,std::placeholders::_2); // also wrong.
}
UPDATE: What I want is like plugin development. I have a server and a client. I can write my function on client, and build it as a shared library, then send that shared library to server as well as a signal telling server to load it. I hope server program can load function with different signatures from shared library without restart or rebuild. Is this possible, or I have to fix the signature and restrict all shared functions with that?
Upvotes: 1
Views: 864
Reputation: 620
It is possible but at some point you will need to know the return and parameter types.
You can use a type-erasure class to hide the return/parameter types and then store the type-erasure class. A naive implementation like this would suffice,
#include <iostream>
#include <functional>
#include <unordered_map>
class MyLambda {
public:
MyLambda() = default;
virtual ~MyLambda() = default;
};
template <typename T>
class HiddenLambda : public MyLambda {
static_assert(std::integral_constant<T, false>::value, "Template parameter needs to be of function type.");
};
template <typename Ret, typename... Args>
class HiddenLambda<Ret(Args...)> : public MyLambda {
public:
HiddenLambda(std::function<Ret(Args...)> _fun) : fun_(_fun) { }
Ret operator() (Args... args) {return fun_(args...);}
private:
std::function<Ret(Args...)> fun_;
};
int main() {
std::unordered_map<std::string, std::shared_ptr<MyLambda>> my_lambdas;
my_lambdas.insert(std::make_pair("fun1", std::shared_ptr<MyLambda>(
new HiddenLambda<size_t(std::string)>(
[](std::string s) { return s.size(); } // <- lambda you want to store
)
)
));
my_lambdas.insert(std::make_pair("fun2", std::shared_ptr<MyLambda>(
new HiddenLambda<int(int)>(
[](int x) { return x * 5; } // <- lambda you want to store
)
)
));
auto it = my_lambdas.find("fun1");
/* Get the function we want! */
std::shared_ptr<MyLambda> a_lam = it->second;
/* Need to know the types to actually use it though */
HiddenLambda<size_t(std::string)>& actual_lam = dynamic_cast<HiddenLambda<size_t(std::string)>&>(*a_lam);
std::cout << actual_lam("how long is this string?") << "\n";
}
If this is what we really need to do, I suggest looking up various methods for type erasure.
I think the problem you are trying to solve probably has an easier solution. If you could give more details perhaps we could help?
EDIT
More relevant example to your provided code...
/* include above classes and includes */
void myfunc(int x, int y) {
std::cout << "uf1, " << x << ", " << y << std::endl;
}
int main() {
std::unordered_map<std::string, std::shared_ptr<MyLambda>> func_map;
func_map["userFun1"] = std::shared_ptr<MyLambda>(
new HiddenLambda<void(int, int)>( &myfunc ) // <- no args binded, notice the type = void(int,int)
);
func_map["userFun2"] = std::shared_ptr<MyLambda>(
new HiddenLambda<void(int)>( std::bind(myfunc, std::placeholders::_1, 5) ) // <- one args binded, notice the type = void(int)
);
func_map["userFun3"] = std::shared_ptr<MyLambda>(
new HiddenLambda<void()>( std::bind(myfunc, 1, 2)) // <- two args binded, notice the type = void()
);
/* we still need to know the type though,it will be either void(int, int), void(int) or void() */
HiddenLambda<void(int)>& actual_lam = dynamic_cast<HiddenLambda<void(int)>&>(*func_map["userFun2"]);
actual_lam(4);
}
EDIT V2
This is more of a guess then anything. I am not sure if you should do this (well definitely not outside of some interesting experimentation) or if it will even work. Here is a possible way if the amount different arguments for different functions is known and finite. This would require a technique called Library Interposing, which I do not know much about.
Firstly in the main program you define this enum and a factory function. The enum will describe every possible parameter range.
enum Types { kVOID, kINT_INT }; // <- verbosely define all the possible ones you would use
std::pair<Types, std::shared_ptr<MyLambda>> Factory(){
return
{
kVOID, /* <- some sensible default */
std::shared_ptr<MyLambda>(
new HiddenLambda<void()>( []{} )
)
};
}
The shared library will have to provide an overriden factory method. Here is where I think you need the interposer to do so.
std::pair<Types, std::shared_ptr<MyLambda>> Factory(){
return
{
kVOID_INT_INT,
std::shared_ptr<MyLambda>(
new HiddenLambda<void(int, int)>( [](int x, int y){ std::cout << (x + y);} )
)
};
}
Then the main method would look like:
int main() {
std::unordered_map<std::string, std::pair<Types, std::shared_ptr<MyLambda>>> func_map;
func_map.insert({"fun1", Factory()});
auto it = func_map.find("fun1");
/* always need to be able to deduce they type */
if (it->second.first == kVOID) {
CallHidden(*it->second.second);
}
else if (it->second.first == kINT_INT) {
CallHidden<int, int>(*it->second.second, 3, 4);
} else {
/* other ones you have statically typed */
}
}
Upvotes: 1
Reputation: 3277
If at time of creating object of std::function<void()>
, you know function signature then you may be able to do like below.
#include <iostream>
#include <functional>
#include <unordered_map>
void userFun1 (int i1, int i2)
{
std::cout << "uf1, " << i1 << ", " << i2 << std::endl;
}
int main()
{
std::unordered_map<std::string, std::function<void()>> func_map;
func_map["userFun1"] = std::bind(userFun1,1,2);
func_map["userFun1"]();
int i1{2},i2{3};
func_map["userFun1.1"] = std::bind(userFun1,std::ref(i1),std::ref(i2));
func_map["userFun1.1"]();
i1 = 3;
i2 = 4;
func_map["userFun1.1"]();
return 0;
}
Upvotes: 0