【问题标题】:How to call a function from an object with a std::string如何使用 std::string 从对象调用函数
【发布时间】:2016-09-10 16:29:33
【问题描述】:

这是我的问题,我想调用我的一个对象的 getter/setter,但不是直接调用,我想通过使用 std::string 来实现。

我找到了this,但它不适用于我的情况,我认为这是因为我的函数不是在我的 main 方法中定义的,而是在我的 square 类中定义的。此外,我的函数的定义方式也不尽相同 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::string 调用我的 square.setName() 而不在我的 main 中写入这个 square.setName。

【问题讨论】:

  • 你的字符串文字 "Roger' 使用单引号和双引号,我很确定这不会编译。
  • @Jezor 哦,我的错,我只是按错了键。已编辑
  • 您阅读了链接,发现它并不那么简单。如链接所述,除非您愿意为您的 setter 和 getter 编写伪反射设计,否则目前在 C++ 语言中是不可能的。
  • 另外,this could be helpful

标签: c++ c++11


【解决方案1】:

要基于字符串调用函数,您有一些选择。在我列出选择之前,请在互联网上搜索“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&lt;std::string, Function_Pointer_Type&gt;。将您的姓名和职能添加到地图中:

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数据结构需要初始化;它不能直接访问或从可执行代码加载。

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

哈希表

这里的想法是有一个函数指针数组或一个带有文本和函数指针的结构数组。创建一个 hash 函数,该函数根据名称字符串生成唯一的数组索引。使用索引从数组中获取函数指针,然后通过函数指针执行函数。

【讨论】:

  • 哈希表方式是否适用于具有不同签名的函数?而且我目前正在尝试 Map 方式,但它不适用于其他对象的方法是正常的还是我的函数指针出错了。 (在我尝试了您的一些解决方案后,我可能会在明天之前将您的答案标记为已接受)
  • 结构和数组需要单一的、不变的类型。因此,数组或容器中的函数指针需要相同的签名。您可以通过在结构中传递参数来简化函数的签名。因此签名是相同的,但(参数的)结构可能会有所不同。
【解决方案2】:

您可以使用多种解决方案。您基本上想要解析用户输入以填充您的 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

在阅读其他答案后,我想提出我对您问题的解决方案。正如我在 cmets 中多次指出的那样,这不是一个简单的答案!
我需要这样一个类来编写一个通用的测试引擎,这就是我使用的代码。它适用于任何类型的函数(返回类型为 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 在参数列表中扩展我的元组

【讨论】:

  • 这个答案会起作用,但它并没有按照我想要的方式进行。循环的目的以及我想通过函数名称的字符串调用函数我将编辑我的问题,因为可能不清楚
  • 我不明白“通过名称字符串调用函数”:/
  • 你的向量包含什么?新值的字符串表示形式或其他类似 'name:Roger;width:1;happiness:5.3'?
  • 假设我想将“Roger”更改为“Henry”。我不想做:square.setName("Henry") 我想做:someFunction("setName","Henry")
  • 不知道你为什么想要那个,但是好的,当然,为什么不 x) 让我更新我的答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
相关资源
最近更新 更多