2017-02-09 655 views
2

我想在std::map中存储和识别std::function对象。 确定我想使用std::function::target。 如果我使用std::bind绑定到类的成员函数,我无法从std::function::target得到指针。C++ 11获取std :: function的指针

#include <functional> 
#include <iostream> 


using namespace std; 
void normal_fun(int a) 
{ 
    std::cout << "hello\n"<<a; 
} 

class testclass 
{ 
public: 
    int b; 
    void mytest(int a) 
    { 
     b = a; 
    } 
}; 

uintptr_t getFuncAddress(std::function<void(int)> &f) 
{ 
    uintptr_t returnAddress = reinterpret_cast<uintptr_t>(f.target<void(*)(int)>()); 
    return returnAddress; 
} 

int main() 
{ 

    testclass c; 
    std::function<void(int)> f1 = bind(&testclass::mytest,&c, placeholders::_1); 
    std::function<void(int)> f2 = normal_fun; 
    auto addrF1 = getFuncAddress(f1); 
    auto addrF2 = getFuncAddress(f2); 
} 

我该如何实现自己想做的事?

+1

你不会在'std :: function'中存储实际的'void(int)',所以'target'返回一个'nullptr'。您存储绑定的结果,这是一些未知的函子类型。 – StoryTeller

+0

这就是我所害怕的...你知道我怎么写'target'来获得绑定的指针? – bdahl

+1

如果您需要这样做,我认为您正在接触错误的工具。 'std :: function'是关于类型擦除。试图通过类型来获取确切的目标有点像用'dynamic_cast's乱丢你的代码,而不是依赖于多态性。 – StoryTeller

回答

0

我想存储和识别std :: map中的std :: function对象。

我假设,你想识别std::function对象(例如,要选择调用或删除它们)。 在这种情况下,我使用了一个附加的键,例如一个简单的unsigned。 全局函数可以使用:

typedef unsigned Key; 

Key genId() 
{ 
    static Key id = 0; 
    return ++id; 
} 

现在,std::function对象可以用此键配对。该API可以授予对配对std::function对象的访问可以专门使用该密钥完成。

+0

问题在于,包含相同目标的两个'std :: function'对象将具有不同的键,它们可能不符合OP的需求。 –

+0

@JonathanWakely是的,我期待这个答案。我们使用受sigC++启发的信号插槽概念。我们试图通过“平衡”访问和跟踪连接来防止这种情况。最后,两个相同的目标是允许的,并且可能(在极少数情况下)是一个功能而不是bug。 – Scheff

+0

关于此主题我认为这是将随机密钥放入包含我的成员函数的类中的最佳方式。 – bdahl

1

A std::function不是函数指针。它甚至不是指针。

如果您知道std::function中存储的是什么类型,您可以获取指向其存储内容的指针。请注意,这里的指针不是函数指针,而是指向函数指针的指针。

如果你不知道它存储的是什么类型,你不能。

如果你想<==hash(etc),std::function不提供此为你。它键入删除(),复制,移动,销毁,typeid和投回到原始。

您可以使用类型擦除技术来增加std::function这些操作;注意二进制操作的类型擦除比一般的擦除类型更容易触摸。

删除某些东西的类型不应该是您在解决问题时的第一步,但它可以解决您的问题。有关C++的SO文档中的类型擦除的文章。这不是初学者主题。

赔率是你可以用更简单的方法解决你的潜在问题。

在任何情况下,使用从target返回的类型进行排序并不是一个好主意,因为它是指向可能自动存储对象或可能堆存储对象的指针;其中两项将在无害操作(如移动)下具有明显不同的失效规则。一个SBO(小缓冲区优化)目标将随着std::function的实例而移动,而堆分配的一个可能会停留在std::function类似于std::move类似操作的状态。这真是一团糟。

0
f.target<void(*)(int)> 

是错误的,因为通过绑定返回的类型不是void(*)(int),使目标绝不会返回一个非空指针。绑定不返回一个函数指针。它返回一些实现指定类型的对象。

该类型可以通过decltype获得。这是一个有点乖张,但应该是正确的:

f.target<decltype(bind(
    &testclass::mytest, 
    (testclass*)nullptr, 
    placeholders::_1) 
)> 

,但我有没有在getFuncAddress知道如果我有识别TestClass或myOtherClass或别人类的问题

你如果知道包装对象的类型,则只能使用std::function::target。如果选项数量有限,那么您可以尝试使用每种类型获取目标,直到返回非null值。对于任意未知类型,std::function::target不能用于您。


我想存储在一个std确定的std ::函数对象::地图

我认为你将不得不使用一些外部密钥,不能从std::function提取。这意味着如果你想防止重复,你还需要一个外部方法来保证从键到函数的唯一映射。

+0

好的,这是好的,但我有问题,我不知道'getFuncAddress'如果我有'testclass'或'myOtherClass'或其他类。我知道只有成员函数的签名是'void(int)'... – bdahl

+0

@bdahl请参阅编辑 – user2079303

1

std::function的要点是为符合给定签名的可调用对象提供统一的接口和类型。你期待它也提供了一个统一的排序键,但它不这样做。没有办法对std::bind返回的函数指针和任意函数对象和可调用对象进行排序。它们是完全不同的东西,比较它们是没有意义的。所有std::function允许你做的是存储他们并给他们打电话。

如果你需要一个排序机制,你将不得不自己发明一些东西,你不会从std::function得到它。

+2

“*如果你需要一个排序机制,你将不得不自己创造一些东西,你不会从'std :: function'中得到它。*”这就像把马车放在马前面一样。如果你需要“发明”一些机制来排序你的对象,你可能不需要首先对它们进行排序。另一方面,如果你需要对它们进行排序,那么你就应该知道你正试图解决的问题的排序条件,并且你可以使用这些信息来产生散列或排序机制。 – Pixelchemist

+0

@Pixelchemist是伟大的一点。我认为关键在于你是否“需要”它,如你所说,如果你不能描述一个有意义的关键词来排序,你可能不需要它。 –