I am looking to make a class in C++ which can have a changeable function as one of its individual properties. Is there any standard way to achieve this? What would the syntax look like?
For example, something I have tried and want to do:
#include <iostream>
class Thing {
private:
int val;
void func();
public:
Thing(int val = 0) {
this->val = val;
}
void setFunc(void func()) { this->func() = func; }
void useFunc() { this->func(); }
};
int main() {
Thing thing1 = Thing(1);
thing1.setFunc({
std::cout << "i am thing 1, my value is: " << this->val;
});
thing1.useFunc();
return 0;
}
I'm not sure what the syntax would be like for something like this, or if pointers are needed. This (probably obviously) returns a couple of errors, those being:
./main.cpp:16:46: error: expression is not assignable
void setFunc(void func()) { this->func() = func; }
~~~~~~~~~~~~ ^
./main.cpp:28:51: error: invalid use of 'this' outside of a non-static member function
std::cout << "i am thing 1, my value is: " << this->val;
^
I realize I could use a lot of properties that aren't functions instead, then have one method which uses these properties to determine the outcome, however I am not looking to do this, as it would require a lot more properties and I want to try something different for the sake of learning how to do it. In this example there'd be no point in doing what I am trying to do, but this is just an example.
Step 1: Define a function pointer!
class Thing {
private:
int val;
// void func(); declares a function that will be defined later
// not what we want.
using func_ptr = void (*)(); // define a new type. It make everything
// that follows much easier
func_ptr func; // declares func to be a pointer to a function
public:
Thing(int val = 0): val(val) // use member initializer list
// all members must be initialized
// before entering the body of the
// constructor. not that important
// for an int, but very important for
// types with expensive initialization
// or no default initialization
{
}
void setFunc(func_ptr func) // pass a function pointer
{
this->func = func; // Do not call the function!
// avoid reusing names in the same scope.
// It only leads to trouble
}
void useFunc()
{
func(); // which func is not ambiguous here. No need for this
}
};
Step 2: Assigning the function!
thing1.setFunc({
std::cout << "i am thing 1, my value is: " << this->val;
});
Looks like you're trying to use a Lambda Expression. A couple of problems here.
Proper syntax looks like
thing1.setFunc([](){
std::cout << "i am thing 1, my value is: " << this->val;
});
thing1
nor is it a member of Thing
so it cannot use this
. This is a bit of a show-stopper. The Lambda need to Capture thing1
, but as soon as you capture, you can't use function pointers anymore. They are too simple to deal with captures. Enter std::function
.Step 3: Start over with std::function
#include <iostream>
#include <functional>
class Thing {
private:
int val;
using func_ptr = std::function<void ()>;
func_ptr func;
public:
Thing(int val = 0): val(val)
{
}
void setFunc(func_ptr func)
{
this->func = func;
}
void useFunc()
{
func();
}
int get_val()
{
return val;
}
};
int main() {
Thing thing1 = Thing(1);
thing1.setFunc([thing1](){ // captures by value. Not always what you
// want, but generally the safest option.
// Just fine for a simple example like this
std::cout << "i am thing 1, my value is: " << thing1.val;
});
thing1.useFunc();
return 0;
}
And this still can't work because Thing::val
is a private member and cannot be accessed from outside Thing
or a friend of Thing
. The Lambda is neither and cannot easily be shaped into one. Either A) the Lambda must call a Thing
member function to do the real work, pointless because you'd discard the all of this rigamarole and simply directly call the member function, B) you add a getter for Thing::val
and the Lambda uses the getter, or C) you make Thing::val
public
.
B looks like the best option. But what if the logic you need has to change thing1
? That needs a...
Step 4: Find a simpler answer to the overarching problem.