2

This might sound a little convoluted, but here we go.

So, I have the following code:

  void Utility::validateRangeAndModify(Pet pet, int checkint, 
                                       int numbertovalidate, 
                                       bool greaterorless)
{
    if (greaterorless) {
        if (numbertovalidate < checkint)
            pet.getAttributes()->setPetHunger(0);
    } else
        if (numbertovalidate > checkint)
            pet.getAttributes()->func(100);
}

Firstly, This code is designed to validate a single integer. That part is easy.

Then, what I want it to do is carry out a function depending on if the integer meets the condition or not. In this case, setPetHunger() is being set to either 0 or 100. The issue is, I have setPetHealth(), and setPetEnergy() too.

The function I want it to perform is the thing that I want to change.

For instance. This code will only work for my pets Hunger. It won't work for it's Health, Happiness, or any of it's other variables. and I have a ton of other variables.

I'm wondering if there is any way to achieve something like this:

 void Utility::validateRangeAndModify(Pet pet, 
                                     int checkint, 
                                     int numbertovalidate, 
                                     bool greaterorless, 
                                     string functiontouse)
{

    if (greaterorless) {
        if (numbertovalidate < checkint)
            pet.getAttributes()->setPetHunger(0);
    } else
        if (numbertovalidate > checkint)
            pet.getAttributes()->FUNCTION_TO_USE(100);
}

I could use something like reflection for this in C#. However, I don't know an alternative function in c++

lost_in_the_source
  • 10,998
  • 9
  • 46
  • 75

3 Answers3

0

You should use a function pointer:

  void Utility::validateRangeAndModify(Pet pet, int checkint, 
                                       int numbertovalidate, 
                                       bool greaterorless,
                                       void (*func)(int, attribs &))
{
    if (greaterorless) {
        if (numbertovalidate < checkint)
            pet.getAttributes()->setPetHunger(0);
    } else
        if (numbertovalidate > checkint)
            func(100, pet.getAttribs());
}

You and the client will agree on the prototype of the function that you will pass; in this case, the prototype will be void func(int, attribs &);

The client can write code like this:

void happiness(int level, attribs &a)
{
    // some code here
}
Utility::validateRangeAndModify(..., happiness);

Now happiness willl be called by your validateRangeAndModify function.

NOTE: happiness is a free function. If it is a member function, mark it as static: then there won't be the extra this argument.

lost_in_the_source
  • 10,998
  • 9
  • 46
  • 75
  • This won't work. A pointer to member function is not the same as a pointer to a function – Kevin Nov 13 '15 at 22:31
  • why would this not work? `happiness` is not part of any class, so it does not have the extra `this` argument. @Kevin – lost_in_the_source Nov 13 '15 at 22:33
  • You aren't calling the `func` parameter but you're trying to call a `func` member function on `pet.getAttributes()` – Kevin Nov 13 '15 at 22:34
  • A pointer to member function is used like this: `void (Object::*func)(int) = ...; (obj.*func)(123);`. You can also use `std::bind` to turn a member function into something that acts as a regular function to avoid the strange syntax. – Kevin Nov 13 '15 at 22:38
  • Getting " Class 'Attributes' has no member 'func' " – Mike Fredrik Nov 13 '15 at 22:41
  • 1
    See my comments. You could also have that `happiness` function take the attributes as a parameter and avoid the whole member function business entirely. – Kevin Nov 13 '15 at 22:42
  • I edited my answer to show how you would pass a reference to a structure that would contain the attributes @MikeFredrik – lost_in_the_source Nov 13 '15 at 22:47
  • `pet.getAttributes()->func(100)` tries to call a member function called `func` on the attributes. It has nothing to do with the parameter passed in. If you want to call a variable member function on an object use a pointer to member function (google it) or a regular function that takes the object as a parameter and calls the member function inside it (see stackptr's edit) – Kevin Nov 13 '15 at 22:47
  • Tries to call func on the attributes, yeah. Got that part. Nothing to do with the parameter passed in? what parameter? Then I lost you. I only learnt pointers last week, please go a little slower when explaining – Mike Fredrik Nov 13 '15 at 22:51
  • 'initial value of reference to non-const must be an lvalue' – Mike Fredrik Nov 13 '15 at 22:57
0

To keep this simple, I'm providing a really trivial program that uses std::bind, std::function, and std::placeholders to allow calls to a bound method. This keeps the correct this with the method and generally prevents nastiness.

#include <iostream>
#include <functional>

// bait class to catch bound function calls. Replace this with pet for OP's example
class TwoMethods
{
public:
    void method1(int val)
    {
        std::cout << "Method 1 received " << val << std::endl;
    }

    void method2(int val)
    {
        std::cout << "Method 2 received " << val << std::endl;
    }

};

// utility function that will operate on a bound function
void utilityfunction(std::function<void(int)> func)
{
    // calls passed function. Quietly handles bound this parameter and replaces 
    // placeholder with 42
    func(42);
}
int main()
{
    TwoMethods obj;

    // call utility function with appropriate object and method
    // the std::placeholders::_1 lets the compiler know there is another parameter that 
    // will be supplied later
    utilityfunction(std::bind(&TwoMethods::method1, obj, std::placeholders::_1));
    utilityfunction(std::bind(&TwoMethods::method2, obj, std::placeholders::_1));
}

In OP's case:

void Utility::validateRangeAndModify(int checkint, 
                                     int numbertovalidate, 
                                     bool greaterorless,
                                     std::function<void(int)> func)
{
    switch (greaterorless)
    {
        case 0: 
            {
                if (numbertovalidate < checkint) 
                { 
                    func(0); 
                }  
            } 
            break;
        case 1: 
            {
                if (numbertovalidate > checkint) 
                {
                    func(100); 
                } 
                break; 
            } 
    }
}

And called something like:

validateRangeAndModify(42, 666, false,
                       std::bind(&Attributes::setPetHunger,  
                                 pet.getAttributes(), 
                                 std::placeholders::_1))
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • 'Attributes has no member func' – Mike Fredrik Nov 13 '15 at 23:20
  • I wouldn't expect it to. `func` already knows everything it needs to. The attributes were programmed in with the call to std::bind. There is no need for `pet.getAttributes()->func(0);`. You just call `func(0);` – user4581301 Nov 13 '15 at 23:42
0

You can do it by passing the correct member function pointer as parameter to Utility::validateRangeAndModify as below:

void
Utility::validateRangeAndModify(Pet &pet, int checkint, int numbertovalidate, 
                                bool greaterorless, void(Attrib::*memfun)(int)) {
                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  if(greaterorless) {
    if(numbertovalidate < checkint) (pet.getAttributes()->*memfun)(0);
  } else {
    if(numbertovalidate < checkint) (pet.getAttributes()->*memfun)(100);
  }
}

And then call it as (if Utility::validateRangeAndModify is not static use obj.validateRangeAndModify):

Utility::validateRangeAndModify(p, 10, 9, false, &Attrib::setPetHunger);
Utility::validateRangeAndModify(p, 10, 9, false, &Attrib::setPetThirst);

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168