Krowk Krowk - 2 months ago 6
C++ Question

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

Here's my issue, I would like to call the getters/setters of one of my objects, but not directly, I want to do it by using a std::string.

I found this but it won't work on my case I think it is because my function aren't defined in my main method but in my square class. Also my function are not all defined the same way there's void(std::string) std::string() void(int)...

here's an exemple of what a would like to do.

my object square

#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()
}


and my main

#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")


}
}

I hope i have been clear it's pretty hard to explain something you don't know how to do. If you want me to be more specific tell me and I'll do what I can.

EDIT: What I want to do is to call for example my square.setName() with a str::string without writting this square.setName in my main.

Answer

Several solutions are available to you. You basically want to parse user input to fill your Square class attribute. One way is to use the std::stoi family of functions:

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

I'm not sure why you'd need the for loop, unless there is something I didn't understand in your question. I'll update my answer accordingly.

Update 1

After reading other answers, I would like to propose my solution to your problem. As stated several times in my comments, this is not an easy answer !
I needed such a class to write a generic test engine, and this is the code I used. It works really well with any type of function (except for routines with a return type of void -- a simple template specialization would solve it though)

# 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;
};

The idea behind this code is to hide the function signature in the inner class Task::functor and get the return value in the first parameter passed to the Task(...) constructor. I'm giving this code first because I think it might help some people, but also because I think it is an elegant solution to your problem. Bear in mind that to understand most of the code, you need solid C++ knowledge. I'll detail the code in subsequent updates if needed.

Here's how you'd use it:

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"]();
}

This whole class could be optimized, of course, primarily thanks to C++14 and move semantics, universal references and all, but I kept it simple ~ A major problem is that you have to use pointers if you don't know the values of the parameters at the time you fill the task map. I'm working on another version to simplify this aspect, but I wanted to show you that C++ is not designed to do what you ask simply. Maybe you come from a functional or JS world, in which this would be trivial x)

Update 2

I just wanted to point out that with C++14, you could omit the first 3 structures that are here to help me expand my tuple in an argument list using interger_sequence