如何在容器中存储具有不同签名的功能对象
How to store functional objects with different signatures in a container?
假设我们有两个函数(void : ( void ) )
和(std::string : (int, std::string))
,我们可以有10个以上。所有(或其中一些)接受不同的参数类型,并且可以返回不同的类型。我们希望将它们存储在std::map
中,所以我们得到这样的API:
//Having a functions like:
int hello_world(std::string name, const int & number )
{
name += "!";
std::cout << "Hello, " << name << std::endl;
return number;
}
//and
void i_do_shadowed_stuff()
{
return;
}
//We want to be capable to create a map (or some type with similar API) that would hold our functional objects. like so:
myMap.insert(std::pair<std::string, fun_object>("my_method_hello", hello_world) )
myMap.insert(std::pair<std::string, fun_object>("my_void_method", i_do_shadowed_stuff) )
//And we could call tham with params if needed:
int a = myMap["my_method_hello"]("Tim", 25);
myMap["my_void_method"];
我想知道如何将许多不同的函数放入同一个容器中。具体来说,如何在c++ 03中使用Boost.
API应该独立于实际的函数类型(有int a = myMap["my_method_hello"]("Tim", 25);
而不是int a = myMap<int, (std::string, int)>["my_method_hello"]("Tim", 25);
)
#include <functional>
#include <iostream>
#include <string>
#include <map>
class api {
// maps containing the different function pointers
typedef void(*voidfuncptr)();
typedef int(*stringcrintptr)(std::string, const int&);
std::map<std::string, voidfuncptr> voida;
std::map<std::string, stringcrintptr> stringcrint;
public:
// api temp class
// given an api and a name, it converts to a function pointer
// depending on parameters used
class apitemp {
const std::string n;
const api* p;
public:
apitemp(const std::string& name, const api* parent)
: n(name), p(parent) {}
operator voidfuncptr()
{ return p->voida.find(n)->second; }
operator stringcrintptr()
{ return p->stringcrint.find(n)->second; }
};
// insertion of new functions into appropriate maps
void insert(const std::string& name, voidfuncptr ptr)
{ voida[name]=ptr; }
void insert(const std::string& name, stringcrintptr ptr)
{ stringcrint[name]=ptr; }
// operator[] for the name gets halfway to the right function
apitemp operator[](std::string n) const
{ return apitemp(n, this); }
};
用法:
api myMap;
int hello_world(std::string name, const int & number )
{
name += "!";
std::cout << "Hello, " << name << std::endl;
return number;
}
int main()
{
myMap.insert("my_method_hello", &hello_world );
int a = myMap["my_method_hello"]("Tim", 25);
}
不太好看。更好的建议是不要做任何你想做的事情,哪怕是一点点。
请注意,这要求具有相同形参的所有函数返回相同的类型。
你可以使用boost::任何…
#include <boost/any.hpp>
#include <iostream>
#include <map>
#include <string>
void voidFunc()
{
std::cout << "void called" << std::endl;
}
void stringFunc(std::string str)
{
std::cout << str << std::endl;
}
int main()
{
std::map<std::string, boost::any> funcs;
funcs.insert(std::pair<std::string, boost::any>("voidFunc", &voidFunc));
funcs.insert(std::pair<std::string, boost::any>("stringFunc", &stringFunc));
boost::any_cast<void(*)(void)>(funcs["voidFunc"])();
boost::any_cast<void(*)(std::string)>(funcs["stringFunc"])("hello");
return 0;
}
注意,如果你没有在any_cast中正确指定函数签名,你将得到一个运行时异常。
完整的示例在这里和这里。
可以像boost::any那样使用类型擦除。但是由于我们只想保存函数,我们可以想出更方便的用法。
我们首先需要的是函数签名演绎。看到这里。
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template<typename R, typename ...Args>
struct function_traits<std::function<R(Args...)>>
{
typedef R result_type;
typedef typename std::function<R(Args...)> type;
typedef typename std::function<void(Args...)> typeNoRet;
};
template<typename R, typename ...Args>
struct function_traits<R(*)(Args...)>
{
typedef R result_type;
typedef typename std::function<R(Args...)> type;
typedef typename std::function<void(Args...)> typeNoRet;
};
template<typename R, typename cls, typename ...Args>
struct function_traits<R(cls::*)(Args...)>
{
typedef R result_type;
typedef typename std::function<R(Args...)> type;
typedef typename std::function<void(Args...)> typeNoRet;
};
要实现类型擦除,我们需要非模板的基类和派生模板类。FunctionHolder将存储基类指针。构造函数将使用Function_traits来确定正确的派生类类型。
class FunctionHolder {
private:
struct BaseHolder {
BaseHolder() {}
virtual ~BaseHolder() {}
};
template <typename T>
struct Holder : public BaseHolder {
Holder(T arg) : mFptr(arg) {}
template<typename... Args>
void Call(Args&&...args) {
mFptr(std::forward<Args>(args)...);
}
template<typename R, typename... Args>
R CallRet(Args&&...args) {
return mFptr(std::forward<Args>(args)...);
}
T mFptr;
};
public:
template<typename T>
FunctionHolder(T t) : mBaseHolder(new Holder<typename function_traits<T>::type>(t))
, mBaseHolderNoRet(new Holder<typename function_traits<T>::typeNoRet>(t)) {}
template<typename T, typename...Args>
FunctionHolder(T&& t, Args&&... args) : mBaseHolder(new Holder<typename function_traits<T>::type>
(std::bind(std::forward<T>(t), std::forward<Args>(args)...)))
, mBaseHolderNoRet(new Holder<typename function_traits<T>::typeNoRet>
(std::bind(std::forward<T>(t), std::forward<Args>(args)...))) {}
void operator()() {
this->operator()<>();
}
template<typename... Args>
void operator()(Args&&... args) {
auto f = dynamic_cast<Holder<std::function < void(Args...) > >*>(mBaseHolderNoRet.get());
if (f) {
f->Call(std::forward<Args>(args)...);
return;
}
throw std::invalid_argument("");
}
template<typename R, typename... Args>
R call(Args&&... args) {
auto f = dynamic_cast<Holder<std::function<R(Args...)>>*>(mBaseHolder.get());
if (f) {
return f->template CallRet<R>(std::forward<Args>(args)...);
}
throw std::invalid_argument("");
}
private:
std::unique_ptr<BaseHolder> mBaseHolder;
std::unique_ptr<BaseHolder> mBaseHolderNoRet;
};
在这种情况下,FunctionHolder存储了BaseHolder的2个指针,第一个具有正确的签名,第二个返回void。为了避免开销,如果你打算总是指定返回值或从不使用它,你可以删除其中一个。
最后我们可以这样使用。
struct st0 {
st0(int x) : mX(x) {}
std::string print(int p) {
std::cout << "st0::print "
<< mX << " " << p << std::endl;
return "ret_from_st0::print";
}
int mX;
};
struct st1 {
st1(int x) : mX(x) {}
void operator()() {
std::cout << "st1::operator() "
<< mX << " " << std::endl;
}
int mX;
};
void Func0(int a, int b) {
std::cout << "Func0. "
<< " a: " << a
<< " b: " << b << std::endl;
}
void Func1(int a, int b, std::string str) {
std::cout << "Func0. "
<< " a: " << a
<< " b: " << b
<< " str: " << str << std::endl;
}
uint64_t Func2(int a, int b, std::string str) {
std::cout << "Func0. "
<< " a: " << a
<< " b: " << b
<< " str: " << str << std::endl;
return 0xBAB0CAFE;
}
int main() {
try {
// void(int, int)
FunctionHolder ex1(&Func0);
ex1(1,12);
// void(int, int, std::string)
FunctionHolder ex2(&Func1);
ex2(1, 12, std::string("Some text here"));
// int(int, int, std::string)
// call and print return value
FunctionHolder ex3(&Func2);
std::cout << "Ret: " << std::hex << ex3.call<uint64_t>(123, 3211, std::string("another text"))
<< std::dec << std::endl;
// call and drop return value
ex3(123, 3211, std::string("another text"));
// Hold std::function<void(int)>
std::function<void(int)> ex4 = std::bind(&Func0, 1, std::placeholders::_1);
FunctionHolder c(std::function<void(int)>(std::bind(&Func0, 1, std::placeholders::_1)));
ex4(12);
// will bind to st0 member function print
st0 st0object(8955);
FunctionHolder ex5(&st0::print, st0object, std::placeholders::_1);
ex5(2222);
// call and print return value
std::cout << "Ret: " << ex5.call<std::string>(7531) << std::endl;
// wrap lambda function with std::function and pass to holder
FunctionHolder ex6(std::function<void(void)>([]() {std::cout << "lambda function called" << std::endl;}));
ex6();
// functor object st1
FunctionHolder ex7(st1(123654));
ex7();
// Will throw, because st1::operator() gets no arguments
ex7(123);
} catch (std::invalid_argument &e) {
std::cout << "Invalid argument(s) were passed" << std::endl;
// error handling here...
}
return 0;
}
FunctionHolder可以存储C类函数指针、函数对象、lambda函数和成员函数指针。只有两个例外需要记住。
- 如果你打算使用std::bind将对象指针绑定到成员函数,你可以将所有参数传递给FunctionHolder构造函数,它将为你做绑定。如果你想绑定任何其他函数参数,你必须手动绑定它,构造std::function并传递给FunctionHolder。
FunctionHolder fh(std::function<void(std::string)>(std::bind(&Func1, 888, 333, std::placeholders::_1)));
fh(std::string("Ok."));
- 要存储lambda函数,首先用std::函数包装它们,如上例所示。
如果传递错误的参数,FunctionHolder将抛出std::invalid_argument。
问题是,当你调用你的函数时,你已经知道它们的类型了。
如果我们这样做
int x = map["key"](1, "2")
我们已经可以推断出存储在"key"中的函数的类型是(int (*)(int, char*))
所以我们还不如像
int x = map_of_int_and_string_to_int["key"](1, "2");
并避免合并所有密钥的麻烦…虽然c++确实有一些重载特性,正是为了这类东西,但我真的不明白为什么你要在这种特殊情况下费心。
最后,为什么要把所有这些函数放在同一个映射中呢?它们不共享任何类似的接口,所以你不能统一地访问它们,你不能迭代它们,你不能不透明地将它们传递给其他人。如果没有任何共同之处,您就无法安全地对这个假想映射中的函数进行操作。
可以通过将函数指针转换为void指针并返回来实现。您应该在运行时知道签名,因此硬连接强制转换操作符不是问题。然而,我不明白这样做的逻辑。这一点意义都没有,至少在c++中是这样。使用模板类/函数或函数指针的结构更有意义。
,例如:
template <typename X> foo(X param1) { /* do something with param1*/};
template <typename X, typename Y> foo(X param1, Y param2)
{/* do something with 2 params*/};
template <int X> foo(X param1) { /* only one parameter, which is int */};
:
foo(5); // calls the third one
foo("5"); // calls the first one
foo("5", 5); // calls the second one.
谁需要地图?
- 将带有派生模板的对象传递给接受具有基本模板的对象的功能
- 对象内部对象的重载功能
- 基于不同字段的对象向量的排序功能
- 具有小对象优化功能的智能指针
- 如果在创建对象时创建了 VPTR,那么为什么具有虚拟功能的类的大小在 32 位系统上为 4,在 64 位机器上为 8
- 如何改进我的"String"对象比较功能
- 如何存储指针以功能模板,该模板将可呼叫对象作为其参数之一
- GPU设备函数如何访问主机功能中定义的对象
- 如何将对象用作C 功能的参数
- 如何将指针分配给功能中的新对象,而不会使该对象在退出后消失
- 将对象传递到类,没有接收匹配功能错误
- 是否可以在辅助功能中概括对象类型
- 使用for_each从对象列表中调用打印功能
- 如何创建功能以用受保护或私人侵犯的对象在对象内部操纵对象
- 实现功能对象绑定而无需使用C 11
- 在功能中设置对象变量
- 成员功能在面向对象的框架中返回std :: vector.size()的效率
- 如何使程序使用功能过载用于派生的类对象
- 明智的选择是更喜欢lambdas功能对象
- 在C 中使用DLSYM加载共享对象功能