如何使用std :: string从对象调用函数

时间:2016-09-10 16:29:34

标签: c++ c++11

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

我找到了this,但它不适用于我的情况我认为这是因为我的函数没有在我的main方法中定义,而是在我的方形类中。此外,我的函数并非都以与void(std :: string)相同的方式定义std :: string()void(int)......

这是一个想要做的事情的例子。

我的对象广场

#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。

2 个答案:

答案 0 :(得分:1)

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

  • If-else ladder
  • 查找表
  • 地图/关联数组
  • 哈希表

可能还有其他方法,但我想到了上述方法。

if-else ladder(a.k.a。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数据结构;它不能直接从可执行代码访问或加载。

同样,所有功能必须具有相同的签名。

哈希表

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

答案 1 :(得分:0)

您可以使用多种解决方案。您基本上想要解析用户输入以填充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,您可以省略前三个结构,以帮助我使用interger_sequence

在参数列表中扩展我的元组