1

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.

Community
  • 1
  • 1
Krowk
  • 46
  • 8
  • 2
    Your string literal `"Roger'` uses both single and double quotation marks, I'm pretty sure this will not compile. – Jezor Sep 10 '16 at 16:31
  • 1
    @Jezor Ohh my mistake, I just pressed the wrong key. EDITED – Krowk Sep 10 '16 at 16:35
  • 1
    You read the link, and see that it isn't that simple. As the link stated, unless you're willing to write a pseudo-reflection design to your setters and getters, it isn't possible right now in the C++ language. – PaulMcKenzie Sep 10 '16 at 17:17
  • Also, [this could be helpful](http://stackoverflow.com/questions/41453/how-can-i-add-reflection-to-a-c-application). – PaulMcKenzie Sep 10 '16 at 17:26

2 Answers2

1

To call functions, based on a string, you have some choices. Before I list the choices, please search the internet for "C++ factory design pattern".

  • If-else ladder
  • Lookup table
  • Map / Associative array
  • Hash table

There may be other methods, but the above come to mind.

if-else ladder (a.k.a. switch)

The problem with this method is that the switch statement doesn't work with strings nor text literals. So you'll have to suffice with if statements:

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

Anytime you need to add a new string, you will have to add another "else if" statement to the ladder. Not only do you have to change the code, but you also have to retest it.

Lookup Table

You will need to understand function pointers for this technique and the map technique. Consider this a prerequisite.

Use a structure for mapping text strings to function pointers:

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

An issue with this technique is that all the function pointers must have the same signature. This is true for the map as well.

A nice feature is that the data in the table is constant, so it can be placed in Read-Only Memory.

Also, to add more associations, add an entry to the the table. The search / processing function hasn't changed, so it doesn't need to be tested again.

Map / Associative Array

Prerequisite: Function pointers.
Declare a std::map<std::string, Function_Pointer_Type>. Add your names and functions to the map:

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

One issue with this method is that the std::map data structure needs to be initialized; it can't be directly accessed or loaded from executable code.

Again, all functions must have the same signature.

Hash Table

The idea here is to have an array of function pointers or an array of structures with text & function pointers. Create a hash function that generates a unique array index based on the name string. Use the index to get the function pointer from the array, then execute the function via the function pointer.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • Do the Hash Table way works with function with different signature? And I'm currently trying the Map way but it doesn't work with a method from an other object is it normal or is it me who did a mistake with my Function pointer. (I'll maybe mark your answer as accepted by tomorrow after I've tried some of your solutions) – Krowk Sep 10 '16 at 18:18
  • Structures and arrays require single, non-changing, types. So function pointers in an array or container require the same signature. You can simplify a function's signature by passing arguments in a structure. Thus the signature is the same, but the structure (of arguments) can vary. – Thomas Matthews Sep 10 '16 at 18:24
0

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

Naliwe
  • 322
  • 1
  • 8
  • This answer would work but it doesn't do it the way I wanted. The purpose of the loop and what I want to do to call the functions via a string of their names I'll edit my question because maybe it wasn't clear – Krowk Sep 10 '16 at 17:03
  • I don't understand 'call the functions via a string of their names' :/ – Naliwe Sep 10 '16 at 17:05
  • What does your vector contain? String representation of the new values or something else like 'name:Roger;width:1;happiness:5.3'? – Naliwe Sep 10 '16 at 17:07
  • Let's say I want to change "Roger" to "Henry". I don't want to do: square.setName("Henry") I want to do: someFunction("setName","Henry") – Krowk Sep 10 '16 at 17:09
  • Not sure why you'd want that but ok, sure, why not x) lemme update my answer – Naliwe Sep 10 '16 at 17:10
  • Look at the link in my question to see an exemple of what I'm trying to do but which doesn't work in my case – Krowk Sep 10 '16 at 17:16
  • Hmm ok, why doesn't this work for you? In the mean time, let me get back to my computer before I answer, the phone keyboard is NOT made to write code, sadly... Unless someone else can give an example using std::map and std::function. Although I have an elegant solution, it is NOT simple. Handling functions in C++ involves some meta-programming when you cwant to do generic things. – Naliwe Sep 10 '16 at 17:18
  • To call functions using a User input string, use a `std::map`, or a table of structures that includes function pointers. Search the internet for "stackoverflow c++ string function pointers map". I've already answered a lot of them under this topic. – Thomas Matthews Sep 10 '16 at 17:31
  • Yes that's basically what I was going for. But it is simple only when all your function pointers have the same signature. Otherwise, it gets tricky – Naliwe Sep 10 '16 at 17:32
  • I just read Thomas Matthews answer, and I first want to say kudos for the time you took and the great answer x) Nevertheless, as I was saying in the commentaries, working with heteroclite functions in C++ can become tricky. I myself have wrote a container that can handle any type of function pointers, but it is not an easy answer, not even close ; please let me know if you'd like to see it, but please be aware it involves advanced C++ and template meta programming features which require a lot of documentation reading. – Naliwe Sep 10 '16 at 18:52
  • Thank you for this answer, it seems to be exactly the kind of answer I seek. I understand C++ isn't the easiest language to do that, but my goal here is to do an app in C++ to grasp some of the more advanced skill in this language. – Krowk Sep 10 '16 at 22:06
  • If this is your will, then I'd recommend starting with easier meta programming patterns, like a type list or even simpler, implement the Fibonacci function. I found [this website](https://monoinfinito.wordpress.com/series/introduction-to-c-template-metaprogramming/) to be quite helpful when I started template programming in C++. This is a vast subject, but I find it really interesting x) – Naliwe Sep 10 '16 at 22:08