如何使用std::字符串从对象中调用函数

How to call a function from an object with a std::string

本文关键字:调用 函数 对象 字符串 何使用 std      更新时间:2023-10-16

这是我的问题,我想调用我的一个对象的getters/ssetter,但不是直接调用,我想通过使用std::字符串来实现。

我发现了这个,但它对我的情况不起作用,我想这是因为我的函数不是在我的主方法中定义的,而是在我的平方类中定义的。此外,我的函数的定义方式也不完全相同——有void(std::string(std::string((void(int(。。。

这是a想做什么的一个例子。

我的目标正方形

#include <map>
#include <functional>
#include <string>
class Square{
    private:
        std::string name;
        int width;
        float happinessPoint; //extremly important for your square.
    public:
        void setName(std::string);
        void setWidth(int);
        void setHappinessPoint(float);
        std::string getName()
        int getWidth()
        float getHappinnessPoint()
}

和我的主要

#include "Square.h/cpp"
int main(){
    Square square = Square("Roger",2,3.5);
    // here in my  magicalFunction I ask to the users the new values for my square (all in std::string for now)        
    vector <std::string> newValueForSquare = magicalFunction();
    for (unsigned int i=0; i < newValueForSquare.size(), i++){
        //here I have a function which tell me if my std::string
        // is in fact a float or an int            
        // and I would like to call each of my setters one by one to
        // sets my Square to some value I asked to the user before all that.
        // something like that:
        // someFunction("setName","Henry")

}}

我希望我已经清楚了,很难解释一些你不知道该怎么做的事情。如果你想让我更具体地告诉我,我会尽我所能。

编辑:例如,我想用str::字符串调用我的square.setName((,而不在main中写入这个square.setRame。

要根据字符串调用函数,您有一些选择。在我列出选择之前,请在网上搜索"C++工厂设计模式"。

  • If else梯子
  • 查阅表格
  • 映射/关联阵列
  • 哈希表

可能还有其他方法,但以上的方法会浮现在脑海中。

if else梯形图(又名switch(

此方法的问题在于switch语句既不能处理字符串也不能处理文本。因此,您必须满足if语句:

if (string == "Roger")
{
  Process_Roger();
}
else if (string == "Felicity")
{
  Process_Felicity();
}
else
{
  Display_Error_Message();
}

每当需要添加新字符串时,都必须向梯形图中添加另一个"else-if"语句。你不仅要更改代码,还要重新测试

查阅表格

您需要了解函数指针,了解此技术和映射技术。将此视为先决条件。

使用一种将文本字符串映射到函数指针的结构:

struct Text_Function_Pointer
{
  const char * name;
  Function_Pointer p_function;
};
static const Text_Function_Pointer table[] =
{
  {"Larry", Process_Larry},
  {"Felicity", Process_Felicity},
};
static const unsigned int table_size =
    sizeof(table) / sizeof(table[0]);  
//...
for (unsigned int i = 0; i < table_size; ++i)
{
  if (search_name == table[i].name)
  {
    // Execute the processing function.
    table[i].p_function(search_name);
    break;
  }
}

此技术的一个问题是,所有函数指针都必须具有相同的签名。地图也是如此。

一个很好的特性是表中的数据是恒定的,所以它可以放在只读存储器中。

此外,要添加更多关联,请在表中添加一个条目。搜索/处理功能没有改变,因此不需要再次测试。

映射/关联阵列

先决条件:函数指针
声明一个std::map<std::string, Function_Pointer_Type>。将您的姓名和功能添加到地图中:

std::map<std::string, Function_Pointer_Type> dispatch_table;
dispatch_table["Roger"] = Process_Roger;
dispatch_table["Felicity"] = Process_Felicity;
dispatch_table["Larry"] = Process_Larry;
//...
// Execute appropriate processing function:
(dispatch_table[search_name])();

该方法的一个问题是std::map数据结构需要初始化;它不能直接访问或从可执行代码加载。

同样,所有函数都必须具有相同的签名。

哈希表

这里的想法是拥有一个函数指针数组或一个具有文本&函数指针。创建一个散列函数,该函数基于名称字符串生成唯一的数组索引。使用索引从数组中获取函数指针,然后通过函数指针执行函数。

您可以使用多种解决方案。您基本上想要解析用户输入来填充Square类属性。一种方法是使用std::stoi函数族:

std::vector<string> values { "Roger", "2", "3.5" };
std::string name = values[0]; // No problem, two strings
int width = std::stoi(values[1]); // stoi = stringToInt
float happiness = std::stof(values[2]); // stof = stringToFloat

我不知道你为什么需要for循环,除非你的问题中有我不理解的地方。我会相应地更新我的答案。

更新1

在阅读了其他答案后,我想对你的问题提出我的解决方案。正如我在评论中多次指出的,这不是一个简单的答案
我需要这样一个类来编写一个通用测试引擎,这就是我使用的代码。它可以很好地与任何类型的函数配合使用(除了返回类型为void的例程——不过,一个简单的模板专用化可以解决这个问题(

# include <functional>
# include <tuple>
template<int ...>
struct seq
{
};
template<int N, int ...S>
struct gens : gens<N - 1, N - 1, S...>
{
};
template<int ...S>
struct gens<0, S...>
{
    typedef seq<S...> type;
};
struct callable_base
{
    virtual void operator()() = 0;
    virtual ~callable_base()
    { }
};
class Task
{
private:
    template<class RT, class Functor, class ...Args>
    struct functor : public callable_base
    {
        functor(RT& result, Functor func, Args ...args)
                : _ret(result)
        {
            _func = func;
            _args = std::make_tuple(args...);
        }
        void operator()()
        {
            _ret = call(typename gens<sizeof...(Args)>::type());
        }
        template<int ...S>
        RT call(seq<S...>)
        {
            return (_func(std::get<S>(_args)...));
        }
    private:
        std::function<RT(Args...)> _func;
        std::tuple<Args...>        _args;
        RT& _ret;
    };
public:
    Task()
    {
        _functor = nullptr;
    }
    template<class RT, class Functor, class ...Args>
    Task(RT& result, Functor func, Args... args)
    {
        _functor = new functor<RT, Functor, Args...>(result, func, args...);
    }
    void operator()()
    {
        (*_functor)();
    }
    ~Task()
    {
        delete _functor;
    }
private:
    callable_base *_functor;
};

此代码背后的想法是在内部类Task::functor中隐藏函数签名,并在传递给Task(…(构造函数的第一个参数中获取返回值。我首先给出这段代码是因为我认为它可能会帮助一些人,但也因为我认为这是解决您问题的一个优雅的解决方案。请记住,要理解大部分代码,您需要扎实的C++知识。如果需要,我将在后续更新中详细介绍代码。

以下是使用方法:

int main()
{
  int retVal;
  std::string newName;
  std::map<std::string, Task *> tasks {
    {"setName", new Task(retVal, &Square::setName, &newName)}
    ...
  }
  /* Modify the name however you want */
  ...
  tasks["setname"]();
}

当然,整个类可以进行优化,这主要归功于C++14和移动语义、通用引用等等,但我保持了的简单~一个主要问题是,如果在填充任务映射时不知道参数的值,则必须使用指针。我正在开发另一个版本来简化这一方面,但我想向您展示C++的设计并不是为了满足您的要求。也许你来自一个函数或JS世界,在这个世界里,这将是微不足道的x(

更新2

我只是想指出,使用C++14,您可以省略这里的前3个结构,这些结构可以帮助我使用interger_sequence 在参数列表中扩展元组