如何在C ++ 11中创建指向静态成员函数的指针映射? [英] How to create map of pointers to static member functions in C++11?

查看:72
本文介绍了如何在C ++ 11中创建指向静态成员函数的指针映射?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个包含一些静态成员函数的类,以及一个指向这些函数的指针的映射。但是,它们可以采用不同数量和类型的参数。因此,在此线程之后,我尝试了以下操作: / p>

I want to create a class that contains some static member functions and a map with pointers to these functions. They can, however, take different numbers and types of arguments. So following this thread I tried something like:

class BeliefCondFunc
{
    static std::unordered_map<std::string, std::function<bool()>> FuncMap;

    static bool Greater(int A, int B)
    {
        return A > B;
    }

    static bool Between(float A, float B, float C)
    {
        return A > B && A < C;
    }

    static void InitMap()
    {
        FunctionMap["Greater"] = std::bind(&BeliefCondFunc::Greater, ???);
        FunctionMap["Between"] = std::bind(&BeliefCondFunc::Between, ???);
    }
};

现在,我需要替换 ??? 在上面的代码中使其起作用?最后,我希望能够这样称呼它:

Now, what do I need to replace the ??? with in the code above to make it work? In the end I want to be able to call it like this:

BeliefCondFunc::FuncMap["Greater"](1, 2);
BeliefCondFunc::FuncMap["Between"](1.0f, 2.0f, 3.0f);

在C ++ 11中,甚至不使用boost这样的外部库,这是否有可能? / p>

Is this even possible in C++11, without the use of an external library like boost?

推荐答案

您需要使用占位符替换 ??? 来转发

You need to replace the ??? with placeholders, to forward the arguments of the closure to the function.

std::bind(&BeliefCondFunc::Greater, _1, _2);

但是您应该注意,您的映射包含不接受任何参数的函数对象。因此,您可能会遇到呼叫不匹配错误。从C ++ 14开始,甚至可能是尝试将其分配给新构造的值。

You should note however, that your map holds function objects that accept no arguments. So you'll likely get a call mismatch error. Since C++14, probably even on the attempted assignment into the newly constructed value.

我也觉得您使解决方案过于复杂。对于静态成员函数,简单的函数指针类型就足够了。然后,您需要执行一些强制转换,但是可以将其封装在另一个模板化静态成员函数中。现在,强制转换函数指针类型不是很安全(请考虑一下当您弄错数字参数时会发生什么)。但是您似乎已经打算避开类型系统,所以这里是:

I also feel you over complicate the solution. For static member functions, a simple function pointer type can suffice. You'd then need to preform some casting, however that can be encapsulated in another templated static member function. Now, casting function pointer types isn't very safe (think about what happens when you get the number arguments wrong). But you seem intent on sidestepping the type system already, so here it is:

#include <unordered_map>
#include <string>
#include <utility>

struct BeliefCondFunc
{
    using cb_type = bool(*)();
    static std::unordered_map<std::string, cb_type> const FuncMap;

    static bool Greater(int A, int B)
    {
        return A > B;
    }

    static bool Between(float A, float B, float C)
    {
        return A > B && A < C;
    }

    template<typename ...Args>
    static bool call(std::string const& key, Args&&... args){
      using prototype = bool(*)(Args...);
      return reinterpret_cast<prototype>(FuncMap.at(key))(std::forward<Args>(args)...);
    };
};

std::unordered_map<std::string, BeliefCondFunc::cb_type> const BeliefCondFunc::FuncMap {
  {"Greater", reinterpret_cast<cb_type>(&BeliefCondFunc::Greater) },
  {"Between", reinterpret_cast<cb_type>(&BeliefCondFunc::Between) }
};

int main() {
    BeliefCondFunc::call("Greater", 1, 2);
    return 0;
}

实时查看

如果转换令您担心,则可以存储该功能指针原型的 std :: type_info 指针。然后,将两者进行比较,然后在运行时因不匹配而引发异常,这很简单。像这样:

If the casting worries you, you can store the function pointers prototype's std::type_info pointer alongside with it. Then it's a simple matter of comparing the two and throwing an exception upon a mismatch at run time. Like so:

#include <unordered_map>
#include <string>
#include <utility>
#include <typeinfo>
#include <stdexcept>
#include <iostream>

struct BeliefCondFunc
{
    using cb_type = bool(*)();
    using map_val = std::pair<cb_type, std::type_info const*>;
    static std::unordered_map<std::string, map_val> const FuncMap;

    static bool Greater(int A, int B)
    {
        return A > B;
    }

    static bool Between(float A, float B, float C)
    {
        return A > B && A < C;
    }

    template<typename ...Args>
    static map_val make_map_val(bool (*func)(Args...)) {

        return std::make_pair(reinterpret_cast<cb_type>(func), 
                              &typeid(decltype(func)));
    }

    template<typename ...Args>
    static bool call(std::string const& key, Args&&... args){
      using prototype = bool(*)(Args...);

      auto& func_n_typeid = FuncMap.at(key);

      if (typeid(prototype) != *func_n_typeid.second )
        throw std::domain_error("Prototype mismatch");

      return reinterpret_cast<prototype>(func_n_typeid.first)(std::forward<Args>(args)...);
    };
};

std::unordered_map<std::string, BeliefCondFunc::map_val> const BeliefCondFunc::FuncMap {
  {"Greater", make_map_val(&BeliefCondFunc::Greater) },
  {"Between", make_map_val(&BeliefCondFunc::Between) }
};

int main() {
    BeliefCondFunc::call("Greater", 1, 2);

    try {
        BeliefCondFunc::call("Lesser", 1, 2);
    } catch (std::out_of_range&) {
        std::cout << "No such function\n";
    }

    try {
        BeliefCondFunc::call("Between", 1, 2);
    } catch (std::domain_error&) {
        std::cout << "Wrong number of arguments\n";
    }

    return 0;
}

再次,实时查看

如所承诺的,一个解释:

And as promised, an explanation:


  1. 以下两行创建了两个类型别名(c ++ 11语法,等同于typedef,但显然将新标识符与类型分开)。我命名为虚拟回调类型,将其存储为所有指针,并将其存储在映射中的一对值。

  1. The following two lines create two type aliases (c++11 syntax, equivalent to a typedef, but clearly separates the new identifier from the type). I name the dummy callback type I'll store all pointers as, and the pair of values I'll store in the map.

using cb_type = bool(*)();
using map_val = std::pair<cb_type, std::type_info const*>;


  • make_map_val 是模板函数可以传递返回 bool 的任何函数指针。每当我使用函数指针调用它时,它都会使用模板参数推论来计算排除函数的参数类型。我不是直接使用参数类型,而是依靠 decltype 来获取函数的类型。回想起来,我本可以将函数指针直接传递给 typeid ,因为它会以相同的方式推导类型。在这里使用模板函数的重要性在于,无需询问 make_map_val 的调用位置,就可以自动捕获有关函数指针的静态类型数据。

  • make_map_val is a template function that can be passed any function pointer which returns a bool. Whenever I call it with a function pointer, it uses template argument deduction to figure out the argument types of the function. I don't the argument types directly, but instead rely on decltype to get the type of the function. In retrospect, I could have just passed the function pointer directly to typeid, since it would have deduced the type the same way. The importance of using a tempalted function here, is to capture as much static type data about the function pointer automatically, without asking the call site of make_map_val for it.

    调用再次使用模板参数类型推导来找出我们要调用的函数的参数类型。类型别名还用于方便地引用我们最终将转换为的函数指针类型,以及 typeid 的参数。别名和函数调用本身是使用参数数据包扩展编写的。参数通过转发引用传递给函数,然后通过调用 std :: forward ,全部用于保留参数的值类别。如果您所有的函数仅在小的廉价复制值上运行,则可能无法按值传递所有内容。

    call again uses template argument type deduction to figure out the types of parameters for the function we want to call. A type alias is also used to conveniently reference the function pointer type we will ultimately cast to, as well as the parameter for typeid. The alias and function call itself are written using parameter pack expansion. The parameters are passed to the function with a forwarding reference, and onward to the function pointer via a call to std::forward, all to preserve the value category of the parameter. If all your functions operate only on small, cheap to copy values, you could probably get away with passing everything by value.

    这篇关于如何在C ++ 11中创建指向静态成员函数的指针映射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆